From 5abef702e4f91240caea2be71b886fec9a8473e6 Mon Sep 17 00:00:00 2001 From: f4exb Date: Mon, 4 Apr 2022 10:23:52 +0200 Subject: [PATCH 001/115] Massive UI revamping (v7): features --- app/main.cpp | 10 +- doc/img/corner.xcf | Bin 0 -> 8603 bytes doc/img/cross.xcf | Bin 0 -> 12000 bytes doc/img/dock.xcf | Bin 0 -> 9342 bytes doc/img/exit.xcf | Bin 0 -> 362360 bytes doc/img/gear.xcf | Bin 0 -> 19233 bytes doc/img/help.xcf | Bin 0 -> 30701 bytes doc/img/mimo.xcf | Bin 0 -> 22527 bytes doc/img/rxtx.xcf | Bin 0 -> 31248 bytes doc/img/shrink.xcf | Bin 0 -> 28472 bytes doc/img/tiles.xcf | Bin 0 -> 8029 bytes doc/img/tool.xcf | Bin 0 -> 14271 bytes plugins/feature/afc/afcgui.cpp | 29 +- plugins/feature/afc/afcgui.h | 1 + plugins/feature/afc/afcgui.ui | 14 +- plugins/feature/ais/aisgui.cpp | 16 +- plugins/feature/ais/aisgui.ui | 6 +- .../feature/antennatools/antennatoolsgui.cpp | 31 +- .../feature/antennatools/antennatoolsgui.h | 1 + .../feature/antennatools/antennatoolsgui.ui | 6 +- plugins/feature/aprs/aprsgui.cpp | 31 +- plugins/feature/aprs/aprsgui.h | 1 + plugins/feature/aprs/aprsgui.ui | 14 +- .../demodanalyzer/demodanalyzergui.cpp | 23 +- .../feature/demodanalyzer/demodanalyzergui.h | 1 + .../feature/demodanalyzer/demodanalyzergui.ui | 16 +- .../gs232controller/gs232controllergui.cpp | 36 +- .../gs232controller/gs232controllergui.h | 1 + .../gs232controller/gs232controllergui.ui | 14 +- .../jogdialcontrollergui.cpp | 21 +- .../jogdialcontroller/jogdialcontrollergui.h | 1 + .../jogdialcontroller/jogdialcontrollergui.ui | 14 +- plugins/feature/map/mapgui.cpp | 30 +- plugins/feature/map/mapgui.h | 1 + plugins/feature/map/mapgui.ui | 12 +- plugins/feature/pertester/pertestergui.cpp | 35 +- plugins/feature/pertester/pertestergui.h | 1 + plugins/feature/pertester/pertestergui.ui | 14 +- plugins/feature/radiosonde/radiosondegui.cpp | 25 +- plugins/feature/radiosonde/radiosondegui.h | 1 + plugins/feature/radiosonde/radiosondegui.ui | 6 +- .../feature/rigctlserver/rigctlservergui.cpp | 25 +- .../feature/rigctlserver/rigctlservergui.h | 1 + .../feature/rigctlserver/rigctlservergui.ui | 14 +- .../satellitetracker/satellitetrackergui.cpp | 37 +- .../satellitetracker/satellitetrackergui.h | 1 + .../satellitetracker/satellitetrackergui.ui | 14 +- plugins/feature/simpleptt/simplepttgui.cpp | 29 +- plugins/feature/simpleptt/simplepttgui.h | 1 + plugins/feature/simpleptt/simplepttgui.ui | 14 +- .../feature/startracker/startrackergui.cpp | 49 +- plugins/feature/startracker/startrackergui.h | 1 + plugins/feature/startracker/startrackergui.ui | 14 +- .../feature/vorlocalizer/vorlocalizergui.cpp | 24 +- .../feature/vorlocalizer/vorlocalizergui.h | 1 + .../feature/vorlocalizer/vorlocalizergui.ui | 15 +- sdrbase/pipes/messagepipeslegacy.cpp | 78 + sdrbase/pipes/messagepipeslegacy.h | 59 + sdrbase/pipes/messagepipeslegacycommon.cpp | 20 + sdrbase/pipes/messagepipeslegacycommon.h | 61 + sdrbase/pipes/messagepipeslegacygcworker.cpp | 78 + sdrbase/pipes/messagepipeslegacygcworker.h | 69 + sdrbase/pipes/pipeendpoint.cpp | 185 ++ sdrbase/pipes/pipeendpoint.h | 100 + sdrgui/CMakeLists.txt | 7 + sdrgui/feature/featuregui.cpp | 204 ++ sdrgui/feature/featuregui.h | 70 +- sdrgui/feature/featureuiset.cpp | 18 +- sdrgui/feature/featureuiset.h | 11 +- sdrgui/gui/basicfeaturesettingsdialog.cpp | 31 - sdrgui/gui/basicfeaturesettingsdialog.h | 6 - sdrgui/gui/basicfeaturesettingsdialog.ui | 57 +- sdrgui/gui/featurepresetsdialog.cpp | 2 +- sdrgui/gui/featurepresetsdialog.h | 3 + sdrgui/gui/rollupcontents.cpp | 601 ++++++ sdrgui/gui/rollupcontents.h | 68 + sdrgui/gui/workspace.cpp | 271 +++ sdrgui/gui/workspace.h | 84 + sdrgui/gui/workspaceselectiondialog.cpp | 47 + sdrgui/gui/workspaceselectiondialog.h | 50 + sdrgui/gui/workspaceselectiondialog.ui | 67 + sdrgui/mainwindow.cpp | 1758 +++++++++-------- sdrgui/mainwindow.h | 38 +- sdrgui/mainwindow.ui | 13 + sdrgui/resources/cascade.png | Bin 0 -> 6300 bytes sdrgui/resources/corner_botleft.png | Bin 0 -> 5120 bytes sdrgui/resources/corner_botright.png | Bin 0 -> 5104 bytes sdrgui/resources/corner_topleft.png | Bin 0 -> 5103 bytes sdrgui/resources/corner_topright.png | Bin 0 -> 5112 bytes sdrgui/resources/cross.png | Bin 0 -> 5731 bytes sdrgui/resources/dock.png | Bin 0 -> 6256 bytes sdrgui/resources/exit.png | Bin 0 -> 7781 bytes sdrgui/resources/gear.png | Bin 0 -> 6916 bytes sdrgui/resources/help.png | Bin 0 -> 5466 bytes sdrgui/resources/mimo.png | Bin 0 -> 4992 bytes sdrgui/resources/res.qrc | 16 + sdrgui/resources/rx.png | Bin 0 -> 5009 bytes sdrgui/resources/shrink.png | Bin 0 -> 9350 bytes sdrgui/resources/tiles.png | Bin 0 -> 5869 bytes sdrgui/resources/tool.png | Bin 0 -> 5466 bytes sdrgui/resources/tx.png | Bin 0 -> 5019 bytes 101 files changed, 3610 insertions(+), 1114 deletions(-) create mode 100644 doc/img/corner.xcf create mode 100644 doc/img/cross.xcf create mode 100644 doc/img/dock.xcf create mode 100644 doc/img/exit.xcf create mode 100644 doc/img/gear.xcf create mode 100644 doc/img/help.xcf create mode 100644 doc/img/mimo.xcf create mode 100644 doc/img/rxtx.xcf create mode 100644 doc/img/shrink.xcf create mode 100644 doc/img/tiles.xcf create mode 100644 doc/img/tool.xcf create mode 100644 sdrbase/pipes/messagepipeslegacy.cpp create mode 100644 sdrbase/pipes/messagepipeslegacy.h create mode 100644 sdrbase/pipes/messagepipeslegacycommon.cpp create mode 100644 sdrbase/pipes/messagepipeslegacycommon.h create mode 100644 sdrbase/pipes/messagepipeslegacygcworker.cpp create mode 100644 sdrbase/pipes/messagepipeslegacygcworker.h create mode 100644 sdrbase/pipes/pipeendpoint.cpp create mode 100644 sdrbase/pipes/pipeendpoint.h create mode 100644 sdrgui/gui/rollupcontents.cpp create mode 100644 sdrgui/gui/rollupcontents.h create mode 100644 sdrgui/gui/workspace.cpp create mode 100644 sdrgui/gui/workspace.h create mode 100644 sdrgui/gui/workspaceselectiondialog.cpp create mode 100644 sdrgui/gui/workspaceselectiondialog.h create mode 100644 sdrgui/gui/workspaceselectiondialog.ui create mode 100644 sdrgui/resources/cascade.png create mode 100644 sdrgui/resources/corner_botleft.png create mode 100644 sdrgui/resources/corner_botright.png create mode 100644 sdrgui/resources/corner_topleft.png create mode 100644 sdrgui/resources/corner_topright.png create mode 100644 sdrgui/resources/cross.png create mode 100644 sdrgui/resources/dock.png create mode 100644 sdrgui/resources/exit.png create mode 100644 sdrgui/resources/gear.png create mode 100644 sdrgui/resources/help.png create mode 100644 sdrgui/resources/mimo.png create mode 100644 sdrgui/resources/rx.png create mode 100644 sdrgui/resources/shrink.png create mode 100644 sdrgui/resources/tiles.png create mode 100644 sdrgui/resources/tool.png create mode 100644 sdrgui/resources/tx.png diff --git a/app/main.cpp b/app/main.cpp index 943c2a9b7..045b6e88a 100644 --- a/app/main.cpp +++ b/app/main.cpp @@ -49,20 +49,20 @@ static int runQtApplication(int argc, char* argv[], qtwebapp::LoggerWithFile *lo qApp->setStyle(QStyleFactory::create("fusion")); QPalette palette; - palette.setColor(QPalette::Window, QColor(53,53,53)); + palette.setColor(QPalette::Window, QColor(64,64,64)); palette.setColor(QPalette::WindowText, Qt::white); palette.setColor(QPalette::Base, QColor(25,25,25)); palette.setColor(QPalette::AlternateBase, QColor(53,53,53)); palette.setColor(QPalette::ToolTipBase, Qt::white); palette.setColor(QPalette::ToolTipText, Qt::black); palette.setColor(QPalette::Text, Qt::white); - palette.setColor(QPalette::Button, QColor(0x40, 0x40, 0x40)); + palette.setColor(QPalette::Button, QColor(64,64,64)); palette.setColor(QPalette::ButtonText, Qt::white); palette.setColor(QPalette::BrightText, Qt::red); - palette.setColor(QPalette::Light, QColor(53,53,53).lighter(125).lighter()); - palette.setColor(QPalette::Mid, QColor(53,53,53).lighter(125)); - palette.setColor(QPalette::Dark, QColor(53,53,53).lighter(125).darker()); + palette.setColor(QPalette::Light, QColor(64,64,64).lighter(125).lighter()); + palette.setColor(QPalette::Mid, QColor(64,64,64).lighter(125)); + palette.setColor(QPalette::Dark, QColor(64,64,64).lighter(125).darker()); palette.setColor(QPalette::Link, QColor(0,0xa0,0xa0)); palette.setColor(QPalette::LinkVisited, QColor(0,0xa0,0xa0).lighter()); diff --git a/doc/img/corner.xcf b/doc/img/corner.xcf new file mode 100644 index 0000000000000000000000000000000000000000..1fb705730803d4480035c59ae4ed39834e4c99a2 GIT binary patch literal 8603 zcmeI1Urbw77{CvLz$mPLXek4P+ZNXHr==BIr7 zwAbjWn%Nj_HfzQa788NVKq7$kFqVSb-Tq{FS~w%d;!-rydb{k>(~n;m=01fe?-2nmruSnRPso|J-~L7XB_zm$j%iLv28cxGC(_Z|`8)7=hD z|GkUS@fk4^N=(^%JADt|P4iRI!)4A<2#tPhya9Y-({p7A84V9>MUV7uG5y@n3g zq{8Ex0;yY(gNs}@5sgK}m}e#u!pnFKQ!!+W84lToy8j$Vh%3lgk6jOt4q$eTUZ>H^ zG`gVCbsD`~qgQBjJ?Qv-o2dj8{BFE|6X^ifh&qP^bZB&K+(Y$dDjMF@G(RGEUr0r- zsI<)z=ZUYWM7cG6pclWX)|Y%sTvb_0^H^FO^V~})n z#8*{Tz;76)gO`c#tJITS`UNU~LHv&Ri%P?yN)y>-I-!;;SBSr=#Hqw&HV_4(SEc1M z;@2vzBg83Ul6Zmm7V%w`haMr49W~#m<(d^D*-@(&SuNR7E2tH9*NJ3D{c*KiPj=Lk z9ra{KJ=sxDb~KRx8pwY(r`q4vMSPNYiWpMKSjxcoRxZ57(!~!QwGCKV+I#+HBeyWw z^`4VgvUI`O4Hq{tx%8bDZe()7)m8vM+r|yhWF5gjaC1H5UJuH7MJ{{zK_(Z=2;R_+ zvP+SD`69Qb4yUnjiTMXo<(dh-FSWuzy1&&_%N%^;_iJP2R(6=DS7%Es%*EwphnY2V zdG%N&YvJ-*r-`+4`FpF8wQ+t0r7U+Cn49wjl=7NR&pezDDRN$s7Zv%5B0tX;sdXbP z(!5!oxi}WBFYdJtYdde&a4wET>)%{ltNCQJ))#l^C3T!%vIlE9f5Fc3NoI_+gmHf` zRl1b9E827IJ&Mdvm8b6|MZToSQ9VAi|281_qVgQ?H{hfFLnA)te|8x0slPF3!acyB zqm{TL__5238-%}JH&}4Tkp3!JXJtZgWBh5aDOGwaV=D4#x#!|oG8U_L;|_X01!XRd zCCfJ%aRb-(RL;et*>RdrYd9CjlI7d|xceV%y|a9? zA9s25+9=Dn`*F7)ce(XS)UTW`v?|hsQZD#SIgz-g$T5_1`E56c4cv<;FAHoz-wTZ9?!b9VllV><}Cn)FJqVMpn(`9}JDm3NISEnphQ=*GCUC z3%rCFa56KOKXSO35neki_O-Bz8|#agUmn$`irkqKy2rmw;2cPJ)Qee3=Rjh{)JK{9 E1H++q^Z)<= literal 0 HcmV?d00001 diff --git a/doc/img/cross.xcf b/doc/img/cross.xcf new file mode 100644 index 0000000000000000000000000000000000000000..7c64325c58d79c8d4acb9f9906c7899ae7784172 GIT binary patch literal 12000 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 C0wR_G literal 0 HcmV?d00001 diff --git a/doc/img/exit.xcf b/doc/img/exit.xcf new file mode 100644 index 0000000000000000000000000000000000000000..711f58188cf8ab15773ee32301bb7e4fe0530cec GIT binary patch literal 362360 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%0AN4`1waG^09PGdHiyKB*>p>+)V%d>{r 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 ATL1t6 literal 0 HcmV?d00001 diff --git a/doc/img/gear.xcf b/doc/img/gear.xcf new file mode 100644 index 0000000000000000000000000000000000000000..f0b589179be7d6f186f031506e375aa0ac9a9eb9 GIT binary patch literal 19233 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 OXCZnv{)`AU^Zx*}M8_-u literal 0 HcmV?d00001 diff --git a/doc/img/help.xcf b/doc/img/help.xcf new file mode 100644 index 0000000000000000000000000000000000000000..ddb3d041194634070dbac2b98734855e38b64767 GIT binary patch literal 30701 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 L|0%pz{OA5B7TAkg literal 0 HcmV?d00001 diff --git a/doc/img/mimo.xcf b/doc/img/mimo.xcf new file mode 100644 index 0000000000000000000000000000000000000000..d3c4e98c9e5e43afc6eb5a6329b917d0b0b09092 GIT binary patch literal 22527 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 literal 0 HcmV?d00001 diff --git a/doc/img/rxtx.xcf b/doc/img/rxtx.xcf new file mode 100644 index 0000000000000000000000000000000000000000..d1cb79a4df81059bc735e2c62a282ff275530ed9 GIT binary patch literal 31248 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 applySettings(); } @@ -130,7 +130,8 @@ AFCGUI::AFCGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISet, Feature *featur m_doApplySettings(true), m_lastFeatureState(0) { - ui->setupUi(this); + ui->setupUi(getRollupContents()); + getRollupContents()->arrangeRollups(); m_helpURL = "plugins/feature/afc/readme.md"; setAttribute(Qt::WA_DeleteOnClose, true); @@ -140,8 +141,7 @@ AFCGUI::AFCGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISet, Feature *featur ui->toleranceFrequency->setColorMapper(ColorMapper(ColorMapper::GrayYellow)); ui->toleranceFrequency->setValueRange(5, 0, 99999L); - setChannelWidget(false); - connect(this, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + connect(getRollupContents(), SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); m_afc = reinterpret_cast(feature); m_afc->setMessageQueueToGUI(&m_inputMessageQueue); @@ -162,6 +162,7 @@ AFCGUI::AFCGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISet, Feature *featur requestDeviceSetLists(); displaySettings(); applySettings(true); + makeUIConnections(); } AFCGUI::~AFCGUI() @@ -178,6 +179,7 @@ void AFCGUI::displaySettings() { setTitleColor(m_settings.m_rgbColor); setWindowTitle(m_settings.m_title); + setTitle(m_settings.m_title); blockApplySettings(true); ui->hasTargetFrequency->setChecked(m_settings.m_hasTargetFrequency); ui->transverterTarget->setChecked(m_settings.m_transverterTarget); @@ -185,7 +187,7 @@ void AFCGUI::displaySettings() ui->toleranceFrequency->setValue(m_settings.m_freqTolerance); ui->targetPeriod->setValue(m_settings.m_trackerAdjustPeriod); ui->targetPeriodText->setText(tr("%1").arg(m_settings.m_trackerAdjustPeriod)); - restoreState(m_rollupState); + getRollupContents()->restoreState(m_rollupState); blockApplySettings(false); } @@ -269,7 +271,6 @@ void AFCGUI::onMenuDialogCalled(const QPoint &p) { BasicFeatureSettingsDialog dialog(this); dialog.setTitle(m_settings.m_title); - dialog.setColor(m_settings.m_rgbColor); dialog.setUseReverseAPI(m_settings.m_useReverseAPI); dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); @@ -279,7 +280,6 @@ void AFCGUI::onMenuDialogCalled(const QPoint &p) dialog.move(p); dialog.exec(); - m_settings.m_rgbColor = dialog.getColor().rgb(); m_settings.m_title = dialog.getTitle(); m_settings.m_useReverseAPI = dialog.useReverseAPI(); m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); @@ -414,3 +414,18 @@ void AFCGUI::applySettings(bool force) m_afc->getInputMessageQueue()->push(message); } } + +void AFCGUI::makeUIConnections() +{ + QObject::connect(ui->startStop, &ButtonSwitch::toggled, this, &AFCGUI::on_startStop_toggled); + QObject::connect(ui->hasTargetFrequency, &ButtonSwitch::toggled, this, &AFCGUI::on_hasTargetFrequency_toggled); + QObject::connect(ui->targetFrequency, &ValueDial::changed, this, &AFCGUI::on_targetFrequency_changed); + QObject::connect(ui->transverterTarget, &ButtonSwitch::toggled, this, &AFCGUI::on_transverterTarget_toggled); + QObject::connect(ui->toleranceFrequency, &ValueDial::changed, this, &AFCGUI::on_toleranceFrequency_changed); + QObject::connect(ui->deviceTrack, &QPushButton::clicked, this, &AFCGUI::on_deviceTrack_clicked); + QObject::connect(ui->devicesRefresh, &QPushButton::clicked, this, &AFCGUI::on_devicesRefresh_clicked); + QObject::connect(ui->trackerDevice, QOverload::of(&QComboBox::currentIndexChanged), this, &AFCGUI::on_trackerDevice_currentIndexChanged); + QObject::connect(ui->trackedDevice, QOverload::of(&QComboBox::currentIndexChanged), this, &AFCGUI::on_trackedDevice_currentIndexChanged); + QObject::connect(ui->devicesApply, &QPushButton::clicked, this, &AFCGUI::on_devicesApply_clicked); + QObject::connect(ui->targetPeriod, &QDial::valueChanged, this, &AFCGUI::on_targetPeriod_valueChanged); +} diff --git a/plugins/feature/afc/afcgui.h b/plugins/feature/afc/afcgui.h index 9f76e4333..1b376f9a0 100644 --- a/plugins/feature/afc/afcgui.h +++ b/plugins/feature/afc/afcgui.h @@ -67,6 +67,7 @@ private: void requestDeviceSetLists(); void updateDeviceSetLists(const AFC::MsgDeviceSetListsReport& report); bool handleMessage(const Message& message); + void makeUIConnections(); void leaveEvent(QEvent*); void enterEvent(QEvent*); diff --git a/plugins/feature/afc/afcgui.ui b/plugins/feature/afc/afcgui.ui index bb6c85553..373da46f5 100644 --- a/plugins/feature/afc/afcgui.ui +++ b/plugins/feature/afc/afcgui.ui @@ -1,7 +1,7 @@ AFCGUI - + 0 @@ -406,12 +406,6 @@ - - RollupWidget - QWidget -
gui/rollupwidget.h
- 1 -
ButtonSwitch QToolButton @@ -423,6 +417,12 @@
gui/valuedial.h
1
+ + RollupContents + QWidget +
gui/rollupcontents.h
+ 1 +
diff --git a/plugins/feature/ais/aisgui.cpp b/plugins/feature/ais/aisgui.cpp index 9b284bbdd..e480f9223 100644 --- a/plugins/feature/ais/aisgui.cpp +++ b/plugins/feature/ais/aisgui.cpp @@ -166,7 +166,8 @@ void AISGUI::onWidgetRolled(QWidget* widget, bool rollDown) (void) widget; (void) rollDown; - saveState(m_rollupState); + shrinkWindow(); + getRollupContents()->saveState(m_rollupState); applySettings(); } @@ -178,11 +179,11 @@ AISGUI::AISGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISet, Feature *featur m_doApplySettings(true), m_lastFeatureState(0) { - ui->setupUi(this); + ui->setupUi(getRollupContents()); + getRollupContents()->arrangeRollups(); m_helpURL = "plugins/feature/ais/readme.md"; setAttribute(Qt::WA_DeleteOnClose, true); - setChannelWidget(false); - connect(this, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + connect(getRollupContents(), SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); m_ais = reinterpret_cast(feature); m_ais->setMessageQueueToGUI(&m_inputMessageQueue); @@ -238,6 +239,7 @@ void AISGUI::displaySettings() { setTitleColor(m_settings.m_rgbColor); setWindowTitle(m_settings.m_title); + setTitle(m_settings.m_title); blockApplySettings(true); // Order and size columns @@ -253,9 +255,9 @@ void AISGUI::displaySettings() header->moveSection(header->visualIndex(i), m_settings.m_vesselColumnIndexes[i]); } - restoreState(m_rollupState); + getRollupContents()->restoreState(m_rollupState); blockApplySettings(false); - arrangeRollups(); + getRollupContents()->arrangeRollups(); } void AISGUI::leaveEvent(QEvent*) @@ -272,7 +274,6 @@ void AISGUI::onMenuDialogCalled(const QPoint &p) { BasicFeatureSettingsDialog dialog(this); dialog.setTitle(m_settings.m_title); - dialog.setColor(m_settings.m_rgbColor); dialog.setUseReverseAPI(m_settings.m_useReverseAPI); dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); @@ -282,7 +283,6 @@ void AISGUI::onMenuDialogCalled(const QPoint &p) dialog.move(p); dialog.exec(); - m_settings.m_rgbColor = dialog.getColor().rgb(); m_settings.m_title = dialog.getTitle(); m_settings.m_useReverseAPI = dialog.useReverseAPI(); m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); diff --git a/plugins/feature/ais/aisgui.ui b/plugins/feature/ais/aisgui.ui index 5c3b9fb9f..bfeaf9342 100644 --- a/plugins/feature/ais/aisgui.ui +++ b/plugins/feature/ais/aisgui.ui @@ -1,7 +1,7 @@ AISGUI - + 0 @@ -206,9 +206,9 @@ - RollupWidget + RollupContents QWidget -
gui/rollupwidget.h
+
gui/rollupcontents.h
1
diff --git a/plugins/feature/antennatools/antennatoolsgui.cpp b/plugins/feature/antennatools/antennatoolsgui.cpp index f37f7fef8..07dc57f00 100644 --- a/plugins/feature/antennatools/antennatoolsgui.cpp +++ b/plugins/feature/antennatools/antennatoolsgui.cpp @@ -102,7 +102,7 @@ void AntennaToolsGUI::onWidgetRolled(QWidget* widget, bool rollDown) (void) widget; (void) rollDown; - saveState(m_rollupState); + getRollupContents()->saveState(m_rollupState); applySettings(); } @@ -114,12 +114,12 @@ AntennaToolsGUI::AntennaToolsGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISe m_doApplySettings(true), m_deviceSets(0) { - ui->setupUi(this); + ui->setupUi(getRollupContents()); + getRollupContents()->arrangeRollups(); m_helpURL = "plugins/feature/antennatools/readme.md"; setAttribute(Qt::WA_DeleteOnClose, true); - setChannelWidget(false); - connect(this, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + connect(getRollupContents(), SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); m_antennatools = reinterpret_cast(feature); m_antennatools->setMessageQueueToGUI(&m_inputMessageQueue); @@ -136,6 +136,7 @@ AntennaToolsGUI::AntennaToolsGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISe displaySettings(); applySettings(true); + makeUIConnections(); } AntennaToolsGUI::~AntennaToolsGUI() @@ -152,6 +153,7 @@ void AntennaToolsGUI::displaySettings() { setTitleColor(m_settings.m_rgbColor); setWindowTitle(m_settings.m_title); + setTitle(m_settings.m_title); blockApplySettings(true); ui->dipoleFrequency->setValue(m_settings.m_dipoleFrequencyMHz); ui->dipoleFrequencySelect->setCurrentIndex(m_settings.m_dipoleFrequencySelect); @@ -170,7 +172,24 @@ void AntennaToolsGUI::displaySettings() calcDishBeamwidth(); calcDishGain(); calcDishEffectiveArea(); - restoreState(m_rollupState); + getRollupContents()->restoreState(m_rollupState); +} + +void AntennaToolsGUI::makeUIConnections() +{ + QObject::connect(ui->dipoleFrequency, qOverload(&QDoubleSpinBox::valueChanged), this, &AntennaToolsGUI::on_dipoleFrequency_valueChanged); + QObject::connect(ui->dipoleFrequencySelect, qOverload(&QComboBox::currentIndexChanged), this, &AntennaToolsGUI::on_dipoleFrequencySelect_currentIndexChanged); + QObject::connect(ui->dipoleEndEffectFactor, qOverload(&QDoubleSpinBox::valueChanged), this, &AntennaToolsGUI::on_dipoleEndEffectFactor_valueChanged); + QObject::connect(ui->dipoleLengthUnits, qOverload(&QComboBox::currentIndexChanged), this, &AntennaToolsGUI::on_dipoleLengthUnits_currentIndexChanged); + QObject::connect(ui->dipoleLength, qOverload(&QDoubleSpinBox::valueChanged), this, &AntennaToolsGUI::on_dipoleLength_valueChanged); + QObject::connect(ui->dipoleElementLength, qOverload(&QDoubleSpinBox::valueChanged), this, &AntennaToolsGUI::on_dipoleElementLength_valueChanged); + QObject::connect(ui->dishFrequency, qOverload(&QDoubleSpinBox::valueChanged), this, &AntennaToolsGUI::on_dishFrequency_valueChanged); + QObject::connect(ui->dishFrequencySelect, qOverload(&QComboBox::currentIndexChanged), this, &AntennaToolsGUI::on_dishFrequencySelect_currentIndexChanged); + QObject::connect(ui->dishDiameter, qOverload(&QDoubleSpinBox::valueChanged), this, &AntennaToolsGUI::on_dishDiameter_valueChanged); + QObject::connect(ui->dishLengthUnits, qOverload(&QComboBox::currentIndexChanged), this, &AntennaToolsGUI::on_dishLengthUnits_currentIndexChanged); + QObject::connect(ui->dishDepth, qOverload(&QDoubleSpinBox::valueChanged), this, &AntennaToolsGUI::on_dishDepth_valueChanged); + QObject::connect(ui->dishEfficiency, qOverload(&QSpinBox::valueChanged), this, &AntennaToolsGUI::on_dishEfficiency_valueChanged); + QObject::connect(ui->dishSurfaceError, qOverload(&QDoubleSpinBox::valueChanged), this, &AntennaToolsGUI::on_dishSurfaceError_valueChanged); } void AntennaToolsGUI::leaveEvent(QEvent*) @@ -187,7 +206,6 @@ void AntennaToolsGUI::onMenuDialogCalled(const QPoint &p) { BasicFeatureSettingsDialog dialog(this); dialog.setTitle(m_settings.m_title); - dialog.setColor(m_settings.m_rgbColor); dialog.setUseReverseAPI(m_settings.m_useReverseAPI); dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); @@ -197,7 +215,6 @@ void AntennaToolsGUI::onMenuDialogCalled(const QPoint &p) dialog.move(p); dialog.exec(); - m_settings.m_rgbColor = dialog.getColor().rgb(); m_settings.m_title = dialog.getTitle(); m_settings.m_useReverseAPI = dialog.useReverseAPI(); m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); diff --git a/plugins/feature/antennatools/antennatoolsgui.h b/plugins/feature/antennatools/antennatoolsgui.h index 5d4213ab4..c21c8654b 100644 --- a/plugins/feature/antennatools/antennatoolsgui.h +++ b/plugins/feature/antennatools/antennatoolsgui.h @@ -67,6 +67,7 @@ private: void applySettings(bool force = false); void displaySettings(); bool handleMessage(const Message& message); + void makeUIConnections(); void leaveEvent(QEvent*); void enterEvent(QEvent*); diff --git a/plugins/feature/antennatools/antennatoolsgui.ui b/plugins/feature/antennatools/antennatoolsgui.ui index 7cdbb6a19..73cff9790 100644 --- a/plugins/feature/antennatools/antennatoolsgui.ui +++ b/plugins/feature/antennatools/antennatoolsgui.ui @@ -1,7 +1,7 @@ AntennaToolsGUI - + 0 @@ -503,9 +503,9 @@ - RollupWidget + RollupContents QWidget -
gui/rollupwidget.h
+
gui/rollupcontents.h
1
diff --git a/plugins/feature/aprs/aprsgui.cpp b/plugins/feature/aprs/aprsgui.cpp index 1023bb53b..bc4949c8c 100644 --- a/plugins/feature/aprs/aprsgui.cpp +++ b/plugins/feature/aprs/aprsgui.cpp @@ -418,7 +418,7 @@ void APRSGUI::onWidgetRolled(QWidget* widget, bool rollDown) (void) widget; (void) rollDown; - saveState(m_rollupState); + getRollupContents()->saveState(m_rollupState); applySettings(); } @@ -430,12 +430,12 @@ APRSGUI::APRSGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISet, Feature *feat m_doApplySettings(true), m_lastFeatureState(0) { - ui->setupUi(this); + ui->setupUi(getRollupContents()); + getRollupContents()->arrangeRollups(); m_helpURL = "plugins/feature/aprs/readme.md"; setAttribute(Qt::WA_DeleteOnClose, true); - setChannelWidget(false); - connect(this, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + connect(getRollupContents(), SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); m_aprs = reinterpret_cast(feature); m_aprs->setMessageQueueToGUI(&m_inputMessageQueue); @@ -564,6 +564,7 @@ APRSGUI::APRSGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISet, Feature *feat displaySettings(); applySettings(true); + makeUIConnections(); } APRSGUI::~APRSGUI() @@ -629,6 +630,7 @@ void APRSGUI::displaySettings() { setTitleColor(m_settings.m_rgbColor); setWindowTitle(m_settings.m_title); + setTitle(m_settings.m_title); blockApplySettings(true); ui->igate->setChecked(m_settings.m_igateEnabled); ui->stationFilter->setCurrentIndex((int)m_settings.m_stationFilter); @@ -643,7 +645,7 @@ void APRSGUI::displaySettings() displayTableSettings(ui->telemetryTable, telemetryTableMenu, m_settings.m_telemetryTableColumnSizes, m_settings.m_telemetryTableColumnIndexes, APRS_TELEMETRY_TABLE_COLUMNS); displayTableSettings(ui->motionTable, motionTableMenu, m_settings.m_motionTableColumnSizes, m_settings.m_motionTableColumnIndexes, APRS_MOTION_TABLE_COLUMNS); - restoreState(m_rollupState); + getRollupContents()->restoreState(m_rollupState); blockApplySettings(false); } @@ -683,7 +685,6 @@ void APRSGUI::onMenuDialogCalled(const QPoint &p) { BasicFeatureSettingsDialog dialog(this); dialog.setTitle(m_settings.m_title); - dialog.setColor(m_settings.m_rgbColor); dialog.setUseReverseAPI(m_settings.m_useReverseAPI); dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); @@ -693,7 +694,6 @@ void APRSGUI::onMenuDialogCalled(const QPoint &p) dialog.move(p); dialog.exec(); - m_settings.m_rgbColor = dialog.getColor().rgb(); m_settings.m_title = dialog.getTitle(); m_settings.m_useReverseAPI = dialog.useReverseAPI(); m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); @@ -2026,3 +2026,20 @@ void APRSGUI::on_viewOnMap_clicked() } } } + +void APRSGUI::makeUIConnections() +{ + QObject::connect(ui->stationFilter, qOverload(&QComboBox::currentIndexChanged), this, &APRSGUI::on_stationFilter_currentIndexChanged); + QObject::connect(ui->stationSelect, qOverload(&QComboBox::currentIndexChanged), this, &APRSGUI::on_stationSelect_currentIndexChanged); + QObject::connect(ui->filterAddressee, &QLineEdit::editingFinished, this, &APRSGUI::on_filterAddressee_editingFinished); + QObject::connect(ui->deleteMessages, &QPushButton::clicked, this, &APRSGUI::on_deleteMessages_clicked); + QObject::connect(ui->weatherTimeSelect, qOverload(&QComboBox::currentIndexChanged), this, &APRSGUI::on_weatherTimeSelect_currentIndexChanged); + QObject::connect(ui->weatherPlotSelect, qOverload(&QComboBox::currentIndexChanged), this, &APRSGUI::on_weatherPlotSelect_currentIndexChanged); + QObject::connect(ui->telemetryTimeSelect, qOverload(&QComboBox::currentIndexChanged), this, &APRSGUI::on_telemetryTimeSelect_currentIndexChanged); + QObject::connect(ui->telemetryPlotSelect, qOverload(&QComboBox::currentIndexChanged), this, &APRSGUI::on_telemetryPlotSelect_currentIndexChanged); + QObject::connect(ui->motionTimeSelect, qOverload(&QComboBox::currentIndexChanged), this, &APRSGUI::on_motionTimeSelect_currentIndexChanged); + QObject::connect(ui->motionPlotSelect, qOverload(&QComboBox::currentIndexChanged), this, &APRSGUI::on_motionPlotSelect_currentIndexChanged); + QObject::connect(ui->displaySettings, &QPushButton::clicked, this, &APRSGUI::on_displaySettings_clicked); + QObject::connect(ui->igate, &ButtonSwitch::toggled, this, &APRSGUI::on_igate_toggled); + QObject::connect(ui->viewOnMap, &QPushButton::clicked, this, &APRSGUI::on_viewOnMap_clicked); +} diff --git a/plugins/feature/aprs/aprsgui.h b/plugins/feature/aprs/aprsgui.h index 170c2282f..412387164 100644 --- a/plugins/feature/aprs/aprsgui.h +++ b/plugins/feature/aprs/aprsgui.h @@ -155,6 +155,7 @@ private: void displaySettings(); void updateChannelList(); bool handleMessage(const Message& message); + void makeUIConnections(); void leaveEvent(QEvent*); void enterEvent(QEvent*); diff --git a/plugins/feature/aprs/aprsgui.ui b/plugins/feature/aprs/aprsgui.ui index 68a8c1acf..8194bb504 100644 --- a/plugins/feature/aprs/aprsgui.ui +++ b/plugins/feature/aprs/aprsgui.ui @@ -1,7 +1,7 @@ APRSGUI - + 0 @@ -1447,17 +1447,17 @@ - - RollupWidget - QWidget -
gui/rollupwidget.h
- 1 -
ButtonSwitch QToolButton
gui/buttonswitch.h
+ + RollupContents + QWidget +
gui/rollupcontents.h
+ 1 +
QChartView QGraphicsView diff --git a/plugins/feature/demodanalyzer/demodanalyzergui.cpp b/plugins/feature/demodanalyzer/demodanalyzergui.cpp index 7b064fc4a..4d42071bb 100644 --- a/plugins/feature/demodanalyzer/demodanalyzergui.cpp +++ b/plugins/feature/demodanalyzer/demodanalyzergui.cpp @@ -125,7 +125,7 @@ void DemodAnalyzerGUI::onWidgetRolled(QWidget* widget, bool rollDown) (void) widget; (void) rollDown; - saveState(m_rollupState); + getRollupContents()->saveState(m_rollupState); applySettings(); } @@ -139,11 +139,11 @@ DemodAnalyzerGUI::DemodAnalyzerGUI(PluginAPI* pluginAPI, FeatureUISet *featureUI m_lastFeatureState(0), m_selectedChannel(nullptr) { - ui->setupUi(this); + ui->setupUi(getRollupContents()); + getRollupContents()->arrangeRollups(); m_helpURL = "plugins/feature/demodanalyzer/readme.md"; setAttribute(Qt::WA_DeleteOnClose, true); - setChannelWidget(false); - connect(this, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + connect(getRollupContents(), SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); m_demodAnalyzer = reinterpret_cast(feature); m_demodAnalyzer->setMessageQueueToGUI(&m_inputMessageQueue); @@ -177,6 +177,7 @@ DemodAnalyzerGUI::DemodAnalyzerGUI(PluginAPI* pluginAPI, FeatureUISet *featureUI displaySettings(); applySettings(true); + makeUIConnections(); } DemodAnalyzerGUI::~DemodAnalyzerGUI() @@ -193,9 +194,10 @@ void DemodAnalyzerGUI::displaySettings() { setTitleColor(m_settings.m_rgbColor); setWindowTitle(m_settings.m_title); + setTitle(m_settings.m_title); blockApplySettings(true); ui->log2Decim->setCurrentIndex(m_settings.m_log2Decim); - restoreState(m_rollupState); + getRollupContents()->restoreState(m_rollupState); blockApplySettings(false); } @@ -253,7 +255,6 @@ void DemodAnalyzerGUI::onMenuDialogCalled(const QPoint &p) { BasicFeatureSettingsDialog dialog(this); dialog.setTitle(m_settings.m_title); - dialog.setColor(m_settings.m_rgbColor); dialog.setUseReverseAPI(m_settings.m_useReverseAPI); dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); @@ -263,7 +264,6 @@ void DemodAnalyzerGUI::onMenuDialogCalled(const QPoint &p) dialog.move(p); dialog.exec(); - m_settings.m_rgbColor = dialog.getColor().rgb(); m_settings.m_title = dialog.getTitle(); m_settings.m_useReverseAPI = dialog.useReverseAPI(); m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); @@ -369,3 +369,12 @@ void DemodAnalyzerGUI::applySettings(bool force) m_demodAnalyzer->getInputMessageQueue()->push(message); } } + +void DemodAnalyzerGUI::makeUIConnections() +{ + QObject::connect(ui->startStop, &ButtonSwitch::toggled, this, &DemodAnalyzerGUI::on_startStop_toggled); + QObject::connect(ui->devicesRefresh, &QPushButton::clicked, this, &DemodAnalyzerGUI::on_devicesRefresh_clicked); + QObject::connect(ui->channels, qOverload(&QComboBox::currentIndexChanged), this, &DemodAnalyzerGUI::on_channels_currentIndexChanged); + QObject::connect(ui->channelApply, &QPushButton::clicked, this, &DemodAnalyzerGUI::on_channelApply_clicked); + QObject::connect(ui->log2Decim, qOverload(&QComboBox::currentIndexChanged), this, &DemodAnalyzerGUI::on_log2Decim_currentIndexChanged); +} diff --git a/plugins/feature/demodanalyzer/demodanalyzergui.h b/plugins/feature/demodanalyzer/demodanalyzergui.h index 69f0fab22..b11100ecf 100644 --- a/plugins/feature/demodanalyzer/demodanalyzergui.h +++ b/plugins/feature/demodanalyzer/demodanalyzergui.h @@ -78,6 +78,7 @@ private: void displaySampleRate(int sampleRate); void updateChannelList(); bool handleMessage(const Message& message); + void makeUIConnections(); void leaveEvent(QEvent*); void enterEvent(QEvent*); diff --git a/plugins/feature/demodanalyzer/demodanalyzergui.ui b/plugins/feature/demodanalyzer/demodanalyzergui.ui index 49a3fdcd0..6b4316257 100644 --- a/plugins/feature/demodanalyzer/demodanalyzergui.ui +++ b/plugins/feature/demodanalyzer/demodanalyzergui.ui @@ -1,7 +1,7 @@ DemodAnalyzerGUI - + 0 @@ -370,9 +370,14 @@ - RollupWidget + ButtonSwitch + QToolButton +
gui/buttonswitch.h
+
+ + RollupContents QWidget -
gui/rollupwidget.h
+
gui/rollupcontents.h
1
@@ -387,11 +392,6 @@
gui/glspectrumgui.h
1
- - ButtonSwitch - QToolButton -
gui/buttonswitch.h
-
GLScope QWidget diff --git a/plugins/feature/gs232controller/gs232controllergui.cpp b/plugins/feature/gs232controller/gs232controllergui.cpp index 6d8110bc7..ffba05030 100644 --- a/plugins/feature/gs232controller/gs232controllergui.cpp +++ b/plugins/feature/gs232controller/gs232controllergui.cpp @@ -127,7 +127,7 @@ void GS232ControllerGUI::onWidgetRolled(QWidget* widget, bool rollDown) { (void) widget; (void) rollDown; - saveState(m_rollupState); + getRollupContents()->saveState(m_rollupState); applySettings(); } @@ -140,11 +140,11 @@ GS232ControllerGUI::GS232ControllerGUI(PluginAPI* pluginAPI, FeatureUISet *featu m_lastFeatureState(0), m_lastOnTarget(false) { - ui->setupUi(this); + ui->setupUi(getRollupContents()); + getRollupContents()->arrangeRollups(); m_helpURL = "plugins/feature/gs232controller/readme.md"; setAttribute(Qt::WA_DeleteOnClose, true); - setChannelWidget(false); - connect(this, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + connect(getRollupContents(), SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); m_gs232Controller = reinterpret_cast(feature); m_gs232Controller->setMessageQueueToGUI(&m_inputMessageQueue); @@ -165,6 +165,7 @@ GS232ControllerGUI::GS232ControllerGUI(PluginAPI* pluginAPI, FeatureUISet *featu displaySettings(); applySettings(true); + makeUIConnections(); } GS232ControllerGUI::~GS232ControllerGUI() @@ -181,6 +182,7 @@ void GS232ControllerGUI::displaySettings() { setTitleColor(m_settings.m_rgbColor); setWindowTitle(m_settings.m_title); + setTitle(m_settings.m_title); blockApplySettings(true); ui->azimuth->setValue(m_settings.m_azimuth); ui->elevation->setValue(m_settings.m_elevation); @@ -202,7 +204,7 @@ void GS232ControllerGUI::displaySettings() ui->elevationMin->setValue(m_settings.m_elevationMin); ui->elevationMax->setValue(m_settings.m_elevationMax); ui->tolerance->setValue(m_settings.m_tolerance); - restoreState(m_rollupState); + getRollupContents()->restoreState(m_rollupState); updateConnectionWidgets(); blockApplySettings(false); } @@ -298,7 +300,6 @@ void GS232ControllerGUI::onMenuDialogCalled(const QPoint &p) { BasicFeatureSettingsDialog dialog(this); dialog.setTitle(m_settings.m_title); - dialog.setColor(m_settings.m_rgbColor); dialog.setUseReverseAPI(m_settings.m_useReverseAPI); dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); @@ -308,7 +309,6 @@ void GS232ControllerGUI::onMenuDialogCalled(const QPoint &p) dialog.move(p); dialog.exec(); - m_settings.m_rgbColor = dialog.getColor().rgb(); m_settings.m_title = dialog.getTitle(); m_settings.m_useReverseAPI = dialog.useReverseAPI(); m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); @@ -528,3 +528,25 @@ void GS232ControllerGUI::applySettings(bool force) m_gs232Controller->getInputMessageQueue()->push(message); } } + +void GS232ControllerGUI::makeUIConnections() +{ + QObject::connect(ui->startStop, &ButtonSwitch::toggled, this, &GS232ControllerGUI::on_startStop_toggled); + QObject::connect(ui->protocol, qOverload(&QComboBox::currentIndexChanged), this, &GS232ControllerGUI::on_protocol_currentIndexChanged); + QObject::connect(ui->connection, qOverload(&QComboBox::currentIndexChanged), this, &GS232ControllerGUI::on_connection_currentIndexChanged); + QObject::connect(ui->serialPort, qOverload(&QComboBox::currentIndexChanged), this, &GS232ControllerGUI::on_serialPort_currentIndexChanged); + QObject::connect(ui->host, &QLineEdit::editingFinished, this, &GS232ControllerGUI::on_host_editingFinished); + QObject::connect(ui->port, qOverload(&QSpinBox::valueChanged), this, &GS232ControllerGUI::on_port_valueChanged); + QObject::connect(ui->baudRate, qOverload(&QComboBox::currentIndexChanged), this, &GS232ControllerGUI::on_baudRate_currentIndexChanged); + QObject::connect(ui->track, &QCheckBox::stateChanged, this, &GS232ControllerGUI::on_track_stateChanged); + QObject::connect(ui->azimuth, qOverload(&QDoubleSpinBox::valueChanged), this, &GS232ControllerGUI::on_azimuth_valueChanged); + QObject::connect(ui->elevation, qOverload(&QDoubleSpinBox::valueChanged), this, &GS232ControllerGUI::on_elevation_valueChanged); + QObject::connect(ui->sources, &QComboBox::currentTextChanged, this, &GS232ControllerGUI::on_sources_currentTextChanged); + QObject::connect(ui->azimuthOffset, qOverload(&QSpinBox::valueChanged), this, &GS232ControllerGUI::on_azimuthOffset_valueChanged); + QObject::connect(ui->elevationOffset, qOverload(&QSpinBox::valueChanged), this, &GS232ControllerGUI::on_elevationOffset_valueChanged); + QObject::connect(ui->azimuthMin, qOverload(&QSpinBox::valueChanged), this, &GS232ControllerGUI::on_azimuthMin_valueChanged); + QObject::connect(ui->azimuthMax, qOverload(&QSpinBox::valueChanged), this, &GS232ControllerGUI::on_azimuthMax_valueChanged); + QObject::connect(ui->elevationMin, qOverload(&QSpinBox::valueChanged), this, &GS232ControllerGUI::on_elevationMin_valueChanged); + QObject::connect(ui->elevationMax, qOverload(&QSpinBox::valueChanged), this, &GS232ControllerGUI::on_elevationMax_valueChanged); + QObject::connect(ui->tolerance, qOverload(&QDoubleSpinBox::valueChanged), this, &GS232ControllerGUI::on_tolerance_valueChanged); +} diff --git a/plugins/feature/gs232controller/gs232controllergui.h b/plugins/feature/gs232controller/gs232controllergui.h index 5ecdd8932..1fbd42d03 100644 --- a/plugins/feature/gs232controller/gs232controllergui.h +++ b/plugins/feature/gs232controller/gs232controllergui.h @@ -71,6 +71,7 @@ private: void updatePipeList(const QList& sources); void updateSerialPortList(); bool handleMessage(const Message& message); + void makeUIConnections(); void leaveEvent(QEvent*); void enterEvent(QEvent*); diff --git a/plugins/feature/gs232controller/gs232controllergui.ui b/plugins/feature/gs232controller/gs232controllergui.ui index 656be9249..3b7ff827b 100644 --- a/plugins/feature/gs232controller/gs232controllergui.ui +++ b/plugins/feature/gs232controller/gs232controllergui.ui @@ -1,7 +1,7 @@ GS232ControllerGUI - + 0 @@ -541,17 +541,17 @@ - - RollupWidget - QWidget -
gui/rollupwidget.h
- 1 -
ButtonSwitch QToolButton
gui/buttonswitch.h
+ + RollupContents + QWidget +
gui/rollupcontents.h
+ 1 +
startStop diff --git a/plugins/feature/jogdialcontroller/jogdialcontrollergui.cpp b/plugins/feature/jogdialcontroller/jogdialcontrollergui.cpp index 12afa2009..56a038a48 100644 --- a/plugins/feature/jogdialcontroller/jogdialcontrollergui.cpp +++ b/plugins/feature/jogdialcontroller/jogdialcontrollergui.cpp @@ -132,7 +132,7 @@ void JogdialControllerGUI::onWidgetRolled(QWidget* widget, bool rollDown) (void) widget; (void) rollDown; - saveState(m_rollupState); + getRollupContents()->saveState(m_rollupState); applySettings(); } @@ -145,11 +145,11 @@ JogdialControllerGUI::JogdialControllerGUI(PluginAPI* pluginAPI, FeatureUISet *f m_lastFeatureState(0), m_selectedChannel(nullptr) { - ui->setupUi(this); + ui->setupUi(getRollupContents()); + getRollupContents()->arrangeRollups(); m_helpURL = "plugins/feature/jogdialcontroller/readme.md"; setAttribute(Qt::WA_DeleteOnClose, true); - setChannelWidget(false); - connect(this, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + connect(getRollupContents(), SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); m_jogdialController = reinterpret_cast(feature); m_jogdialController->setMessageQueueToGUI(&m_inputMessageQueue); @@ -169,6 +169,7 @@ JogdialControllerGUI::JogdialControllerGUI(PluginAPI* pluginAPI, FeatureUISet *f displaySettings(); applySettings(true); + makeUIConnections(); } JogdialControllerGUI::~JogdialControllerGUI() @@ -185,8 +186,9 @@ void JogdialControllerGUI::displaySettings() { setTitleColor(m_settings.m_rgbColor); setWindowTitle(m_settings.m_title); + setTitle(m_settings.m_title); blockApplySettings(true); - restoreState(m_rollupState); + getRollupContents()->restoreState(m_rollupState); blockApplySettings(false); } @@ -243,7 +245,6 @@ void JogdialControllerGUI::onMenuDialogCalled(const QPoint &p) { BasicFeatureSettingsDialog dialog(this); dialog.setTitle(m_settings.m_title); - dialog.setColor(m_settings.m_rgbColor); dialog.setUseReverseAPI(m_settings.m_useReverseAPI); dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); @@ -253,7 +254,6 @@ void JogdialControllerGUI::onMenuDialogCalled(const QPoint &p) dialog.move(p); dialog.exec(); - m_settings.m_rgbColor = dialog.getColor().rgb(); m_settings.m_title = dialog.getTitle(); m_settings.m_useReverseAPI = dialog.useReverseAPI(); m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); @@ -362,3 +362,10 @@ void JogdialControllerGUI::focusOutEvent(QFocusEvent*) ui->focusIndicator->setStyleSheet("QLabel { background-color: gray; border-radius: 8px; }"); // gray ui->focusIndicator->setToolTip("Idle"); } + +void JogdialControllerGUI::makeUIConnections() +{ + QObject::connect(ui->startStop, &ButtonSwitch::toggled, this, &JogdialControllerGUI::on_startStop_toggled); + QObject::connect(ui->devicesRefresh, &QPushButton::clicked, this, &JogdialControllerGUI::on_devicesRefresh_clicked); + QObject::connect(ui->channels, qOverload(&QComboBox::currentIndexChanged), this, &JogdialControllerGUI::on_channels_currentIndexChanged); +} diff --git a/plugins/feature/jogdialcontroller/jogdialcontrollergui.h b/plugins/feature/jogdialcontroller/jogdialcontrollergui.h index a6b680ceb..d7d27633c 100644 --- a/plugins/feature/jogdialcontroller/jogdialcontrollergui.h +++ b/plugins/feature/jogdialcontroller/jogdialcontrollergui.h @@ -76,6 +76,7 @@ private: void displaySettings(); void updateChannelList(); bool handleMessage(const Message& message); + void makeUIConnections(); void leaveEvent(QEvent*); void enterEvent(QEvent*); diff --git a/plugins/feature/jogdialcontroller/jogdialcontrollergui.ui b/plugins/feature/jogdialcontroller/jogdialcontrollergui.ui index 5b9a8e687..142ba45fc 100644 --- a/plugins/feature/jogdialcontroller/jogdialcontrollergui.ui +++ b/plugins/feature/jogdialcontroller/jogdialcontrollergui.ui @@ -1,7 +1,7 @@ JogdialControllerGUI - + 0 @@ -193,17 +193,17 @@ - - RollupWidget - QWidget -
gui/rollupwidget.h
- 1 -
ButtonSwitch QToolButton
gui/buttonswitch.h
+ + RollupContents + QWidget +
gui/rollupcontents.h
+ 1 +
diff --git a/plugins/feature/map/mapgui.cpp b/plugins/feature/map/mapgui.cpp index c344eb856..de5401908 100644 --- a/plugins/feature/map/mapgui.cpp +++ b/plugins/feature/map/mapgui.cpp @@ -162,7 +162,7 @@ void MapGUI::onWidgetRolled(QWidget* widget, bool rollDown) (void) widget; (void) rollDown; - saveState(m_rollupState); + getRollupContents()->saveState(m_rollupState); applySettings(); } @@ -179,7 +179,8 @@ MapGUI::MapGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISet, Feature *featur m_radioTimeDialog(this), m_cesium(nullptr) { - ui->setupUi(this); + ui->setupUi(getRollupContents()); + getRollupContents()->arrangeRollups(); m_helpURL = "plugins/feature/map/readme.md"; m_osmPort = 0; @@ -201,8 +202,7 @@ MapGUI::MapGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISet, Feature *featur m_webServer->addPathSubstitution("3d", m_settings.m_modelDir); setAttribute(Qt::WA_DeleteOnClose, true); - setChannelWidget(false); - connect(this, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + connect(getRollupContents(), SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); m_map = reinterpret_cast(feature); m_map->setMessageQueueToGUI(&m_inputMessageQueue); @@ -268,6 +268,8 @@ MapGUI::MapGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISet, Feature *featur connect(&m_redrawMapTimer, &QTimer::timeout, this, &MapGUI::redrawMap); m_redrawMapTimer.setSingleShot(true); ui->map->installEventFilter(this); + + makeUIConnections(); } MapGUI::~MapGUI() @@ -847,6 +849,7 @@ void MapGUI::displaySettings() { setTitleColor(m_settings.m_rgbColor); setWindowTitle(m_settings.m_title); + setTitle(m_settings.m_title); blockApplySettings(true); ui->displayNames->setChecked(m_settings.m_displayNames); ui->displaySelectedGroundTracks->setChecked(m_settings.m_displaySelectedGroundTracks); @@ -857,7 +860,7 @@ void MapGUI::displaySettings() m_mapModel.updateItemSettings(m_settings.m_itemSettings); applyMap2DSettings(true); applyMap3DSettings(true); - restoreState(m_rollupState); + getRollupContents()->restoreState(m_rollupState); blockApplySettings(false); } @@ -875,7 +878,6 @@ void MapGUI::onMenuDialogCalled(const QPoint &p) { BasicFeatureSettingsDialog dialog(this); dialog.setTitle(m_settings.m_title); - dialog.setColor(m_settings.m_rgbColor); dialog.setUseReverseAPI(m_settings.m_useReverseAPI); dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); @@ -885,7 +887,6 @@ void MapGUI::onMenuDialogCalled(const QPoint &p) dialog.move(p); dialog.exec(); - m_settings.m_rgbColor = dialog.getColor().rgb(); m_settings.m_title = dialog.getTitle(); m_settings.m_useReverseAPI = dialog.useReverseAPI(); m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); @@ -1214,3 +1215,18 @@ void MapGUI::preferenceChanged(int elementType) update(m_map, &m_antennaMapItem, "Station"); } } + +void MapGUI::makeUIConnections() +{ + QObject::connect(ui->displayNames, &ButtonSwitch::clicked, this, &MapGUI::on_displayNames_clicked); + QObject::connect(ui->displayAllGroundTracks, &ButtonSwitch::clicked, this, &MapGUI::on_displayAllGroundTracks_clicked); + QObject::connect(ui->displaySelectedGroundTracks, &ButtonSwitch::clicked, this, &MapGUI::on_displaySelectedGroundTracks_clicked); + QObject::connect(ui->find, &QLineEdit::returnPressed, this, &MapGUI::on_find_returnPressed); + QObject::connect(ui->maidenhead, &QToolButton::clicked, this, &MapGUI::on_maidenhead_clicked); + QObject::connect(ui->deleteAll, &QToolButton::clicked, this, &MapGUI::on_deleteAll_clicked); + QObject::connect(ui->displaySettings, &QToolButton::clicked, this, &MapGUI::on_displaySettings_clicked); + QObject::connect(ui->mapTypes, qOverload(&QComboBox::currentIndexChanged), this, &MapGUI::on_mapTypes_currentIndexChanged); + QObject::connect(ui->beacons, &QToolButton::clicked, this, &MapGUI::on_beacons_clicked); + QObject::connect(ui->ibpBeacons, &QToolButton::clicked, this, &MapGUI::on_ibpBeacons_clicked); + QObject::connect(ui->radiotime, &QToolButton::clicked, this, &MapGUI::on_radiotime_clicked); +} diff --git a/plugins/feature/map/mapgui.h b/plugins/feature/map/mapgui.h index 96cbf2c2b..16ad02a19 100644 --- a/plugins/feature/map/mapgui.h +++ b/plugins/feature/map/mapgui.h @@ -132,6 +132,7 @@ private: QString maptilerAPIKey() const; QString cesiumIonAPIKey() const; void redrawMap(); + void makeUIConnections(); void leaveEvent(QEvent*); void enterEvent(QEvent*); diff --git a/plugins/feature/map/mapgui.ui b/plugins/feature/map/mapgui.ui index c0596d44a..70834ba70 100644 --- a/plugins/feature/map/mapgui.ui +++ b/plugins/feature/map/mapgui.ui @@ -1,7 +1,7 @@ MapGUI - + 0 @@ -339,7 +339,7 @@ - + 0 @@ -365,9 +365,9 @@
QtQuickWidgets/QQuickWidget
- RollupWidget + QWebEngineView QWidget -
gui/rollupwidget.h
+
QtWebEngineWidgets/QWebEngineView
1
@@ -376,9 +376,9 @@
gui/buttonswitch.h
- QWebEngineView + RollupContents QWidget -
QWebEngineView
+
gui/rollupcontents.h
1
diff --git a/plugins/feature/pertester/pertestergui.cpp b/plugins/feature/pertester/pertestergui.cpp index cc3083931..d53ce34c3 100644 --- a/plugins/feature/pertester/pertestergui.cpp +++ b/plugins/feature/pertester/pertestergui.cpp @@ -114,7 +114,7 @@ void PERTesterGUI::onWidgetRolled(QWidget* widget, bool rollDown) (void) widget; (void) rollDown; - saveState(m_rollupState); + getRollupContents()->saveState(m_rollupState); applySettings(); } @@ -126,11 +126,11 @@ PERTesterGUI::PERTesterGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISet, Fea m_doApplySettings(true), m_lastFeatureState(0) { - ui->setupUi(this); + ui->setupUi(getRollupContents()); + getRollupContents()->arrangeRollups(); m_helpURL = "plugins/feature/pertester/readme.md"; setAttribute(Qt::WA_DeleteOnClose, true); - setChannelWidget(false); - connect(this, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + connect(getRollupContents(), SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); m_perTester = reinterpret_cast(feature); m_perTester->setMessageQueueToGUI(&m_inputMessageQueue); @@ -145,6 +145,7 @@ PERTesterGUI::PERTesterGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISet, Fea displaySettings(); applySettings(true); + makeUIConnections(); } PERTesterGUI::~PERTesterGUI() @@ -161,6 +162,7 @@ void PERTesterGUI::displaySettings() { setTitleColor(m_settings.m_rgbColor); setWindowTitle(m_settings.m_title); + setTitle(m_settings.m_title); blockApplySettings(true); ui->packetCount->setValue(m_settings.m_packetCount); ui->start->setCurrentIndex((int)m_settings.m_start); @@ -175,9 +177,9 @@ void PERTesterGUI::displaySettings() ui->txUDPPort->setText(QString::number(m_settings.m_txUDPPort)); ui->rxUDPAddress->setText(m_settings.m_rxUDPAddress); ui->rxUDPPort->setText(QString::number(m_settings.m_rxUDPPort)); - restoreState(m_rollupState); + getRollupContents()->restoreState(m_rollupState); blockApplySettings(false); - arrangeRollups(); + getRollupContents()->arrangeRollups(); } void PERTesterGUI::leaveEvent(QEvent*) @@ -194,7 +196,6 @@ void PERTesterGUI::onMenuDialogCalled(const QPoint &p) { BasicFeatureSettingsDialog dialog(this); dialog.setTitle(m_settings.m_title); - dialog.setColor(m_settings.m_rgbColor); dialog.setUseReverseAPI(m_settings.m_useReverseAPI); dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); @@ -204,7 +205,6 @@ void PERTesterGUI::onMenuDialogCalled(const QPoint &p) dialog.move(p); dialog.exec(); - m_settings.m_rgbColor = dialog.getColor().rgb(); m_settings.m_title = dialog.getTitle(); m_settings.m_useReverseAPI = dialog.useReverseAPI(); m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); @@ -251,7 +251,7 @@ void PERTesterGUI::on_start_currentIndexChanged(int index) ui->satellites->setVisible(m_settings.m_start != PERTesterSettings::START_IMMEDIATELY); ui->satellitesLabel->setVisible(m_settings.m_start != PERTesterSettings::START_IMMEDIATELY); applySettings(); - arrangeRollups(); + getRollupContents()->arrangeRollups(); } void PERTesterGUI::on_satellites_editingFinished() @@ -353,3 +353,20 @@ void PERTesterGUI::applySettings(bool force) m_perTester->getInputMessageQueue()->push(message); } } + +void PERTesterGUI::makeUIConnections() +{ + QObject::connect(ui->startStop, &ButtonSwitch::toggled, this, &PERTesterGUI::on_startStop_toggled); + QObject::connect(ui->resetStats, &QToolButton::clicked, this, &PERTesterGUI::on_resetStats_clicked); + QObject::connect(ui->packetCount, qOverload(&QSpinBox::valueChanged), this, &PERTesterGUI::on_packetCount_valueChanged); + QObject::connect(ui->start, qOverload(&QComboBox::currentIndexChanged), this, &PERTesterGUI::on_start_currentIndexChanged); + QObject::connect(ui->satellites, &QLineEdit::editingFinished, this, &PERTesterGUI::on_satellites_editingFinished); + QObject::connect(ui->interval, qOverload(&QDoubleSpinBox::valueChanged), this, &PERTesterGUI::on_interval_valueChanged); + QObject::connect(ui->packet, &QPlainTextEdit::textChanged, this, &PERTesterGUI::on_packet_textChanged); + QObject::connect(ui->leading, qOverload(&QSpinBox::valueChanged), this, &PERTesterGUI::on_leading_valueChanged); + QObject::connect(ui->trailing, qOverload(&QSpinBox::valueChanged), this, &PERTesterGUI::on_trailing_valueChanged); + QObject::connect(ui->txUDPAddress, &QLineEdit::editingFinished, this, &PERTesterGUI::on_txUDPAddress_editingFinished); + QObject::connect(ui->txUDPPort, &QLineEdit::editingFinished, this, &PERTesterGUI::on_txUDPPort_editingFinished); + QObject::connect(ui->rxUDPAddress, &QLineEdit::editingFinished, this, &PERTesterGUI::on_rxUDPAddress_editingFinished); + QObject::connect(ui->rxUDPPort, &QLineEdit::editingFinished, this, &PERTesterGUI::on_rxUDPPort_editingFinished); +} diff --git a/plugins/feature/pertester/pertestergui.h b/plugins/feature/pertester/pertestergui.h index 26f26e585..b5fe9b0fa 100644 --- a/plugins/feature/pertester/pertestergui.h +++ b/plugins/feature/pertester/pertestergui.h @@ -66,6 +66,7 @@ private: void applySettings(bool force = false); void displaySettings(); bool handleMessage(const Message& message); + void makeUIConnections(); void leaveEvent(QEvent*); void enterEvent(QEvent*); diff --git a/plugins/feature/pertester/pertestergui.ui b/plugins/feature/pertester/pertestergui.ui index ff1e2d391..648ee13db 100644 --- a/plugins/feature/pertester/pertestergui.ui +++ b/plugins/feature/pertester/pertestergui.ui @@ -1,7 +1,7 @@ PERTesterGUI - + 0 @@ -554,17 +554,17 @@ Substitutions: - - RollupWidget - QWidget -
gui/rollupwidget.h
- 1 -
ButtonSwitch QToolButton
gui/buttonswitch.h
+ + RollupContents + QWidget +
gui/rollupcontents.h
+ 1 +
startStop diff --git a/plugins/feature/radiosonde/radiosondegui.cpp b/plugins/feature/radiosonde/radiosondegui.cpp index 871d02a27..af4416ecb 100644 --- a/plugins/feature/radiosonde/radiosondegui.cpp +++ b/plugins/feature/radiosonde/radiosondegui.cpp @@ -117,7 +117,7 @@ void RadiosondeGUI::onWidgetRolled(QWidget* widget, bool rollDown) (void) widget; (void) rollDown; - saveState(m_rollupState); + getRollupContents()->saveState(m_rollupState); applySettings(); } @@ -129,11 +129,11 @@ RadiosondeGUI::RadiosondeGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISet, F m_doApplySettings(true), m_lastFeatureState(0) { - ui->setupUi(this); + ui->setupUi(getRollupContents()); + getRollupContents()->arrangeRollups(); m_helpURL = "plugins/feature/radiosonde/readme.md"; setAttribute(Qt::WA_DeleteOnClose, true); - setChannelWidget(false); - connect(this, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + connect(getRollupContents(), SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); m_radiosonde = reinterpret_cast(feature); m_radiosonde->setMessageQueueToGUI(&m_inputMessageQueue); @@ -180,6 +180,7 @@ RadiosondeGUI::RadiosondeGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISet, F displaySettings(); applySettings(true); + makeUIConnections(); plotChart(); } @@ -199,6 +200,7 @@ void RadiosondeGUI::displaySettings() { setTitleColor(m_settings.m_rgbColor); setWindowTitle(m_settings.m_title); + setTitle(m_settings.m_title); blockApplySettings(true); // Order and size columns @@ -217,9 +219,9 @@ void RadiosondeGUI::displaySettings() ui->y1->setCurrentIndex((int)m_settings.m_y1); ui->y2->setCurrentIndex((int)m_settings.m_y2); - restoreState(m_rollupState); + getRollupContents()->restoreState(m_rollupState); blockApplySettings(false); - arrangeRollups(); + getRollupContents()->arrangeRollups(); } void RadiosondeGUI::leaveEvent(QEvent*) @@ -236,7 +238,6 @@ void RadiosondeGUI::onMenuDialogCalled(const QPoint &p) { BasicFeatureSettingsDialog dialog(this); dialog.setTitle(m_settings.m_title); - dialog.setColor(m_settings.m_rgbColor); dialog.setUseReverseAPI(m_settings.m_useReverseAPI); dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); @@ -246,7 +247,6 @@ void RadiosondeGUI::onMenuDialogCalled(const QPoint &p) dialog.move(p); dialog.exec(); - m_settings.m_rgbColor = dialog.getColor().rgb(); m_settings.m_title = dialog.getTitle(); m_settings.m_useReverseAPI = dialog.useReverseAPI(); m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); @@ -853,3 +853,12 @@ void RadiosondeGUI::on_deleteAll_clicked() m_radiosondes.remove(serial); } } + +void RadiosondeGUI::makeUIConnections() +{ + QObject::connect(ui->radiosondes, &QTableWidget::itemSelectionChanged, this, &RadiosondeGUI::on_radiosondes_itemSelectionChanged); + QObject::connect(ui->radiosondes, &QTableWidget::cellDoubleClicked, this, &RadiosondeGUI::on_radiosondes_cellDoubleClicked); + QObject::connect(ui->y1, qOverload(&QComboBox::currentIndexChanged), this, &RadiosondeGUI::on_y1_currentIndexChanged); + QObject::connect(ui->y2, qOverload(&QComboBox::currentIndexChanged), this, &RadiosondeGUI::on_y2_currentIndexChanged); + QObject::connect(ui->deleteAll, &QPushButton::clicked, this, &RadiosondeGUI::on_deleteAll_clicked); +} diff --git a/plugins/feature/radiosonde/radiosondegui.h b/plugins/feature/radiosonde/radiosondegui.h index 3a9b2883f..18202ab61 100644 --- a/plugins/feature/radiosonde/radiosondegui.h +++ b/plugins/feature/radiosonde/radiosondegui.h @@ -98,6 +98,7 @@ private: void applySettings(bool force = false); void displaySettings(); bool handleMessage(const Message& message); + void makeUIConnections(); void leaveEvent(QEvent*); void enterEvent(QEvent*); diff --git a/plugins/feature/radiosonde/radiosondegui.ui b/plugins/feature/radiosonde/radiosondegui.ui index 14a31362f..810a16bfa 100644 --- a/plugins/feature/radiosonde/radiosondegui.ui +++ b/plugins/feature/radiosonde/radiosondegui.ui @@ -1,7 +1,7 @@ RadiosondeGUI - + 0 @@ -426,9 +426,9 @@ - RollupWidget + RollupContents QWidget -
gui/rollupwidget.h
+
gui/rollupcontents.h
1
diff --git a/plugins/feature/rigctlserver/rigctlservergui.cpp b/plugins/feature/rigctlserver/rigctlservergui.cpp index da18a7698..be2b11a10 100644 --- a/plugins/feature/rigctlserver/rigctlservergui.cpp +++ b/plugins/feature/rigctlserver/rigctlservergui.cpp @@ -114,7 +114,7 @@ void RigCtlServerGUI::onWidgetRolled(QWidget* widget, bool rollDown) (void) widget; (void) rollDown; - saveState(m_rollupState); + getRollupContents()->saveState(m_rollupState); applySettings(); } @@ -126,11 +126,11 @@ RigCtlServerGUI::RigCtlServerGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISe m_doApplySettings(true), m_lastFeatureState(0) { - ui->setupUi(this); + ui->setupUi(getRollupContents()); + getRollupContents()->arrangeRollups(); m_helpURL = "plugins/feature/rigctlserver/readme.md"; setAttribute(Qt::WA_DeleteOnClose, true); - setChannelWidget(false); - connect(this, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + connect(getRollupContents(), SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); m_rigCtlServer = reinterpret_cast(feature); m_rigCtlServer->setMessageQueueToGUI(&m_inputMessageQueue); @@ -146,6 +146,7 @@ RigCtlServerGUI::RigCtlServerGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISe updateDeviceSetList(); displaySettings(); applySettings(true); + makeUIConnections(); } RigCtlServerGUI::~RigCtlServerGUI() @@ -162,10 +163,11 @@ void RigCtlServerGUI::displaySettings() { setTitleColor(m_settings.m_rgbColor); setWindowTitle(m_settings.m_title); + setTitle(m_settings.m_title); blockApplySettings(true); ui->rigCtrlPort->setValue(m_settings.m_rigCtlPort); ui->maxFrequencyOffset->setValue(m_settings.m_maxFrequencyOffset); - restoreState(m_rollupState); + getRollupContents()->restoreState(m_rollupState); blockApplySettings(false); } @@ -281,7 +283,6 @@ void RigCtlServerGUI::onMenuDialogCalled(const QPoint &p) { BasicFeatureSettingsDialog dialog(this); dialog.setTitle(m_settings.m_title); - dialog.setColor(m_settings.m_rgbColor); dialog.setUseReverseAPI(m_settings.m_useReverseAPI); dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); @@ -291,7 +292,6 @@ void RigCtlServerGUI::onMenuDialogCalled(const QPoint &p) dialog.move(p); dialog.exec(); - m_settings.m_rgbColor = dialog.getColor().rgb(); m_settings.m_title = dialog.getTitle(); m_settings.m_useReverseAPI = dialog.useReverseAPI(); m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); @@ -398,3 +398,14 @@ void RigCtlServerGUI::applySettings(bool force) m_rigCtlServer->getInputMessageQueue()->push(message); } } + +void RigCtlServerGUI::makeUIConnections() +{ + QObject::connect(ui->startStop, &ButtonSwitch::toggled, this, &RigCtlServerGUI::on_startStop_toggled); + QObject::connect(ui->enable, &QCheckBox::toggled, this, &RigCtlServerGUI::on_enable_toggled); + QObject::connect(ui->devicesRefresh, &QPushButton::clicked, this, &RigCtlServerGUI::on_devicesRefresh_clicked); + QObject::connect(ui->device, qOverload(&QComboBox::currentIndexChanged), this, &RigCtlServerGUI::on_device_currentIndexChanged); + QObject::connect(ui->channel, qOverload(&QComboBox::currentIndexChanged), this, &RigCtlServerGUI::on_channel_currentIndexChanged); + QObject::connect(ui->rigCtrlPort, qOverload(&QSpinBox::valueChanged), this, &RigCtlServerGUI::on_rigCtrlPort_valueChanged); + QObject::connect(ui->maxFrequencyOffset, qOverload(&QSpinBox::valueChanged), this, &RigCtlServerGUI::on_maxFrequencyOffset_valueChanged); +} diff --git a/plugins/feature/rigctlserver/rigctlservergui.h b/plugins/feature/rigctlserver/rigctlservergui.h index 1529ae2bf..679b61dd6 100644 --- a/plugins/feature/rigctlserver/rigctlservergui.h +++ b/plugins/feature/rigctlserver/rigctlservergui.h @@ -68,6 +68,7 @@ private: void updateDeviceSetList(); bool updateChannelList(); //!< true if channel index has changed bool handleMessage(const Message& message); + void makeUIConnections(); void leaveEvent(QEvent*); void enterEvent(QEvent*); diff --git a/plugins/feature/rigctlserver/rigctlservergui.ui b/plugins/feature/rigctlserver/rigctlservergui.ui index 3e3873dbd..6a5992d14 100644 --- a/plugins/feature/rigctlserver/rigctlservergui.ui +++ b/plugins/feature/rigctlserver/rigctlservergui.ui @@ -1,7 +1,7 @@ RigCtlServerGUI - + 0 @@ -263,17 +263,17 @@ Default is 10000. - - RollupWidget - QWidget -
gui/rollupwidget.h
- 1 -
ButtonSwitch QToolButton
gui/buttonswitch.h
+ + RollupContents + QWidget +
gui/rollupcontents.h
+ 1 +
diff --git a/plugins/feature/satellitetracker/satellitetrackergui.cpp b/plugins/feature/satellitetracker/satellitetrackergui.cpp index d3f983817..157de5899 100644 --- a/plugins/feature/satellitetracker/satellitetrackergui.cpp +++ b/plugins/feature/satellitetracker/satellitetrackergui.cpp @@ -230,7 +230,7 @@ void SatelliteTrackerGUI::onWidgetRolled(QWidget* widget, bool rollDown) (void) widget; (void) rollDown; - saveState(m_rollupState); + getRollupContents()->saveState(m_rollupState); applySettings(); } @@ -248,11 +248,11 @@ SatelliteTrackerGUI::SatelliteTrackerGUI(PluginAPI* pluginAPI, FeatureUISet *fea m_polarChart(nullptr), m_geostationarySatVisible(false) { - ui->setupUi(this); + ui->setupUi(getRollupContents()); + getRollupContents()->arrangeRollups(); m_helpURL = "plugins/feature/satellitetracker/readme.md"; setAttribute(Qt::WA_DeleteOnClose, true); - setChannelWidget(false); - connect(this, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + connect(getRollupContents(), SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); m_satelliteTracker = reinterpret_cast(feature); m_satelliteTracker->setMessageQueueToGUI(&m_inputMessageQueue); @@ -303,6 +303,7 @@ SatelliteTrackerGUI::SatelliteTrackerGUI(PluginAPI* pluginAPI, FeatureUISet *fea displaySettings(); applySettings(true); + makeUIConnections(); // Get initial list of satellites on_updateSatData_clicked(); @@ -322,6 +323,7 @@ void SatelliteTrackerGUI::displaySettings() { setTitleColor(m_settings.m_rgbColor); setWindowTitle(m_settings.m_title); + setTitle(m_settings.m_title); blockApplySettings(true); ui->latitude->setValue(m_settings.m_latitude); ui->longitude->setValue(m_settings.m_longitude); @@ -337,7 +339,7 @@ void SatelliteTrackerGUI::displaySettings() ui->dateTime->setDateTime(QDateTime::fromString(m_settings.m_dateTime, Qt::ISODateWithMs)); ui->autoTarget->setChecked(m_settings.m_autoTarget); ui->darkTheme->setChecked(m_settings.m_chartsDarkTheme); - restoreState(m_rollupState); + getRollupContents()->restoreState(m_rollupState); plotChart(); blockApplySettings(false); } @@ -356,7 +358,6 @@ void SatelliteTrackerGUI::onMenuDialogCalled(const QPoint &p) { BasicFeatureSettingsDialog dialog(this); dialog.setTitle(m_settings.m_title); - dialog.setColor(m_settings.m_rgbColor); dialog.setUseReverseAPI(m_settings.m_useReverseAPI); dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); @@ -366,7 +367,6 @@ void SatelliteTrackerGUI::onMenuDialogCalled(const QPoint &p) dialog.move(p); dialog.exec(); - m_settings.m_rgbColor = dialog.getColor().rgb(); m_settings.m_title = dialog.getTitle(); m_settings.m_useReverseAPI = dialog.useReverseAPI(); m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); @@ -1313,3 +1313,26 @@ void SatelliteTrackerGUI::on_deviceFeatureSelect_currentIndexChanged(int index) } applySettings(); } + +void SatelliteTrackerGUI::makeUIConnections() +{ + QObject::connect(ui->startStop, &ButtonSwitch::toggled, this, &SatelliteTrackerGUI::on_startStop_toggled); + QObject::connect(ui->useMyPosition, &QToolButton::clicked, this, &SatelliteTrackerGUI::on_useMyPosition_clicked); + QObject::connect(ui->latitude, qOverload(&QDoubleSpinBox::valueChanged), this, &SatelliteTrackerGUI::on_latitude_valueChanged); + QObject::connect(ui->longitude, qOverload(&QDoubleSpinBox::valueChanged), this, &SatelliteTrackerGUI::on_longitude_valueChanged); + QObject::connect(ui->target, &QComboBox::currentTextChanged, this, &SatelliteTrackerGUI::on_target_currentTextChanged); + QObject::connect(ui->displaySettings, &QToolButton::clicked, this, &SatelliteTrackerGUI::on_displaySettings_clicked); + QObject::connect(ui->radioControl, &QToolButton::clicked, this, &SatelliteTrackerGUI::on_radioControl_clicked); + QObject::connect(ui->dateTimeSelect, qOverload(&QComboBox::currentIndexChanged), this, &SatelliteTrackerGUI::on_dateTimeSelect_currentIndexChanged); + QObject::connect(ui->dateTime, &WrappingDateTimeEdit::dateTimeChanged, this, &SatelliteTrackerGUI::on_dateTime_dateTimeChanged); + QObject::connect(ui->viewOnMap, &QToolButton::clicked, this, &SatelliteTrackerGUI::on_viewOnMap_clicked); + QObject::connect(ui->updateSatData, &QToolButton::clicked, this, &SatelliteTrackerGUI::on_updateSatData_clicked); + QObject::connect(ui->selectSats, &QToolButton::clicked, this, &SatelliteTrackerGUI::on_selectSats_clicked); + QObject::connect(ui->autoTarget, &ButtonSwitch::clicked, this, &SatelliteTrackerGUI::on_autoTarget_clicked); + QObject::connect(ui->chartSelect, qOverload(&QComboBox::currentIndexChanged), this, &SatelliteTrackerGUI::on_chartSelect_currentIndexChanged); + QObject::connect(ui->nextPass, &QToolButton::clicked, this, &SatelliteTrackerGUI::on_nextPass_clicked); + QObject::connect(ui->prevPass, &QToolButton::clicked, this, &SatelliteTrackerGUI::on_prevPass_clicked); + QObject::connect(ui->darkTheme, &QToolButton::clicked, this, &SatelliteTrackerGUI::on_darkTheme_clicked); + QObject::connect(ui->satTable, &QTableWidget::cellDoubleClicked, this, &SatelliteTrackerGUI::on_satTable_cellDoubleClicked); + QObject::connect(ui->deviceFeatureSelect, qOverload(&QComboBox::currentIndexChanged), this, &SatelliteTrackerGUI::on_deviceFeatureSelect_currentIndexChanged); +} diff --git a/plugins/feature/satellitetracker/satellitetrackergui.h b/plugins/feature/satellitetracker/satellitetrackergui.h index f9c9939e9..2d192f48b 100644 --- a/plugins/feature/satellitetracker/satellitetrackergui.h +++ b/plugins/feature/satellitetracker/satellitetrackergui.h @@ -130,6 +130,7 @@ private: void updateDeviceFeatureCombo(const QStringList &items, const QString &selected); void updateFileInputList(); void updateMapList(); + void makeUIConnections(); void leaveEvent(QEvent*); void enterEvent(QEvent*); diff --git a/plugins/feature/satellitetracker/satellitetrackergui.ui b/plugins/feature/satellitetracker/satellitetrackergui.ui index f15e45f34..fb84bab4a 100644 --- a/plugins/feature/satellitetracker/satellitetrackergui.ui +++ b/plugins/feature/satellitetracker/satellitetrackergui.ui @@ -1,7 +1,7 @@ SatelliteTrackerGUI - + 0 @@ -716,17 +716,17 @@ - - RollupWidget - QWidget -
gui/rollupwidget.h
- 1 -
ButtonSwitch QToolButton
gui/buttonswitch.h
+ + RollupContents + QWidget +
gui/rollupcontents.h
+ 1 +
QChartView QGraphicsView diff --git a/plugins/feature/simpleptt/simplepttgui.cpp b/plugins/feature/simpleptt/simplepttgui.cpp index 695a5800d..9ec1e52d5 100644 --- a/plugins/feature/simpleptt/simplepttgui.cpp +++ b/plugins/feature/simpleptt/simplepttgui.cpp @@ -138,7 +138,7 @@ void SimplePTTGUI::onWidgetRolled(QWidget* widget, bool rollDown) (void) widget; (void) rollDown; - saveState(m_rollupState); + getRollupContents()->saveState(m_rollupState); applySettings(); } @@ -150,11 +150,11 @@ SimplePTTGUI::SimplePTTGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISet, Fea m_doApplySettings(true), m_lastFeatureState(0) { - ui->setupUi(this); + ui->setupUi(getRollupContents()); + getRollupContents()->arrangeRollups(); m_helpURL = "plugins/feature/simpleptt/readme.md"; setAttribute(Qt::WA_DeleteOnClose, true); - setChannelWidget(false); - connect(this, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + connect(getRollupContents(), SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); m_simplePTT = reinterpret_cast(feature); m_simplePTT->setMessageQueueToGUI(&m_inputMessageQueue); @@ -180,6 +180,7 @@ SimplePTTGUI::SimplePTTGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISet, Fea updateDeviceSetLists(); displaySettings(); applySettings(true); + makeUIConnections(); } SimplePTTGUI::~SimplePTTGUI() @@ -196,10 +197,11 @@ void SimplePTTGUI::displaySettings() { setTitleColor(m_settings.m_rgbColor); setWindowTitle(m_settings.m_title); + setTitle(m_settings.m_title); blockApplySettings(true); ui->rxtxDelay->setValue(m_settings.m_rx2TxDelayMs); ui->txrxDelay->setValue(m_settings.m_tx2RxDelayMs); - restoreState(m_rollupState); + getRollupContents()->restoreState(m_rollupState); ui->vox->setChecked(m_settings.m_vox); ui->voxEnable->setChecked(m_settings.m_voxEnable); ui->voxLevel->setValue(m_settings.m_voxLevel); @@ -301,7 +303,6 @@ void SimplePTTGUI::onMenuDialogCalled(const QPoint &p) { BasicFeatureSettingsDialog dialog(this); dialog.setTitle(m_settings.m_title); - dialog.setColor(m_settings.m_rgbColor); dialog.setUseReverseAPI(m_settings.m_useReverseAPI); dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); @@ -311,7 +312,6 @@ void SimplePTTGUI::onMenuDialogCalled(const QPoint &p) dialog.move(p); dialog.exec(); - m_settings.m_rgbColor = dialog.getColor().rgb(); m_settings.m_title = dialog.getTitle(); m_settings.m_useReverseAPI = dialog.useReverseAPI(); m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); @@ -479,3 +479,18 @@ void SimplePTTGUI::audioSelect() applySettings(); } } + +void SimplePTTGUI::makeUIConnections() +{ + QObject::connect(ui->startStop, &ButtonSwitch::toggled, this, &SimplePTTGUI::on_startStop_toggled); + QObject::connect(ui->devicesRefresh, &QPushButton::clicked, this, &SimplePTTGUI::on_devicesRefresh_clicked); + QObject::connect(ui->rxDevice, qOverload(&QComboBox::currentIndexChanged), this, &SimplePTTGUI::on_rxDevice_currentIndexChanged); + QObject::connect(ui->txDevice, qOverload(&QComboBox::currentIndexChanged), this, &SimplePTTGUI::on_txDevice_currentIndexChanged); + QObject::connect(ui->rxtxDelay, qOverload(&QSpinBox::valueChanged), this, &SimplePTTGUI::on_rxtxDelay_valueChanged); + QObject::connect(ui->txrxDelay, qOverload(&QSpinBox::valueChanged), this, &SimplePTTGUI::on_txrxDelay_valueChanged); + QObject::connect(ui->ptt, &ButtonSwitch::toggled, this, &SimplePTTGUI::on_ptt_toggled); + QObject::connect(ui->vox, &ButtonSwitch::toggled, this, &SimplePTTGUI::on_vox_toggled); + QObject::connect(ui->voxEnable, &QCheckBox::clicked, this, &SimplePTTGUI::on_voxEnable_clicked); + QObject::connect(ui->voxLevel, &QDial::valueChanged, this, &SimplePTTGUI::on_voxLevel_valueChanged); + QObject::connect(ui->voxHold, qOverload(&QSpinBox::valueChanged), this, &SimplePTTGUI::on_voxHold_valueChanged); +} diff --git a/plugins/feature/simpleptt/simplepttgui.h b/plugins/feature/simpleptt/simplepttgui.h index b2b73fb7e..3062bde6a 100644 --- a/plugins/feature/simpleptt/simplepttgui.h +++ b/plugins/feature/simpleptt/simplepttgui.h @@ -69,6 +69,7 @@ private: void displaySettings(); void updateDeviceSetLists(); bool handleMessage(const Message& message); + void makeUIConnections(); void leaveEvent(QEvent*); void enterEvent(QEvent*); diff --git a/plugins/feature/simpleptt/simplepttgui.ui b/plugins/feature/simpleptt/simplepttgui.ui index 619aaf38a..0293fff1a 100644 --- a/plugins/feature/simpleptt/simplepttgui.ui +++ b/plugins/feature/simpleptt/simplepttgui.ui @@ -1,7 +1,7 @@ SimplePTTGUI - + 0 @@ -448,17 +448,17 @@ - - RollupWidget - QWidget -
gui/rollupwidget.h
- 1 -
ButtonSwitch QToolButton
gui/buttonswitch.h
+ + RollupContents + QWidget +
gui/rollupcontents.h
+ 1 +
diff --git a/plugins/feature/startracker/startrackergui.cpp b/plugins/feature/startracker/startrackergui.cpp index 4fde5482c..fb85c5ed0 100644 --- a/plugins/feature/startracker/startrackergui.cpp +++ b/plugins/feature/startracker/startrackergui.cpp @@ -224,7 +224,7 @@ void StarTrackerGUI::onWidgetRolled(QWidget* widget, bool rollDown) (void) widget; (void) rollDown; - saveState(m_rollupState); + getRollupContents()->saveState(m_rollupState); applySettings(); } @@ -254,11 +254,11 @@ StarTrackerGUI::StarTrackerGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISet, m_moonRA(0.0), m_moonDec(0.0) { - ui->setupUi(this); + ui->setupUi(getRollupContents()); + getRollupContents()->arrangeRollups(); m_helpURL = "plugins/feature/startracker/readme.md"; setAttribute(Qt::WA_DeleteOnClose, true); - setChannelWidget(false); - connect(this, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + connect(getRollupContents(), SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); m_starTracker = reinterpret_cast(feature); m_starTracker->setMessageQueueToGUI(&m_inputMessageQueue); @@ -325,6 +325,7 @@ StarTrackerGUI::StarTrackerGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISet, ui->dateTime->setDateTime(QDateTime::currentDateTime()); displaySettings(); applySettings(true); + makeUIConnections(); // Populate subchart menu on_chartSelect_currentIndexChanged(0); @@ -392,6 +393,7 @@ void StarTrackerGUI::displaySettings() { setTitleColor(m_settings.m_rgbColor); setWindowTitle(m_settings.m_title); + setTitle(m_settings.m_title); blockApplySettings(true); ui->darkTheme->setChecked(m_settings.m_chartsDarkTheme); if (m_solarFluxChart) { @@ -445,7 +447,7 @@ void StarTrackerGUI::displaySettings() ui->frequency->setValue(m_settings.m_frequency/1000000.0); ui->beamwidth->setValue(m_settings.m_beamwidth); updateForTarget(); - restoreState(m_rollupState); + getRollupContents()->restoreState(m_rollupState); plotChart(); blockApplySettings(false); } @@ -464,7 +466,6 @@ void StarTrackerGUI::onMenuDialogCalled(const QPoint &p) { BasicFeatureSettingsDialog dialog(this); dialog.setTitle(m_settings.m_title); - dialog.setColor(m_settings.m_rgbColor); dialog.setUseReverseAPI(m_settings.m_useReverseAPI); dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); @@ -474,7 +475,6 @@ void StarTrackerGUI::onMenuDialogCalled(const QPoint &p) dialog.move(p); dialog.exec(); - m_settings.m_rgbColor = dialog.getColor().rgb(); m_settings.m_title = dialog.getTitle(); m_settings.m_useReverseAPI = dialog.useReverseAPI(); m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); @@ -2036,3 +2036,38 @@ void StarTrackerGUI::downloadFinished(const QString& filename, bool success) if (success) readSolarFlux(); } + +void StarTrackerGUI::makeUIConnections() +{ + QObject::connect(ui->startStop, &ButtonSwitch::toggled, this, &StarTrackerGUI::on_startStop_toggled); + QObject::connect(ui->link, &ButtonSwitch::clicked, this, &StarTrackerGUI::on_link_clicked); + QObject::connect(ui->useMyPosition, &QToolButton::clicked, this, &StarTrackerGUI::on_useMyPosition_clicked); + QObject::connect(ui->latitude, qOverload(&QDoubleSpinBox::valueChanged), this, &StarTrackerGUI::on_latitude_valueChanged); + QObject::connect(ui->longitude, qOverload(&QDoubleSpinBox::valueChanged), this, &StarTrackerGUI::on_longitude_valueChanged); + QObject::connect(ui->rightAscension, &QLineEdit::editingFinished, this, &StarTrackerGUI::on_rightAscension_editingFinished); + QObject::connect(ui->declination, &QLineEdit::editingFinished, this, &StarTrackerGUI::on_declination_editingFinished); + QObject::connect(ui->azimuth, &DMSSpinBox::valueChanged, this, &StarTrackerGUI::on_azimuth_valueChanged); + QObject::connect(ui->elevation, &DMSSpinBox::valueChanged, this, &StarTrackerGUI::on_elevation_valueChanged); + QObject::connect(ui->azimuthOffset, qOverload(&QDoubleSpinBox::valueChanged), this, &StarTrackerGUI::on_azimuthOffset_valueChanged); + QObject::connect(ui->elevationOffset, qOverload(&QDoubleSpinBox::valueChanged), this, &StarTrackerGUI::on_elevationOffset_valueChanged); + QObject::connect(ui->galacticLatitude, &DMSSpinBox::valueChanged, this, &StarTrackerGUI::on_galacticLatitude_valueChanged); + QObject::connect(ui->galacticLongitude, &DMSSpinBox::valueChanged, this, &StarTrackerGUI::on_galacticLongitude_valueChanged); + QObject::connect(ui->frequency, qOverload(&QSpinBox::valueChanged), this, &StarTrackerGUI::on_frequency_valueChanged); + QObject::connect(ui->beamwidth, qOverload(&QDoubleSpinBox::valueChanged), this, &StarTrackerGUI::on_beamwidth_valueChanged); + QObject::connect(ui->target, &QComboBox::currentTextChanged, this, &StarTrackerGUI::on_target_currentTextChanged); + QObject::connect(ui->displaySettings, &QToolButton::clicked, this, &StarTrackerGUI::on_displaySettings_clicked); + QObject::connect(ui->dateTimeSelect, &QComboBox::currentTextChanged, this, &StarTrackerGUI::on_dateTimeSelect_currentTextChanged); + QObject::connect(ui->dateTime, &WrappingDateTimeEdit::dateTimeChanged, this, &StarTrackerGUI::on_dateTime_dateTimeChanged); + QObject::connect(ui->viewOnMap, &QToolButton::clicked, this, &StarTrackerGUI::on_viewOnMap_clicked); + QObject::connect(ui->chartSelect, qOverload(&QComboBox::currentIndexChanged), this, &StarTrackerGUI::on_chartSelect_currentIndexChanged); + QObject::connect(ui->chartSubSelect, qOverload(&QComboBox::currentIndexChanged), this, &StarTrackerGUI::on_chartSubSelect_currentIndexChanged); + QObject::connect(ui->downloadSolarFlux, &QToolButton::clicked, this, &StarTrackerGUI::on_downloadSolarFlux_clicked); + QObject::connect(ui->darkTheme, &QToolButton::clicked, this, &StarTrackerGUI::on_darkTheme_clicked); + QObject::connect(ui->zoomIn, &QToolButton::clicked, this, &StarTrackerGUI::on_zoomIn_clicked); + QObject::connect(ui->zoomOut, &QToolButton::clicked, this, &StarTrackerGUI::on_zoomOut_clicked); + QObject::connect(ui->addAnimationFrame, &QToolButton::clicked, this, &StarTrackerGUI::on_addAnimationFrame_clicked); + QObject::connect(ui->clearAnimation, &QToolButton::clicked, this, &StarTrackerGUI::on_clearAnimation_clicked); + QObject::connect(ui->saveAnimation, &QToolButton::clicked, this, &StarTrackerGUI::on_saveAnimation_clicked); + QObject::connect(ui->drawSun, &QToolButton::clicked, this, &StarTrackerGUI::on_drawSun_clicked); + QObject::connect(ui->drawMoon, &QToolButton::clicked, this, &StarTrackerGUI::on_drawMoon_clicked); +} diff --git a/plugins/feature/startracker/startrackergui.h b/plugins/feature/startracker/startrackergui.h index b3e6b89d3..2d41484da 100644 --- a/plugins/feature/startracker/startrackergui.h +++ b/plugins/feature/startracker/startrackergui.h @@ -153,6 +153,7 @@ private: void raDecChanged(); void updateChartSubSelect(); void updateSolarFlux(bool all); + void makeUIConnections(); void leaveEvent(QEvent*); void enterEvent(QEvent*); diff --git a/plugins/feature/startracker/startrackergui.ui b/plugins/feature/startracker/startrackergui.ui index 11866fb24..3f42355e6 100644 --- a/plugins/feature/startracker/startrackergui.ui +++ b/plugins/feature/startracker/startrackergui.ui @@ -1,7 +1,7 @@ StarTrackerGUI - + 0 @@ -846,17 +846,17 @@ This can be specified as a decimal (E.g. 34.23) or in degrees, minutes and secon - - RollupWidget - QWidget -
gui/rollupwidget.h
- 1 -
ButtonSwitch QToolButton
gui/buttonswitch.h
+ + RollupContents + QWidget +
gui/rollupcontents.h
+ 1 +
QChartView QGraphicsView diff --git a/plugins/feature/vorlocalizer/vorlocalizergui.cpp b/plugins/feature/vorlocalizer/vorlocalizergui.cpp index ae89e4612..e27fc2332 100644 --- a/plugins/feature/vorlocalizer/vorlocalizergui.cpp +++ b/plugins/feature/vorlocalizer/vorlocalizergui.cpp @@ -1174,7 +1174,7 @@ void VORLocalizerGUI::onWidgetRolled(QWidget* widget, bool rollDown) (void) widget; (void) rollDown; - saveState(m_rollupState); + getRollupContents()->saveState(m_rollupState); applySettings(); } @@ -1184,7 +1184,6 @@ void VORLocalizerGUI::onMenuDialogCalled(const QPoint &p) { BasicFeatureSettingsDialog dialog(this); dialog.setTitle(m_settings.m_title); - dialog.setColor(m_settings.m_rgbColor); dialog.setUseReverseAPI(m_settings.m_useReverseAPI); dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); @@ -1194,7 +1193,6 @@ void VORLocalizerGUI::onMenuDialogCalled(const QPoint &p) dialog.move(p); dialog.exec(); - m_settings.m_rgbColor = dialog.getColor().rgb(); m_settings.m_title = dialog.getTitle(); m_settings.m_useReverseAPI = dialog.useReverseAPI(); m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); @@ -1225,7 +1223,8 @@ VORLocalizerGUI::VORLocalizerGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISe m_lastFeatureState(0), m_rrSecondsCount(0) { - ui->setupUi(this); + ui->setupUi(getRollupContents()); + getRollupContents()->arrangeRollups(); m_helpURL = "plugins/feature/vorlocalizer/readme.md"; ui->map->rootContext()->setContextProperty("vorModel", &m_vorModel); @@ -1235,7 +1234,7 @@ VORLocalizerGUI::VORLocalizerGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISe m_muteIcon.addPixmap(QPixmap("://sound_on.png"), QIcon::Normal, QIcon::Off); setAttribute(Qt::WA_DeleteOnClose, true); - connect(this, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + connect(getRollupContents(), SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &))); connect(&m_dlm, &HttpDownloadManager::downloadComplete, this, &VORLocalizerGUI::downloadFinished); @@ -1328,6 +1327,7 @@ VORLocalizerGUI::VORLocalizerGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISe displaySettings(); applySettings(true); + makeUIConnections(); } VORLocalizerGUI::~VORLocalizerGUI() @@ -1353,6 +1353,7 @@ void VORLocalizerGUI::displaySettings() { setTitleColor(m_settings.m_rgbColor); setWindowTitle(m_settings.m_title); + setTitle(m_settings.m_title); blockApplySettings(true); @@ -1378,7 +1379,7 @@ void VORLocalizerGUI::displaySettings() ui->centerShift->setValue(m_settings.m_centerShift/1000); ui->forceRRAveraging->setChecked(m_settings.m_forceRRAveraging); - restoreState(m_rollupState); + getRollupContents()->restoreState(m_rollupState); blockApplySettings(false); } @@ -1449,3 +1450,14 @@ void VORLocalizerGUI::tick() m_tickCount = 0; } } + +void VORLocalizerGUI::makeUIConnections() +{ + QObject::connect(ui->startStop, &ButtonSwitch::toggled, this, &VORLocalizerGUI::on_startStop_toggled); + QObject::connect(ui->getOurAirportsVORDB, &QPushButton::clicked, this, &VORLocalizerGUI::on_getOurAirportsVORDB_clicked); + QObject::connect(ui->getOpenAIPVORDB, &QPushButton::clicked, this, &VORLocalizerGUI::on_getOpenAIPVORDB_clicked); + QObject::connect(ui->magDecAdjust, &ButtonSwitch::toggled, this, &VORLocalizerGUI::on_magDecAdjust_toggled); + QObject::connect(ui->rrTime, &QDial::valueChanged, this, &VORLocalizerGUI::on_rrTime_valueChanged); + QObject::connect(ui->centerShift, &QDial::valueChanged, this, &VORLocalizerGUI::on_centerShift_valueChanged); + QObject::connect(ui->channelsRefresh, &QPushButton::clicked, this, &VORLocalizerGUI::on_channelsRefresh_clicked); +} diff --git a/plugins/feature/vorlocalizer/vorlocalizergui.h b/plugins/feature/vorlocalizer/vorlocalizergui.h index 6a0092879..b2d1f34f0 100644 --- a/plugins/feature/vorlocalizer/vorlocalizergui.h +++ b/plugins/feature/vorlocalizer/vorlocalizergui.h @@ -252,6 +252,7 @@ private: void applySettings(bool force = false); void displaySettings(); bool handleMessage(const Message& message); + void makeUIConnections(); void leaveEvent(QEvent*); void enterEvent(QEvent*); diff --git a/plugins/feature/vorlocalizer/vorlocalizergui.ui b/plugins/feature/vorlocalizer/vorlocalizergui.ui index 256d519fd..2824b96e4 100644 --- a/plugins/feature/vorlocalizer/vorlocalizergui.ui +++ b/plugins/feature/vorlocalizer/vorlocalizergui.ui @@ -1,7 +1,7 @@ VORLocalizerGUI - + 0 @@ -200,6 +200,7 @@ + Ubuntu 8 @@ -542,17 +543,17 @@ QToolTip{background-color: white; color: black;} QWidget
QtQuickWidgets/QQuickWidget
- - RollupWidget - QWidget -
gui/rollupwidget.h
- 1 -
ButtonSwitch QToolButton
gui/buttonswitch.h
+ + RollupContents + QWidget +
gui/rollupcontents.h
+ 1 +
getOurAirportsVORDB diff --git a/sdrbase/pipes/messagepipeslegacy.cpp b/sdrbase/pipes/messagepipeslegacy.cpp new file mode 100644 index 000000000..6d9355bba --- /dev/null +++ b/sdrbase/pipes/messagepipeslegacy.cpp @@ -0,0 +1,78 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2020 Edouard Griffiths, F4EXB // +// // +// This program 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 as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program 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 V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#include + +#include "util/messagequeue.h" + +#include "messagepipeslegacygcworker.h" +#include "messagepipeslegacy.h" +#include "pipeendpoint.h" + +MessagePipesLegacy::MessagePipesLegacy() +{ + m_gcWorker = new MessagePipesLegacyGCWorker(); + m_gcWorker->setC2FRegistrations( + m_registrations.getMutex(), + m_registrations.getElements(), + m_registrations.getConsumers() + ); + m_gcWorker->moveToThread(&m_gcThread); + startGC(); +} + +MessagePipesLegacy::~MessagePipesLegacy() +{ + if (m_gcWorker->isRunning()) { + stopGC(); + } +} + +MessageQueue *MessagePipesLegacy::registerChannelToFeature(const PipeEndPoint *source, PipeEndPoint *dest, const QString& type) +{ + qDebug("MessagePipesLegacy::registerChannelToFeature: %p %p %s", source, dest, qPrintable(type)); + return m_registrations.registerProducerToConsumer(source, dest, type); +} + +MessageQueue *MessagePipesLegacy::unregisterChannelToFeature(const PipeEndPoint *source, PipeEndPoint *dest, const QString& type) +{ + qDebug("MessagePipesLegacy::unregisterChannelToFeature: %p %p %s", source, dest, qPrintable(type)); + MessageQueue *messageQueue = m_registrations.unregisterProducerToConsumer(source, dest, type); + m_gcWorker->addMessageQueueToDelete(messageQueue); + return messageQueue; +} + +QList* MessagePipesLegacy::getMessageQueues(const PipeEndPoint *source, const QString& type) +{ + //qDebug("MessagePipesLegacy::getMessageQueues: %p %s", source, qPrintable(type)); + return m_registrations.getElements(source, type); +} + +void MessagePipesLegacy::startGC() +{ + qDebug("MessagePipesLegacy::startGC"); + m_gcWorker->startWork(); + m_gcThread.start(); +} + +void MessagePipesLegacy::stopGC() +{ + qDebug("MessagePipesLegacy::stopGC"); + m_gcWorker->stopWork(); + m_gcThread.quit(); + m_gcThread.wait(); +} diff --git a/sdrbase/pipes/messagepipeslegacy.h b/sdrbase/pipes/messagepipeslegacy.h new file mode 100644 index 000000000..562e582a8 --- /dev/null +++ b/sdrbase/pipes/messagepipeslegacy.h @@ -0,0 +1,59 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2020 Edouard Griffiths, F4EXB // +// // +// This program 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 as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program 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 V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef SDRBASE_PIPES_MESSAGEPIPESLEGACY_H_ +#define SDRBASE_PIPES_MESSAGEPIPESLEGACY_H_ + +#include +#include +#include +#include +#include + +#include "export.h" + +#include "messagepipeslegacycommon.h" +#include "elementpipesregistrations.h" + +class PipeEndPoint; +class MessagePipesLegacyGCWorker; +class MessageQueue; + +class SDRBASE_API MessagePipesLegacy : public QObject +{ + Q_OBJECT +public: + MessagePipesLegacy(); + MessagePipesLegacy(const MessagePipesLegacy&) = delete; + MessagePipesLegacy& operator=(const MessagePipesLegacy&) = delete; + ~MessagePipesLegacy(); + + // FIXME: Names of these functions should probably change, as we now support channel or feature at either end + MessageQueue *registerChannelToFeature(const PipeEndPoint *source, PipeEndPoint *dest, const QString& type); + MessageQueue *unregisterChannelToFeature(const PipeEndPoint *source, PipeEndPoint *dest, const QString& type); + QList* getMessageQueues(const PipeEndPoint *source, const QString& type); + +private: + ElementPipesRegistrations m_registrations; + QThread m_gcThread; //!< Garbage collector thread + MessagePipesLegacyGCWorker *m_gcWorker; //!< Garbage collector + + void startGC(); //!< Start garbage collector + void stopGC(); //!< Stop garbage collector +}; + +#endif // SDRBASE_PIPES_MESSAGEPIPESLEGACY_H_ diff --git a/sdrbase/pipes/messagepipeslegacycommon.cpp b/sdrbase/pipes/messagepipeslegacycommon.cpp new file mode 100644 index 000000000..984a452d5 --- /dev/null +++ b/sdrbase/pipes/messagepipeslegacycommon.cpp @@ -0,0 +1,20 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2020 Edouard Griffiths, F4EXB // +// // +// This program 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 as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program 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 V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#include "messagepipeslegacycommon.h" + +MESSAGE_CLASS_DEFINITION(MessagePipesLegacyCommon::MsgReportChannelDeleted, Message) diff --git a/sdrbase/pipes/messagepipeslegacycommon.h b/sdrbase/pipes/messagepipeslegacycommon.h new file mode 100644 index 000000000..42f3b9600 --- /dev/null +++ b/sdrbase/pipes/messagepipeslegacycommon.h @@ -0,0 +1,61 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2020 Edouard Griffiths, F4EXB // +// // +// This program 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 as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program 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 V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef SDRBASE_PIPES_MESSAGEPIPESLEGACYCOMON_H_ +#define SDRBASE_PIPES_MESSAGEPIPESLEGACYCOMON_H_ + +#include +#include +#include + +#include "export.h" +#include "util/message.h" +#include "elementpipescommon.h" + +class PipeEndPoint; +class MessageQueue; + +class SDRBASE_API MessagePipesLegacyCommon +{ +public: + typedef ElementPipesCommon::RegistrationKey ChannelRegistrationKey; + + /** Send this message to stakeholders when the garbage collector finds that a channel was deleted */ + class SDRBASE_API MsgReportChannelDeleted : public Message { + MESSAGE_CLASS_DECLARATION + + public: + const MessageQueue *getMessageQueue() const { return m_messageQueue; } + const ChannelRegistrationKey& getChannelRegistrationKey() const { return m_channelRegistrationKey; } + + static MsgReportChannelDeleted* create(const MessageQueue *messageQueue, const ChannelRegistrationKey& channelRegistrationKey) { + return new MsgReportChannelDeleted(messageQueue, channelRegistrationKey); + } + + private: + const MessageQueue *m_messageQueue; + ChannelRegistrationKey m_channelRegistrationKey; + + MsgReportChannelDeleted(const MessageQueue *messageQueue, const ChannelRegistrationKey& channelRegistrationKey) : + Message(), + m_messageQueue(messageQueue), + m_channelRegistrationKey(channelRegistrationKey) + { } + }; +}; + +#endif // SDRBASE_PIPES_MESSAGEPIPESLEGACYCOMON_H_ diff --git a/sdrbase/pipes/messagepipeslegacygcworker.cpp b/sdrbase/pipes/messagepipeslegacygcworker.cpp new file mode 100644 index 000000000..f05c65bc3 --- /dev/null +++ b/sdrbase/pipes/messagepipeslegacygcworker.cpp @@ -0,0 +1,78 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2020 Edouard Griffiths, F4EXB // +// // +// This program 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 as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program 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 V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#include "channel/channelapi.h" +#include "feature/feature.h" +#include "util/messagequeue.h" +#include "maincore.h" +#include "messagepipeslegacycommon.h" +#include "messagepipeslegacygcworker.h" + +bool MessagePipesLegacyGCWorker::MessagePipesGC::existsProducer(const PipeEndPoint *pipeEndPoint) +{ + return MainCore::instance()->existsChannel((const ChannelAPI *)pipeEndPoint) + || MainCore::instance()->existsFeature((const Feature *)pipeEndPoint); +} + +bool MessagePipesLegacyGCWorker::MessagePipesGC::existsConsumer(const PipeEndPoint *pipeEndPoint) +{ + return MainCore::instance()->existsChannel((const ChannelAPI *)pipeEndPoint) + || MainCore::instance()->existsFeature((const Feature *)pipeEndPoint); +} + +void MessagePipesLegacyGCWorker::MessagePipesGC::sendMessageToConsumer( + const MessageQueue *, + MessagePipesLegacyCommon::ChannelRegistrationKey, + PipeEndPoint *) +{ +} + +MessagePipesLegacyGCWorker::MessagePipesLegacyGCWorker() : + m_running(false) +{} + +MessagePipesLegacyGCWorker::~MessagePipesLegacyGCWorker() +{} + +void MessagePipesLegacyGCWorker::startWork() +{ + connect(&m_gcTimer, SIGNAL(timeout()), this, SLOT(processGC())); + m_gcTimer.start(10000); // collect garbage every 10s + m_running = true; +} + +void MessagePipesLegacyGCWorker::stopWork() +{ + m_running = false; + m_gcTimer.stop(); + disconnect(&m_gcTimer, SIGNAL(timeout()), this, SLOT(processGC())); +} + +void MessagePipesLegacyGCWorker::addMessageQueueToDelete(MessageQueue *messageQueue) +{ + if (messageQueue) + { + m_gcTimer.start(10000); // restart GC to make sure deletion is postponed + m_messagePipesGC.addElementToDelete(messageQueue); + } +} + +void MessagePipesLegacyGCWorker::processGC() +{ + // qDebug("MessagePipesLegacyGCWorker::processGC"); + m_messagePipesGC.processGC(); +} diff --git a/sdrbase/pipes/messagepipeslegacygcworker.h b/sdrbase/pipes/messagepipeslegacygcworker.h new file mode 100644 index 000000000..309b13125 --- /dev/null +++ b/sdrbase/pipes/messagepipeslegacygcworker.h @@ -0,0 +1,69 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2020 Edouard Griffiths, F4EXB // +// // +// This program 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 as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program 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 V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef SDRBASE_PIPES_MESSAGEPIPESLEGACYGCWORKER_H_ +#define SDRBASE_PIPES_MESSAGEPIPESLEGACYGCWORKER_H_ + +#include +#include + +#include "export.h" + +#include "messagepipeslegacycommon.h" +#include "elementpipesgc.h" + +class QMutex; + +class SDRBASE_API MessagePipesLegacyGCWorker : public QObject +{ + Q_OBJECT +public: + MessagePipesLegacyGCWorker(); + ~MessagePipesLegacyGCWorker(); + + void setC2FRegistrations( + QMutex *c2fMutex, + QMap> *c2fQueues, + QMap> *c2fPipeEndPoints + ) + { + m_messagePipesGC.setRegistrations(c2fMutex, c2fQueues, c2fPipeEndPoints); + } + + void startWork(); + void stopWork(); + void addMessageQueueToDelete(MessageQueue *messageQueue); + bool isRunning() const { return m_running; } + +private: + class MessagePipesGC : public ElementPipesGC + { + private: + virtual bool existsProducer(const PipeEndPoint *pipeEndPoint); + virtual bool existsConsumer(const PipeEndPoint *pipeEndPoint); + virtual void sendMessageToConsumer(const MessageQueue *messageQueue, MessagePipesLegacyCommon::ChannelRegistrationKey key, PipeEndPoint *pipeEndPoint); + }; + + MessagePipesGC m_messagePipesGC; + bool m_running; + QTimer m_gcTimer; + +private slots: + void processGC(); //!< Collect garbage +}; + +#endif // SDRBASE_PIPES_MESSAGEPIPESLEGACYGCWORKER_H_ diff --git a/sdrbase/pipes/pipeendpoint.cpp b/sdrbase/pipes/pipeendpoint.cpp new file mode 100644 index 000000000..9a3ba38eb --- /dev/null +++ b/sdrbase/pipes/pipeendpoint.cpp @@ -0,0 +1,185 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2021 Jon Beniston, M7RCE // +// // +// This program 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 as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program 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 V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#include + +#include +#include + +#include "dsp/dspengine.h" +#include "device/deviceset.h" +#include "channel/channelapi.h" +#include "feature/featureset.h" +#include "feature/feature.h" +#include "maincore.h" + +#include "pipeendpoint.h" + +MESSAGE_CLASS_DEFINITION(PipeEndPoint::MsgReportPipes, Message) + +QList PipeEndPoint::updateAvailablePipeSources(QString pipeName, QStringList pipeTypes, QStringList pipeURIs, PipeEndPoint *destination) +{ + MainCore *mainCore = MainCore::instance(); + MessagePipesLegacy& messagePipes = mainCore->getMessagePipesLegacy(); + std::vector& deviceSets = mainCore->getDeviceSets(); + QHash availablePipes; + + // Source is a channel + int deviceIndex = 0; + for (std::vector::const_iterator it = deviceSets.begin(); it != deviceSets.end(); ++it, deviceIndex++) + { + DSPDeviceSourceEngine *deviceSourceEngine = (*it)->m_deviceSourceEngine; + DSPDeviceSinkEngine *deviceSinkEngine = (*it)->m_deviceSinkEngine; + + if (deviceSourceEngine || deviceSinkEngine) + { + for (int chi = 0; chi < (*it)->getNumberOfChannels(); chi++) + { + ChannelAPI *channel = (*it)->getChannelAt(chi); + int i = pipeURIs.indexOf(channel->getURI()); + + if (i >= 0) + { + if (!availablePipes.contains(channel)) + { + MessageQueue *messageQueue = messagePipes.registerChannelToFeature(channel, destination, pipeName); + if (MainCore::instance()->existsFeature((const Feature *)destination)) + { + // Destination is feature + Feature *featureDest = (Feature *)destination; + QObject::connect( + messageQueue, + &MessageQueue::messageEnqueued, + featureDest, + [=](){ featureDest->handlePipeMessageQueue(messageQueue); }, + Qt::QueuedConnection + ); + } + else + { + // Destination is a channel + // Can't use Qt::QueuedConnection because ChannelAPI isn't a QObject + ChannelAPI *channelDest = (ChannelAPI *)destination; + QObject::connect( + messageQueue, + &MessageQueue::messageEnqueued, + [=](){ channelDest->handlePipeMessageQueue(messageQueue); } + ); + } + } + + AvailablePipeSource availablePipe = + AvailablePipeSource{ + deviceSinkEngine != nullptr ? AvailablePipeSource::TX : AvailablePipeSource::RX, + deviceIndex, + chi, + channel, + pipeTypes.at(i) + }; + availablePipes[channel] = availablePipe; + } + } + } + } + + // Source is a feature + std::vector& featureSets = mainCore->getFeatureeSets(); + int featureIndex = 0; + for (std::vector::const_iterator it = featureSets.begin(); it != featureSets.end(); ++it, featureIndex++) + { + for (int fi = 0; fi < (*it)->getNumberOfFeatures(); fi++) + { + Feature *feature = (*it)->getFeatureAt(fi); + int i = pipeURIs.indexOf(feature->getURI()); + + if (i >= 0) + { + if (!availablePipes.contains(feature)) + { + MessageQueue *messageQueue = messagePipes.registerChannelToFeature(feature, destination, pipeName); + if (MainCore::instance()->existsFeature((const Feature *)destination)) + { + // Destination is feature + Feature *featureDest = (Feature *)destination; + QObject::connect( + messageQueue, + &MessageQueue::messageEnqueued, + featureDest, + [=](){ featureDest->handlePipeMessageQueue(messageQueue); }, + Qt::QueuedConnection + ); + } + else + { + // Destination is a channel + // Can't use Qt::QueuedConnection because ChannelAPI isn't a QObject + ChannelAPI *channelDest = (ChannelAPI *)destination; + QObject::connect( + messageQueue, + &MessageQueue::messageEnqueued, + [=](){ channelDest->handlePipeMessageQueue(messageQueue); } + ); + } + } + + AvailablePipeSource availablePipe = + AvailablePipeSource{ + AvailablePipeSource::Feature, + featureIndex, + fi, + feature, + pipeTypes.at(i) + }; + availablePipes[feature] = availablePipe; + } + } + } + + QList availablePipeList; + QHash::iterator it = availablePipes.begin(); + + for (; it != availablePipes.end(); ++it) { + availablePipeList.push_back(*it); + } + return availablePipeList; +} + +PipeEndPoint *PipeEndPoint::getPipeEndPoint(const QString name, const QList &availablePipeSources) +{ + QRegExp re("([TRF])([0-9]+):([0-9]+) ([a-zA-Z0-9]+)"); + if (re.exactMatch(name)) + { + QString type = re.capturedTexts()[1]; + int setIndex = re.capturedTexts()[2].toInt(); + int index = re.capturedTexts()[3].toInt(); + QString id = re.capturedTexts()[4]; + + QListIterator itr(availablePipeSources); + while (itr.hasNext()) + { + AvailablePipeSource p = itr.next(); + if ((p.m_setIndex == setIndex) && (p.m_index == index) && (id == p.m_id)) { + return p.m_source; + } + } + } + else + { + qDebug() << "PipeEndPoint::getPipeEndPoint: " << name << " is malformed"; + } + return nullptr; +} diff --git a/sdrbase/pipes/pipeendpoint.h b/sdrbase/pipes/pipeendpoint.h new file mode 100644 index 000000000..5de230b09 --- /dev/null +++ b/sdrbase/pipes/pipeendpoint.h @@ -0,0 +1,100 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2021 Jon Beniston, M7RCE // +// // +// Parent for ChannelAPI and Features, where either can be used. // +// // +// This program 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 as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program 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 V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef SDRBASE_PIPES_PIPEENDPOINT_H_ +#define SDRBASE_PIPES_PIPEENDPOINT_H_ + +#include +#include +#include + +#include "util/message.h" +#include "export.h" + +class Feature; + +class SDRBASE_API PipeEndPoint { +public: + + // Used by pipe sinks (channels or features) to record details about available pipe sources (channels or features) + struct AvailablePipeSource + { + enum {RX, TX, Feature} m_type; + int m_setIndex; + int m_index; + PipeEndPoint *m_source; + QString m_id; + + AvailablePipeSource() = default; + AvailablePipeSource(const AvailablePipeSource&) = default; + AvailablePipeSource& operator=(const AvailablePipeSource&) = default; + friend bool operator==(const AvailablePipeSource &lhs, const AvailablePipeSource &rhs) + { + return (lhs.m_type == rhs.m_type) + && (lhs.m_setIndex == rhs.m_setIndex) + && (lhs.m_source == rhs.m_source) + && (lhs.m_id == rhs.m_id); + } + + QString getTypeName() const + { + QStringList typeNames = {"R", "T", "F"}; + return typeNames[m_type]; + } + + // Name for use in GUI combo boxes and WebAPI + QString getName() const + { + QString type; + + return QString("%1%2:%3 %4").arg(getTypeName()) + .arg(m_setIndex) + .arg(m_index) + .arg(m_id); + } + }; + + class SDRBASE_API MsgReportPipes : public Message { + MESSAGE_CLASS_DECLARATION + + public: + QList& getAvailablePipes() { return m_availablePipes; } + + static MsgReportPipes* create() { + return new MsgReportPipes(); + } + + private: + QList m_availablePipes; + + MsgReportPipes() : + Message() + {} + }; + + +protected: + + // Utility functions for pipe sinks to manage list of sources + QList updateAvailablePipeSources(QString pipeName, QStringList pipeTypes, QStringList pipeURIs, PipeEndPoint *destination); + PipeEndPoint *getPipeEndPoint(const QString name, const QList &availablePipeSources); + +}; + +#endif // SDRBASE_PIPES_PIPEENDPOINT_H_ diff --git a/sdrgui/CMakeLists.txt b/sdrgui/CMakeLists.txt index 3013ac2aa..2b8a85c4a 100644 --- a/sdrgui/CMakeLists.txt +++ b/sdrgui/CMakeLists.txt @@ -57,6 +57,7 @@ set(sdrgui_SOURCES gui/mypositiondialog.cpp gui/pluginsdialog.cpp gui/presetitem.cpp + gui/rollupcontents.cpp gui/rollupwidget.cpp gui/samplingdevicedialog.cpp gui/samplingdevicesdock.cpp @@ -73,6 +74,8 @@ set(sdrgui_SOURCES gui/tvscreenanalog.cpp gui/valuedial.cpp gui/valuedialz.cpp + gui/workspace.cpp + gui/workspaceselectiondialog.cpp gui/wsspectrumsettingsdialog.cpp gui/wrappingdatetimeedit.cpp @@ -152,6 +155,7 @@ set(sdrgui_HEADERS gui/physicalunit.h gui/pluginsdialog.h gui/presetitem.h + gui/rollupcontents.h gui/rollupwidget.h gui/samplingdevicedialog.h gui/samplingdevicesdock.h @@ -168,6 +172,8 @@ set(sdrgui_HEADERS gui/tvscreenanalog.h gui/valuedial.h gui/valuedialz.h + gui/workspace.h + gui/workspaceselectiondialog.h gui/wsspectrumsettingsdialog.h gui/wrappingdatetimeedit.h @@ -224,6 +230,7 @@ set(sdrgui_FORMS gui/myposdialog.ui gui/transverterdialog.ui gui/loggingdialog.ui + gui/workspaceselectiondialog.ui gui/wsspectrumsettingsdialog.ui soapygui/discreterangegui.ui soapygui/intervalrangegui.ui diff --git a/sdrgui/feature/featuregui.cpp b/sdrgui/feature/featuregui.cpp index df0790251..18128e2c1 100644 --- a/sdrgui/feature/featuregui.cpp +++ b/sdrgui/feature/featuregui.cpp @@ -16,12 +16,216 @@ /////////////////////////////////////////////////////////////////////////////////// #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "mainwindow.h" +#include "gui/workspaceselectiondialog.h" #include "featuregui.h" +FeatureGUI::FeatureGUI(QWidget *parent) : + QMdiSubWindow(parent), + m_featureIndex(0), + m_workspaceIndex(0), + m_contextMenuType(ContextMenuNone), + m_drag(false) +{ + qDebug("FeatureGUI::FeatureGUI"); + setWindowFlags(windowFlags() | Qt::FramelessWindowHint); + + m_indexLabel = new QLabel(); + m_indexLabel->setFixedSize(40, 16); + m_indexLabel->setStyleSheet("QLabel { background-color: rgb(128, 128, 128); qproperty-alignment: AlignCenter; }"); + m_indexLabel->setText(tr("F:%1").arg(m_featureIndex)); + m_indexLabel->setToolTip("Feature index"); + + m_settingsButton = new QPushButton(); + QIcon settingsIcon(":/gear.png"); + m_settingsButton->setIcon(settingsIcon); + m_settingsButton->setToolTip("Common settings"); + + m_titleLabel = new QLabel(); + m_titleLabel->setText("Feature"); + m_titleLabel->setToolTip("Feature name"); + m_titleLabel->setFixedHeight(20); + + m_helpButton = new QPushButton(); + m_helpButton->setFixedSize(20, 20); + QIcon helpIcon(":/help.png"); + m_helpButton->setIcon(helpIcon); + m_helpButton->setToolTip("Show feature documentation in browser"); + + m_moveButton = new QPushButton(); + m_moveButton->setFixedSize(20, 20); + QIcon moveIcon(":/exit.png"); + m_moveButton->setIcon(moveIcon); + m_moveButton->setToolTip("Move to workspace"); + + m_shrinkButton = new QPushButton(); + m_shrinkButton->setFixedSize(20, 20); + QIcon shrinkIcon(":/shrink.png"); + m_shrinkButton->setIcon(shrinkIcon); + m_shrinkButton->setToolTip("Adjust window to minimum size"); + + m_closeButton = new QPushButton(); + m_closeButton->setFixedSize(20, 20); + QIcon closeIcon(":/cross.png"); + m_closeButton->setIcon(closeIcon); + m_closeButton->setToolTip("Close feature"); + + m_statusLabel = new QLabel(); + m_statusLabel->setText("OK"); // for future use + m_statusLabel->setFixedHeight(20); + m_statusLabel->setToolTip("Feature status"); + + m_layouts = new QVBoxLayout(); + m_layouts->setContentsMargins(0, 4, 0, 4); + m_layouts->setSpacing(2); + + m_topLayout = new QHBoxLayout(); + m_topLayout->setContentsMargins(0, 0, 0, 0); + m_topLayout->addWidget(m_indexLabel); + m_topLayout->addWidget(m_settingsButton); + m_topLayout->addWidget(m_titleLabel); + m_topLayout->addStretch(1); + m_topLayout->addWidget(m_helpButton); + m_topLayout->addWidget(m_moveButton); + m_topLayout->addWidget(m_shrinkButton); + m_topLayout->addWidget(m_closeButton); + m_sizeGripTopRight = new QSizeGrip(this); + m_sizeGripTopRight->setStyleSheet("QSizeGrip { background-color: rgb(128, 128, 128); width: 10px; height: 10px; }"); + m_topLayout->addWidget(m_sizeGripTopRight, 0, Qt::AlignTop | Qt::AlignRight); + + m_centerLayout = new QHBoxLayout(); + m_centerLayout->addWidget(&m_rollupContents); + + m_bottomLayout = new QHBoxLayout(); + m_bottomLayout->setContentsMargins(0, 0, 0, 0); + m_bottomLayout->addWidget(m_statusLabel); + m_sizeGripBottomRight = new QSizeGrip(this); + m_sizeGripBottomRight->setStyleSheet("QSizeGrip { background-color: rgb(128, 128, 128); width: 10px; height: 10px; }"); + m_bottomLayout->addWidget(m_sizeGripBottomRight, 0, Qt::AlignBottom | Qt::AlignRight); + + m_layouts->addLayout(m_topLayout); + m_layouts->addLayout(m_centerLayout); + m_layouts->addLayout(m_bottomLayout); + + QObjectCleanupHandler().add(layout()); + setLayout(m_layouts); + + connect(m_settingsButton, SIGNAL(clicked()), this, SLOT(activateSettingsDialog())); + connect(m_helpButton, SIGNAL(clicked()), this, SLOT(showHelp())); + connect(m_moveButton, SIGNAL(clicked()), this, SLOT(openMoveToWorkspaceDialog())); + connect(m_shrinkButton, SIGNAL(clicked()), this, SLOT(shrinkWindow())); + connect(this, SIGNAL(forceShrink()), this, SLOT(shrinkWindow())); + connect(m_closeButton, SIGNAL(clicked()), this, SLOT(close())); +} + +FeatureGUI::~FeatureGUI() +{ + delete m_sizeGripBottomRight; + delete m_bottomLayout; + delete m_centerLayout; + delete m_sizeGripTopRight; + delete m_topLayout; + delete m_layouts; + delete m_statusLabel; + delete m_closeButton; + delete m_shrinkButton; + delete m_moveButton; + delete m_helpButton; + delete m_titleLabel; + delete m_settingsButton; + delete m_indexLabel; +} + void FeatureGUI::closeEvent(QCloseEvent *event) { qDebug("FeatureGUI::closeEvent"); emit closing(); event->accept(); } + +void FeatureGUI::mousePressEvent(QMouseEvent* event) +{ + if ((event->button() == Qt::LeftButton) && isOnMovingPad()) + { + m_drag = true; + m_DragPosition = event->globalPos() - pos(); + event->accept(); + } +} + +void FeatureGUI::mouseMoveEvent(QMouseEvent* event) +{ + if ((event->buttons() & Qt::LeftButton) && isOnMovingPad()) + { + move(event->globalPos() - m_DragPosition); + event->accept(); + } +} + +void FeatureGUI::activateSettingsDialog() +{ + QPoint p = mapFromGlobal(QCursor::pos()); + m_contextMenuType = ContextMenuChannelSettings; + emit customContextMenuRequested(p); +} + +void FeatureGUI::showHelp() +{ + if (m_helpURL.isEmpty()) { + return; + } + + QString url; + + if (m_helpURL.startsWith("http")) { + url = m_helpURL; + } else { + url = QString("https://github.com/f4exb/sdrangel/blob/master/%1").arg(m_helpURL); // Something like "plugins/channelrx/chanalyzer/readme.md" + } + + QDesktopServices::openUrl(QUrl(url)); +} + +void FeatureGUI::openMoveToWorkspaceDialog() +{ + int numberOfWorkspaces = MainWindow::getInstance()->getNumberOfWorkspaces(); + WorkspaceSelectionDialog dialog(numberOfWorkspaces, this); + dialog.exec(); + + if (dialog.hasChanged()) { + emit moveToWorkspace(dialog.getSelectedIndex()); + } +} + +void FeatureGUI::shrinkWindow() +{ + qDebug("FeatureGUI::shrinkWindow"); + adjustSize(); +} + +void FeatureGUI::setTitle(const QString& title) +{ + m_titleLabel->setText(title); +} + +bool FeatureGUI::isOnMovingPad() +{ + return m_indexLabel->underMouse() || m_titleLabel->underMouse() || m_statusLabel->underMouse(); +} + +void FeatureGUI::setIndex(int index) +{ + m_featureIndex = index; + m_indexLabel->setText(tr("F:%1").arg(m_featureIndex)); +} + diff --git a/sdrgui/feature/featuregui.h b/sdrgui/feature/featuregui.h index 90fe4bc4c..c51691acb 100644 --- a/sdrgui/feature/featuregui.h +++ b/sdrgui/feature/featuregui.h @@ -18,20 +18,32 @@ #ifndef SDRGUI_FEATURE_FEATUREGUI_H_ #define SDRGUI_FEATURE_FEATUREGUI_H_ -#include "gui/rollupwidget.h" +#include + +#include "gui/rollupcontents.h" #include "export.h" class QCloseEvent; class MessageQueue; +class QLabel; +class QPushButton; +class QVBoxLayout; +class QHBoxLayout; +class QSizeGrip; -class SDRGUI_API FeatureGUI : public RollupWidget +class SDRGUI_API FeatureGUI : public QMdiSubWindow { Q_OBJECT public: - FeatureGUI(QWidget *parent = nullptr) : - RollupWidget(parent) - { } - virtual ~FeatureGUI() { } + enum ContextMenuType + { + ContextMenuNone, + ContextMenuChannelSettings, + ContextMenuStreamSettings + }; + + FeatureGUI(QWidget *parent = nullptr); + virtual ~FeatureGUI(); virtual void destroy() = 0; virtual void resetToDefaults() = 0; @@ -40,11 +52,57 @@ public: virtual MessageQueue* getInputMessageQueue() = 0; + RollupContents *getRollupContents() { return &m_rollupContents; } + void setTitleColor(const QColor&) {} // not implemented for a feature + void setTitle(const QString& title); + void setIndex(int index); + void setWorkspaceIndex(int index) { m_workspaceIndex = index; } + int getWorkspaceIndex() const { return m_workspaceIndex; } + protected: void closeEvent(QCloseEvent *event); + void mousePressEvent(QMouseEvent* event); + void mouseMoveEvent(QMouseEvent* event); + void resetContextMenuType() { m_contextMenuType = ContextMenuNone; } + + int m_featureIndex; + int m_workspaceIndex; + QString m_helpURL; + RollupContents m_rollupContents; + ContextMenuType m_contextMenuType; + +protected slots: + void shrinkWindow(); + +private: + bool isOnMovingPad(); + + QLabel *m_indexLabel; + QPushButton *m_settingsButton; + QLabel *m_titleLabel; + QPushButton *m_helpButton; + QPushButton *m_moveButton; + QPushButton *m_shrinkButton; + QPushButton *m_closeButton; + QLabel *m_statusLabel; + QVBoxLayout *m_layouts; + QHBoxLayout *m_topLayout; + QHBoxLayout *m_centerLayout; + QHBoxLayout *m_bottomLayout; + QSizeGrip *m_sizeGripTopRight; + QSizeGrip *m_sizeGripBottomRight; + bool m_drag; + QPoint m_DragPosition; + +private slots: + void activateSettingsDialog(); + void showHelp(); + void openMoveToWorkspaceDialog(); signals: void closing(); + void moveToWorkspace(int workspaceIndex); + void forceShrink(); }; #endif // SDRGUI_FEATURE_FEATUREGUI_H_ diff --git a/sdrgui/feature/featureuiset.cpp b/sdrgui/feature/featureuiset.cpp index c86e5fa08..621d811c1 100644 --- a/sdrgui/feature/featureuiset.cpp +++ b/sdrgui/feature/featureuiset.cpp @@ -15,7 +15,7 @@ // along with this program. If not, see . // /////////////////////////////////////////////////////////////////////////////////// -#include "gui/featurewindow.h" +#include "gui/workspace.h" #include "plugin/pluginapi.h" #include "settings/featuresetpreset.h" #include "feature/featureutils.h" @@ -27,7 +27,6 @@ FeatureUISet::FeatureUISet(int tabIndex, FeatureSet *featureSet) { - m_featureWindow = new FeatureWindow; m_featureTabIndex = tabIndex; m_featureSet = featureSet; } @@ -35,12 +34,11 @@ FeatureUISet::FeatureUISet(int tabIndex, FeatureSet *featureSet) FeatureUISet::~FeatureUISet() { freeFeatures(); - delete m_featureWindow; } -void FeatureUISet::addRollupWidget(QWidget *widget) +void FeatureUISet::addRollupWidget(QWidget *) // TODO: remove { - m_featureWindow->addRollupWidget(widget); + // m_featureWindow->addRollupWidget(widget); } void FeatureUISet::registerFeatureInstance(FeatureGUI* featureGUI, Feature *feature) @@ -114,7 +112,12 @@ Feature *FeatureUISet::getFeatureAt(int featureIndex) } } -void FeatureUISet::loadFeatureSetSettings(const FeatureSetPreset *preset, PluginAPI *pluginAPI, WebAPIAdapterInterface *apiAdapter) +void FeatureUISet::loadFeatureSetSettings( + const FeatureSetPreset *preset, + PluginAPI *pluginAPI, + WebAPIAdapterInterface *apiAdapter, + Workspace *workspace +) { qDebug("FeatureUISet::loadFeatureSetSettings: Loading preset [%s | %s]", qPrintable(preset->getGroup()), @@ -160,6 +163,9 @@ void FeatureUISet::loadFeatureSetSettings(const FeatureSetPreset *preset, Plugin featureGUI = (*featureRegistrations)[i].m_plugin->createFeatureGUI(this, feature); registerFeatureInstance(featureGUI, feature); + featureGUI->setIndex(feature->getIndexInFeatureSet()); + featureGUI->setWorkspaceIndex(workspace->getIndex()); + workspace->addToMdiArea((QMdiSubWindow*) featureGUI); break; } } diff --git a/sdrgui/feature/featureuiset.h b/sdrgui/feature/featureuiset.h index af9aa9c5c..b82ff1e82 100644 --- a/sdrgui/feature/featureuiset.h +++ b/sdrgui/feature/featureuiset.h @@ -25,13 +25,13 @@ #include "export.h" class QWidget; -class FeatureWindow; class FeatureGUI; class PluginAPI; class FeatureSet; class Feature; class FeatureSetPreset; class WebAPIAdapterInterface; +class Workspace; class SDRGUI_API FeatureUISet : public QObject { @@ -46,11 +46,14 @@ public: void deleteFeature(int featureIndex); const Feature *getFeatureAt(int featureIndex) const; Feature *getFeatureAt(int featureIndex); - void loadFeatureSetSettings(const FeatureSetPreset* preset, PluginAPI *pluginAPI, WebAPIAdapterInterface *apiAdapter); + void loadFeatureSetSettings( + const FeatureSetPreset* preset, + PluginAPI *pluginAPI, + WebAPIAdapterInterface *apiAdapter, + Workspace *workspace + ); void saveFeatureSetSettings(FeatureSetPreset* preset); - FeatureWindow *m_featureWindow; - private: struct FeatureInstanceRegistration { diff --git a/sdrgui/gui/basicfeaturesettingsdialog.cpp b/sdrgui/gui/basicfeaturesettingsdialog.cpp index f90a4ddcd..f7695af78 100644 --- a/sdrgui/gui/basicfeaturesettingsdialog.cpp +++ b/sdrgui/gui/basicfeaturesettingsdialog.cpp @@ -10,8 +10,6 @@ BasicFeatureSettingsDialog::BasicFeatureSettingsDialog(QWidget *parent) : { ui->setupUi(this); ui->title->setText(m_title); - m_color =m_color; - paintColor(); } BasicFeatureSettingsDialog::~BasicFeatureSettingsDialog() @@ -27,35 +25,6 @@ void BasicFeatureSettingsDialog::setTitle(const QString& title) ui->title->blockSignals(false); } -void BasicFeatureSettingsDialog::setColor(const QColor& color) -{ - m_color = color; - paintColor(); -} - -void BasicFeatureSettingsDialog::paintColor() -{ - QPixmap pm(24, 24); - pm.fill(m_color); - ui->colorBtn->setIcon(pm); - ui->colorText->setText(tr("#%1%2%3") - .arg(m_color.red(), 2, 16, QChar('0')) - .arg(m_color.green(), 2, 16, QChar('0')) - .arg(m_color.blue(), 2, 16, QChar('0'))); -} - -void BasicFeatureSettingsDialog::on_colorBtn_clicked() -{ - QColor c = m_color; - c = QColorDialog::getColor(c, this, tr("Select Color for Channel"), QColorDialog::DontUseNativeDialog); - - if (c.isValid()) - { - m_color = c; - paintColor(); - } -} - void BasicFeatureSettingsDialog::on_title_editingFinished() { m_title = ui->title->text(); diff --git a/sdrgui/gui/basicfeaturesettingsdialog.h b/sdrgui/gui/basicfeaturesettingsdialog.h index 629cdf6f5..4891212e0 100644 --- a/sdrgui/gui/basicfeaturesettingsdialog.h +++ b/sdrgui/gui/basicfeaturesettingsdialog.h @@ -34,9 +34,7 @@ public: explicit BasicFeatureSettingsDialog(QWidget *parent = nullptr); ~BasicFeatureSettingsDialog(); void setTitle(const QString& title); - void setColor(const QColor& color); const QString& getTitle() const { return m_title; } - const QColor& getColor() const { return m_color; } bool useReverseAPI() const { return m_useReverseAPI; } const QString& getReverseAPIAddress() const { return m_reverseAPIAddress; } uint16_t getReverseAPIPort() const { return m_reverseAPIPort; } @@ -50,7 +48,6 @@ public: bool hasChanged() const { return m_hasChanged; } private slots: - void on_colorBtn_clicked(); void on_title_editingFinished(); void on_reverseAPI_toggled(bool checked); void on_reverseAPIAddress_editingFinished(); @@ -61,7 +58,6 @@ private slots: private: Ui::BasicFeatureSettingsDialog *ui; - QColor m_color; QString m_title; bool m_useReverseAPI; QString m_reverseAPIAddress; @@ -69,8 +65,6 @@ private: uint16_t m_reverseAPIFeatureSetIndex; uint16_t m_reverseAPIFeatureIndex; bool m_hasChanged; - - void paintColor(); }; #endif // INCLUDE_SDRGUI_BASICFEATURESETTINGSDIALOG_H diff --git a/sdrgui/gui/basicfeaturesettingsdialog.ui b/sdrgui/gui/basicfeaturesettingsdialog.ui index 9c9aa2442..0e72cd7fc 100644 --- a/sdrgui/gui/basicfeaturesettingsdialog.ui +++ b/sdrgui/gui/basicfeaturesettingsdialog.ui @@ -6,10 +6,16 @@ 0 0 - 456 - 158 + 439 + 118 + + + 0 + 0 + + Liberation Sans @@ -44,53 +50,6 @@ - - - - - - - 0 - 0 - - - - Color - - - - - - - Channel marker color - - - - - - - - - - #ff0000 - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - diff --git a/sdrgui/gui/featurepresetsdialog.cpp b/sdrgui/gui/featurepresetsdialog.cpp index aaf188d9c..9d842ac07 100644 --- a/sdrgui/gui/featurepresetsdialog.cpp +++ b/sdrgui/gui/featurepresetsdialog.cpp @@ -375,7 +375,7 @@ void FeaturePresetsDialog::loadPresetSettings(const FeatureSetPreset* preset) qPrintable(preset->getGroup()), qPrintable(preset->getDescription())); - m_featureUISet->loadFeatureSetSettings(preset, m_pluginAPI, m_apiAdapter); + m_featureUISet->loadFeatureSetSettings(preset, m_pluginAPI, m_apiAdapter, m_workspace); } void FeaturePresetsDialog::sortFeatureSetPresets() diff --git a/sdrgui/gui/featurepresetsdialog.h b/sdrgui/gui/featurepresetsdialog.h index 49f3d7c2c..7785be0f2 100644 --- a/sdrgui/gui/featurepresetsdialog.h +++ b/sdrgui/gui/featurepresetsdialog.h @@ -29,6 +29,7 @@ class FeatureSetPreset; class FeatureUISet; class WebAPIAdapterInterface; class PluginAPI; +class Workspace; namespace Ui { class FeaturePresetsDialog; @@ -43,6 +44,7 @@ public: void setFeatureUISet(FeatureUISet *featureUISet) { m_featureUISet = featureUISet; } void setPluginAPI(PluginAPI *pluginAPI) { m_pluginAPI = pluginAPI; } void setWebAPIAdapter(WebAPIAdapterInterface *apiAdapter) { m_apiAdapter = apiAdapter; } + void setWorkspace(Workspace *workspace) { m_workspace = workspace; } void populateTree(); private: @@ -56,6 +58,7 @@ private: FeatureUISet *m_featureUISet; PluginAPI *m_pluginAPI; WebAPIAdapterInterface *m_apiAdapter; + Workspace *m_workspace; QTreeWidgetItem* addPresetToTree(const FeatureSetPreset* preset); void updatePresetControls(); diff --git a/sdrgui/gui/rollupcontents.cpp b/sdrgui/gui/rollupcontents.cpp new file mode 100644 index 000000000..d8077c96c --- /dev/null +++ b/sdrgui/gui/rollupcontents.cpp @@ -0,0 +1,601 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2022 Edouard Griffiths, F4EXB // +// // +// This program 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 as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program 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 V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include +#include +#include + +#include "gui/rollupcontents.h" +#include "settings/rollupstate.h" +#include "ui_glspectrumgui.h" + +RollupContents::RollupContents(QWidget* parent) : + QWidget(parent), + m_highlighted(false), + m_streamIndicator("S"), + // m_channelWidget(true), + m_newHeight(0) +{ + setMinimumSize(250, 150); + setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding); + setBackgroundRole(QPalette::Window); + + setAutoFillBackground(false); + setAttribute(Qt::WA_OpaquePaintEvent, true); +} + +// QByteArray RollupWidget::saveState(int version) const +// { +// QByteArray state; +// QDataStream stream(&state, QIODevice::WriteOnly); +// int count = 0; + +// for (int i = 0; i < children().count(); ++i) +// { +// QWidget* r = qobject_cast(children()[i]); + +// if (r) { +// count++; +// } +// } + +// stream << VersionMarker; +// stream << version; +// stream << count; + +// for (int i = 0; i < children().count(); ++i) +// { +// QWidget* r = qobject_cast(children()[i]); + +// if (r) +// { +// stream << r->objectName(); + +// if (r->isHidden()) { +// stream << (int) 0; +// } else { +// stream << (int) 1; +// } +// } +// } + +// return state; +// } + +void RollupContents::saveState(RollupState &state) const +{ + QList& childrenStates = state.getChildren(); + childrenStates.clear(); + + for (const auto &child : children()) + { + QWidget* r = qobject_cast(child); + + if (r && isRollupChild(r)) { + childrenStates.push_back({r->objectName(), r->isHidden()}); + } + } +} + +// bool RollupWidget::restoreState(const QByteArray& state, int version) +// { +// if (state.isEmpty()) { +// return false; +// } + +// QByteArray sd = state; +// QDataStream stream(&sd, QIODevice::ReadOnly); +// int marker, v; +// stream >> marker; +// stream >> v; + +// if ((stream.status() != QDataStream::Ok) || (marker != VersionMarker) || (v != version)) { +// return false; +// } + +// int count; +// stream >> count; + +// if (stream.status() != QDataStream::Ok) { +// return false; +// } + +// for (int i = 0; i < count; ++i) +// { +// QString name; +// int visible; + +// stream >> name; +// stream >> visible; + +// if (stream.status() != QDataStream::Ok) { +// return false; +// } + +// for (int j = 0; j < children().count(); ++j) +// { +// QWidget* r = qobject_cast(children()[j]); + +// if (r) +// { +// if (r->objectName() == name) +// { +// if (visible) { +// r->show(); +// } else { +// r->hide(); +// } + +// break; +// } +// } +// } +// } + +// return true; +// } + +void RollupContents::restoreState(const RollupState& state) +{ + const QList& childrenStates = state.getChildren(); + + for (const auto &object : children()) + { + QWidget* r = qobject_cast(object); + + if (r && isRollupChild(r)) + { + for (const auto &childState : childrenStates) + { + if (childState.m_objectName.compare(r->objectName()) == 0) + { + if (childState.m_isHidden) { + r->hide(); + } else { + r->show(); + } + + break; + } + } + } + } +} + +void RollupContents::setHighlighted(bool highlighted) +{ + if (m_highlighted != highlighted) + { + m_highlighted = highlighted; + update(); + } +} + +int RollupContents::arrangeRollups() +{ + QFontMetrics fm(font()); + int pos; + + // First calculate minimum height needed, to determine how much extra space + // we have that can be split between expanding widgets + pos = 2; // fm.height() + 4; + int expandingChildren = 0; + for (int i = 0; i < children().count(); ++i) + { + QWidget* r = qobject_cast(children()[i]); + + if ((r != nullptr) && isRollupChild(r)) + { + pos += fm.height() + 2; + if (!r->isHidden()) + { + if (r->sizePolicy().verticalPolicy() & QSizePolicy::ExpandFlag) { + expandingChildren++; + } + int h = 0; + if (r->hasHeightForWidth()) { + h = r->heightForWidth(width() - 4); + } else { + h = r->minimumSizeHint().height(); + } + pos += h + 5; + } + } + } + + setMinimumHeight(pos); + + // Split extra space equally between widgets + // If there's a remainder, we give it to the first widget + // In the future, we should probably respect 'Vertical Stretch' + int extraSpace; + int firstExtra; + if ((expandingChildren > 0) && (m_newHeight > pos)) + { + int totalExtra = m_newHeight - pos; + extraSpace = totalExtra / expandingChildren; + firstExtra = totalExtra - (extraSpace * expandingChildren); + } + else + { + extraSpace = 0; + firstExtra = 0; + } + + // Now reposition and resize child widgets + pos = 2; // fm.height() + 4; + for (int i = 0; i < children().count(); ++i) + { + QWidget* r = qobject_cast(children()[i]); + + if ((r != nullptr) && isRollupChild(r)) + { + pos += fm.height() + 2; + + if (!r->isHidden()) + { + r->move(2, pos + 3); + + int h = 0; + if (r->hasHeightForWidth()) { + h = r->heightForWidth(width() - 4); + } else { + h = r->minimumSizeHint().height(); + } + if (r->sizePolicy().verticalPolicy() & QSizePolicy::ExpandFlag) + { + h += extraSpace; + h += firstExtra; + firstExtra = 0; + } + + r->resize(width() - 4, h); + pos += r->height() + 5; + } + } + } + + if (expandingChildren == 0) { + setMaximumHeight(pos); + } else { + setMaximumHeight(16777215); + } + updateGeometry(); + return pos; +} + +void RollupContents::paintEvent(QPaintEvent*) +{ + QPainter p(this); + QColor frame = palette().highlight().color(); + + // Eigenbau + QFontMetrics fm(font()); + + p.setRenderHint(QPainter::Antialiasing, true); + + // Ecken (corners) + p.setPen(Qt::NoPen); + p.setBrush(palette().base()); + p.drawRect(0, 0, 5, 5); + p.drawRect(width() - 5, 0, 5, 5); + p.drawRect(0, height() - 5, 5, 5); + p.drawRect(width() - 5, height() - 5, 5, 5); + + // Rahmen (frame) + p.setPen(m_highlighted ? Qt::white : frame); + p.setBrush(palette().window()); + QRectF r(rect()); + r.adjust(0.5, 0.5, -0.5, -0.5); + p.drawRoundedRect(r, 3.0, 3.0, Qt::AbsoluteSize); + + // // Titel-Hintergrund (Title background) + // p.setPen(Qt::NoPen); + // p.setBrush(m_titleColor); + // QPainterPath path; + // path.moveTo(1.5, fm.height() + 2.5); + // path.lineTo(width() - 1.5, fm.height() + 2.5); + // path.lineTo(width() - 1.5, 3.5); + // path.arcTo(QRectF(width() - 3.5, 0, 2.5, 2.5), 270, -90); + // path.lineTo(3.5, 1.5); + // path.arcTo(QRectF(1.5, 2.5, 2.5, 2.5), 90, 90); + // p.drawPath(path); + + // // Titel-Abschlusslinie (Title closing line) + // p.setPen(frame); + // p.drawLine(QPointF(0.5, 2 + fm.height() + 1.5), QPointF(width() - 1.5, 2 + fm.height() + 1.5)); + + // // Aktiv-Button links + // p.setPen(QPen(palette().windowText().color(), 1.0)); + // p.setBrush(palette().light()); + // p.drawRoundedRect(QRectF(3.5, 3.5, fm.ascent(), fm.ascent()), 2.0, 2.0, Qt::AbsoluteSize); + // p.setPen(QPen(Qt::white, 1.0)); + // p.drawText(QRectF(3.5, 2.5, fm.ascent(), fm.ascent()), Qt::AlignCenter, "c"); + + // if (m_channelWidget) + // { + // // Stromkanal-Button links (Current channel) + // p.setPen(QPen(palette().windowText().color(), 1.0)); + // p.setBrush(palette().light()); + // p.drawRoundedRect(QRectF(5.5 + fm.ascent(), 2.5, fm.ascent() + 2.0, fm.ascent() + 2.0), 2.0, 2.0, Qt::AbsoluteSize); + // p.setPen(QPen(Qt::white, 1.0)); + // p.drawText(QRectF(5.5 + fm.ascent(), 2.5, fm.ascent() + 2.0, fm.ascent() + 2.0), Qt::AlignCenter, m_streamIndicator); + // } + + // // Help button + // if (!m_helpURL.isEmpty()) + // { + // p.setRenderHint(QPainter::Antialiasing, true); + // p.setPen(QPen(palette().windowText().color(), 1.0)); + // p.setBrush(palette().light()); + // r = QRectF(width() - 2*(3.5 + fm.ascent()), 3.5, fm.ascent(), fm.ascent()); + // p.drawRoundedRect(r, 2.0, 2.0, Qt::AbsoluteSize); + // p.drawText(QRectF(width() - 2*(3.5 + fm.ascent()), 5, fm.ascent(), fm.ascent() - 2), Qt::AlignCenter, "?"); + // } + + // //p.drawLine(r.topLeft() + QPointF(1, 1), r.bottomRight() + QPointF(-1, -1)); + // //p.drawLine(r.bottomLeft() + QPointF(1, -1), r.topRight() + QPointF(-1, 1)); + + // // Schließen-Button rechts (Close button on the right) + // p.setRenderHint(QPainter::Antialiasing, true); + // p.setPen(QPen(palette().windowText().color(), 1.0)); + // p.setBrush(palette().light()); + // r = QRectF(width() - 3.5 - fm.ascent(), 3.5, fm.ascent(), fm.ascent()); + // p.drawRoundedRect(r, 2.0, 2.0, Qt::AbsoluteSize); + // p.setPen(QPen(palette().windowText().color(), 1.5)); + // p.drawLine(r.topLeft() + QPointF(1, 1), r.bottomRight() + QPointF(-1, -1)); + // p.drawLine(r.bottomLeft() + QPointF(1, -1), r.topRight() + QPointF(-1, 1)); + + // // Titel + // //p.setPen(palette().highlightedText().color()); + // p.setPen(m_titleTextColor); + // p.drawText(QRect(2 + 2*fm.height() + 2, 2, width() - 6 - 3*fm.height(), fm.height()), + // fm.elidedText(windowTitle(), Qt::ElideMiddle, width() - 6 - 3*fm.height(), 0)); + + // Rollups + int pos = 2; // fm.height() + 4; + + const QObjectList& c = children(); + QObjectList::ConstIterator w = c.begin(); + QObjectList::ConstIterator n = c.begin(); + + for (n = c.begin(); n != c.end(); ++n) + { + if (qobject_cast(*n) != nullptr) { + break; + } + } + + for (w = n; w != c.end(); w = n) + { + if (n != c.end()) { + ++n; + } + + for (; n != c.end(); ++n) + { + if (qobject_cast(*n) != nullptr) { + break; + } + } + + pos += paintRollup(qobject_cast(*w), pos, &p, n == c.end(), frame); + } +} + +int RollupContents::paintRollup(QWidget* rollup, int pos, QPainter* p, bool last, const QColor& frame) +{ + QFontMetrics fm(font()); + int height = 1; + + // Titel-Abschlusslinie + if (!rollup->isHidden()) + { + p->setPen(palette().dark().color()); + p->drawLine(QPointF(1.5, pos + fm.height() + 1.5), QPointF(width() - 1.5, pos + fm.height() + 1.5)); + p->setPen(palette().light().color()); + p->drawLine(QPointF(1.5, pos + fm.height() + 2.5), QPointF(width() - 1.5, pos + fm.height() + 2.5)); + height += 2; + } + else + { + if (!last) + { + p->setPen(frame); + p->drawLine(QPointF(1.5, pos + fm.height() + 1.5), QPointF(width() - 1.5, pos + fm.height() + 1.5)); + height++; + } + } + + // Titel + p->setPen(palette().windowText().color()); + p->drawText(QRect(2 + fm.height(), pos, width() - 4 - fm.height(), fm.height()), + fm.elidedText(rollup->windowTitle(), Qt::ElideMiddle, width() - 4 - fm.height(), 0)); + height += fm.height(); + + // Ausklapp-Icon + p->setPen(palette().windowText().color()); + p->setBrush(palette().windowText()); + + if (!rollup->isHidden()) + { + QPolygonF a; + a.append(QPointF(3.5, pos + 2)); + a.append(QPointF(3.5 + fm.ascent(), pos + 2)); + a.append(QPointF(3.5 + fm.ascent() / 2.0, pos + fm.height() - 2)); + p->drawPolygon(a); + } + else + { + QPolygonF a; + a.append(QPointF(3.5, pos + 2)); + a.append(QPointF(3.5, pos + fm.height() - 2)); + a.append(QPointF(3.5 + fm.ascent(), pos + fm.height() / 2)); + p->drawPolygon(a); + } + + // Inhalt + if (!rollup->isHidden() && (!last)) + { + // Rollup-Abschlusslinie + p->setPen(frame); + p->drawLine(QPointF(1.5, pos + fm.height() + rollup->height() + 6.5), + QPointF(width() - 1.5, pos + fm.height() + rollup->height() + 6.5)); + height += rollup->height() + 4; + } + + return height; +} + +void RollupContents::resizeEvent(QResizeEvent* size) +{ + m_newHeight = size->size().height(); + arrangeRollups(); + QWidget::resizeEvent(size); +} + +void RollupContents::mousePressEvent(QMouseEvent* event) +{ + QFontMetrics fm(font()); + + // // menu box left + // if (QRectF(3.5, 3.5, fm.ascent(), fm.ascent()).contains(event->pos())) + // { + // m_contextMenuType = ContextMenuChannelSettings; + // emit customContextMenuRequested(event->globalPos()); + // return; + // } + + // if (m_channelWidget) + // { + // // Stream channel menu left + // if (QRectF(5.5 + fm.ascent(), 2.5, fm.ascent() + 2.0, fm.ascent() + 2.0).contains(event->pos())) + // { + // m_contextMenuType = ContextMenuStreamSettings; + // emit customContextMenuRequested(event->globalPos()); + // return; + // } + // } + + // // help button + // if(!m_helpURL.isEmpty() && QRectF(width() - 2*(3.5 + fm.ascent()), 3.5, fm.ascent(), fm.ascent()).contains(event->pos())) + // { + // QString url; + // if (m_helpURL.startsWith("http")) { + // url = m_helpURL; + // } else { + // url = QString("https://github.com/f4exb/sdrangel/blob/master/%1").arg(m_helpURL); // Something like "plugins/channelrx/chanalyzer/readme.md" + // } + // QDesktopServices::openUrl(QUrl(url)); + // return; + // } + + // // close button right + // if(QRectF(width() - 3.5 - fm.ascent(), 3.5, fm.ascent(), fm.ascent()).contains(event->pos())) { + // close(); + // return; + // } + + // check if we need to change a rollup widget + int pos = 2; // fm.height() + 4; + + for (int i = 0; i < children().count(); ++i) + { + QWidget* r = qobject_cast(children()[i]); + + if (r) + { + if ((event->y() >= pos) && (event->y() < (pos + fm.height() + 3))) + { + if (r->isHidden()) + { + r->show(); + //emit widgetRolled(r, true); + } + else + { + r->hide(); + //emit widgetRolled(r, false); + } + + arrangeRollups(); + repaint(); + return; + } + else + { + pos += fm.height() + 2; + + if (!r->isHidden()) { + pos += r->height() + 5; + } + } + } + } +} + +bool RollupContents::event(QEvent* event) +{ + if (event->type() == QEvent::ChildAdded) + { + ((QChildEvent*)event)->child()->installEventFilter(this); + arrangeRollups(); + } + else if (event->type() == QEvent::ChildRemoved) + { + ((QChildEvent*)event)->child()->removeEventFilter(this); + arrangeRollups(); + } + + return QWidget::event(event); +} + +bool RollupContents::eventFilter(QObject* object, QEvent* event) +{ + if (event->type() == QEvent::Show) + { + if (children().contains(object)) + { + arrangeRollups(); + emit widgetRolled(qobject_cast(object), true); + } + } + else if (event->type() == QEvent::Hide) + { + if (children().contains(object)) + { + arrangeRollups(); + emit widgetRolled(qobject_cast(object), false); + } + } + else if (event->type() == QEvent::WindowTitleChange) + { + if (children().contains(object)) { + repaint(); + } + } + + return QWidget::eventFilter(object, event); +} + +bool RollupContents::isRollupChild(QWidget *childWidget) +{ + return (qobject_cast(childWidget) == nullptr); // exclude Dialogs from rollups +} diff --git a/sdrgui/gui/rollupcontents.h b/sdrgui/gui/rollupcontents.h new file mode 100644 index 000000000..e4eeb3ca6 --- /dev/null +++ b/sdrgui/gui/rollupcontents.h @@ -0,0 +1,68 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2022 Edouard Griffiths, F4EXB // +// // +// API for features // +// // +// This program 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 as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program 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 V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef INCLUDE_ROLLUPCONTENTS_H +#define INCLUDE_ROLLUPCONTENTS_H + +#include +#include "export.h" + +class RollupState; + +class SDRGUI_API RollupContents : public QWidget { + Q_OBJECT + +public: + RollupContents(QWidget* parent = nullptr); + void setHighlighted(bool highlighted); + // void setChannelWidget(bool channelWidget) { m_channelWidget = channelWidget; } + // QByteArray saveState(int version = 0) const; + void saveState(RollupState& state) const; + // bool restoreState(const QByteArray& state, int version = 0); + void restoreState(const RollupState& state); + int arrangeRollups(); + +signals: + void widgetRolled(QWidget* widget, bool rollDown); + +protected: + enum { + VersionMarker = 0xff + }; + + bool m_highlighted; + QString m_streamIndicator; + QString m_helpURL; + + void paintEvent(QPaintEvent*); + int paintRollup(QWidget* rollup, int pos, QPainter* p, bool last, const QColor& frame); + + void resizeEvent(QResizeEvent* size); + void mousePressEvent(QMouseEvent* event); + + bool event(QEvent* event); + bool eventFilter(QObject* object, QEvent* event); + +private: + static bool isRollupChild(QWidget *childWidget); //!< chidl is part of rollups (ex: not a dialog) + // bool m_channelWidget; + int m_newHeight; +}; + +#endif // INCLUDE_ROLLUPCONTENTS_H diff --git a/sdrgui/gui/workspace.cpp b/sdrgui/gui/workspace.cpp new file mode 100644 index 000000000..3509a6810 --- /dev/null +++ b/sdrgui/gui/workspace.cpp @@ -0,0 +1,271 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2022 F4EXB // +// written by Edouard Griffiths // +// // +// This program 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 as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program 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 V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include +#include +#include +#include + +#include "workspace.h" + +Workspace::Workspace(int index, QWidget *parent, Qt::WindowFlags flags) : + QDockWidget(parent, flags), + m_index(index), + m_featureAddDialog(this) +{ + m_mdi = new QMdiArea(this); + m_mdi->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); + m_mdi->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); + setWidget(m_mdi); + + setWindowTitle(tr("W%1").arg(m_index)); + + m_titleBar = new QWidget(); + m_titleBarLayout = new QHBoxLayout(); + m_titleBarLayout->setMargin(0); + m_titleBar->setLayout(m_titleBarLayout); + + m_titleLabel = new QLabel(); + m_titleLabel->setFixedSize(32, 16); + m_titleLabel->setStyleSheet("QLabel { background-color: rgb(128, 128, 128); qproperty-alignment: AlignCenter; }"); + m_titleLabel->setText(windowTitle()); + + m_addRxDeviceButton = new QPushButton(); + QIcon addRxIcon(":/rx.png"); + m_addRxDeviceButton->setIcon(addRxIcon); + m_addRxDeviceButton->setToolTip("Add Rx device"); + m_addRxDeviceButton->setFixedSize(20, 20); + + m_addTxDeviceButton = new QPushButton(); + QIcon addTxIcon(":/tx.png"); + m_addTxDeviceButton->setIcon(addTxIcon); + m_addTxDeviceButton->setToolTip("Add Tx device"); + m_addTxDeviceButton->setFixedSize(20, 20); + + m_addMIMODeviceButton = new QPushButton(); + QIcon addMIMOIcon(":/mimo.png"); + m_addMIMODeviceButton->setIcon(addMIMOIcon); + m_addMIMODeviceButton->setToolTip("Add MIMO device"); + m_addMIMODeviceButton->setFixedSize(20, 20); + + m_vline1 = new QFrame(); + m_vline1->setFrameShape(QFrame::VLine); + m_vline1->setFrameShadow(QFrame::Sunken); + + m_addFeatureButton = new QPushButton(); + QIcon addFeatureIcon(":/tool.png"); + m_addFeatureButton->setIcon(addFeatureIcon); + m_addFeatureButton->setToolTip("Add features"); + m_addFeatureButton->setFixedSize(20, 20); + + m_featurePresetsButton = new QPushButton(); + QIcon presetsIcon(":/star.png"); + m_featurePresetsButton->setIcon(presetsIcon); + m_featurePresetsButton->setToolTip("Feature presets"); + m_featurePresetsButton->setFixedSize(20, 20); + + m_vline2 = new QFrame(); + m_vline2->setFrameShape(QFrame::VLine); + m_vline2->setFrameShadow(QFrame::Sunken); + + m_cascadeSubWindows = new QPushButton(); + QIcon cascadeSubWindowsIcon(":/cascade.png"); + m_cascadeSubWindows->setIcon(cascadeSubWindowsIcon); + m_cascadeSubWindows->setToolTip("Cascade sub windows"); + m_cascadeSubWindows->setFixedSize(20, 20); + + m_tileSubWindows = new QPushButton(); + QIcon tileSubWindowsIcon(":/tiles.png"); + m_tileSubWindows->setIcon(tileSubWindowsIcon); + m_tileSubWindows->setToolTip("Tile sub windows"); + m_tileSubWindows->setFixedSize(20, 20); + + m_normalButton = new QPushButton(); + QIcon normalIcon(":/dock.png"); + m_normalButton->setIcon(normalIcon); + m_normalButton->setToolTip("Dock/undock"); + m_normalButton->setFixedSize(20, 20); + + m_closeButton = new QPushButton(); + QIcon closeIcon(":/cross.png"); + m_closeButton->setIcon(closeIcon); + m_closeButton->setToolTip("Hide workspace"); + m_closeButton->setFixedSize(20, 20); + + m_titleBarLayout->addWidget(m_titleLabel); + m_titleBarLayout->addWidget(m_addRxDeviceButton); + m_titleBarLayout->addWidget(m_addTxDeviceButton); + m_titleBarLayout->addWidget(m_addMIMODeviceButton); + m_titleBarLayout->addWidget(m_vline1); + m_titleBarLayout->addWidget(m_addFeatureButton); + m_titleBarLayout->addWidget(m_featurePresetsButton); + m_titleBarLayout->addWidget(m_vline2); + m_titleBarLayout->addWidget(m_cascadeSubWindows); + m_titleBarLayout->addWidget(m_tileSubWindows); + m_titleBarLayout->addStretch(1); + m_titleBarLayout->addWidget(m_normalButton); + m_titleBarLayout->addWidget(m_closeButton); + setTitleBarWidget(m_titleBar); + + QObject::connect( + m_addRxDeviceButton, + &QPushButton::clicked, + this, + &Workspace::addRxDevice + ); + + QObject::connect( + m_addTxDeviceButton, + &QPushButton::clicked, + this, + &Workspace::addTxDevice + ); + + QObject::connect( + m_addMIMODeviceButton, + &QPushButton::clicked, + this, + &Workspace::addMIMODevice + ); + + QObject::connect( + m_addFeatureButton, + &QPushButton::clicked, + this, + &Workspace::addFeatureDialog + ); + + QObject::connect( + m_featurePresetsButton, + &QPushButton::clicked, + this, + &Workspace::featurePresetsDialog + ); + + QObject::connect( + m_cascadeSubWindows, + &QPushButton::clicked, + this, + &Workspace::cascadeSubWindows + ); + + QObject::connect( + m_tileSubWindows, + &QPushButton::clicked, + this, + &Workspace::tileSubWindows + ); + + QObject::connect( + m_normalButton, + &QPushButton::clicked, + this, + &Workspace::toggleFloating + ); + + connect(m_closeButton, SIGNAL(clicked()), this, SLOT(hide())); + + QObject::connect( + &m_featureAddDialog, + &FeatureAddDialog::addFeature, + this, + &Workspace::addFeatureEmitted + ); +} + +Workspace::~Workspace() +{ + delete m_closeButton; + delete m_normalButton; + delete m_tileSubWindows; + delete m_cascadeSubWindows; + delete m_vline2; + delete m_vline1; + delete m_addRxDeviceButton; + delete m_addTxDeviceButton; + delete m_addMIMODeviceButton; + delete m_addFeatureButton; + delete m_featurePresetsButton; + delete m_titleLabel; + delete m_titleBarLayout; + delete m_titleBar; + delete m_mdi; +} + +void Workspace::toggleFloating() +{ + setFloating(!isFloating()); +} + +void Workspace::addRxDevice() +{ + +} + +void Workspace::addTxDevice() +{ + +} + +void Workspace::addMIMODevice() +{ + +} + +void Workspace::addFeatureDialog() +{ + m_featureAddDialog.exec(); +} + +void Workspace::addFeatureEmitted(int featureIndex) +{ + if (featureIndex >= 0) { + emit addFeature(this, featureIndex); + } +} + +void Workspace::featurePresetsDialog() +{ + QPoint p = mapFromGlobal(QCursor::pos()); + emit featurePresetsDialogRequested(p, this); +} + +void Workspace::cascadeSubWindows() +{ + m_mdi->cascadeSubWindows(); +} + +void Workspace::tileSubWindows() +{ + m_mdi->tileSubWindows(); +} + +void Workspace::addToMdiArea(QMdiSubWindow *sub) +{ + sub->setParent(m_mdi); + m_mdi->addSubWindow(sub); + sub->show(); +} + +void Workspace::removeFromMdiArea(QMdiSubWindow *sub) +{ + m_mdi->removeSubWindow(sub); +} diff --git a/sdrgui/gui/workspace.h b/sdrgui/gui/workspace.h new file mode 100644 index 000000000..56b307fad --- /dev/null +++ b/sdrgui/gui/workspace.h @@ -0,0 +1,84 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2022 F4EXB // +// written by Edouard Griffiths // +// // +// This program 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 as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program 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 V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef SDRGUI_GUI_WORKSPACE_H_ +#define SDRGUI_GUI_WORKSPACE_H_ + +#include + +#include "export.h" +#include "featureadddialog.h" + +class QHBoxLayout; +class QLabel; +class QPushButton; +class QStringList; +class QMdiArea; +class QMdiSubWindow; +class QFrame; + +class SDRGUI_API Workspace : public QDockWidget +{ + Q_OBJECT +public: + Workspace(int index, QWidget *parent = nullptr, Qt::WindowFlags flags = Qt::WindowFlags()); + ~Workspace(); + + int getIndex() const { return m_index; } + void resetAvailableFeatures() { m_featureAddDialog.resetFeatureNames(); } + void addAvailableFeatures(const QStringList& featureNames) { m_featureAddDialog.addFeatureNames(featureNames); } + void addToMdiArea(QMdiSubWindow *sub); + void removeFromMdiArea(QMdiSubWindow *sub); + +private: + int m_index; + QPushButton *m_addRxDeviceButton; + QPushButton *m_addTxDeviceButton; + QPushButton *m_addMIMODeviceButton; + QFrame *m_vline1; + QPushButton *m_addFeatureButton; + QPushButton *m_featurePresetsButton; + QFrame *m_vline2; + QPushButton *m_cascadeSubWindows; + QPushButton *m_tileSubWindows; + QWidget *m_titleBar; + QHBoxLayout *m_titleBarLayout; + QLabel *m_titleLabel; + QPushButton *m_normalButton; + QPushButton *m_closeButton; + FeatureAddDialog m_featureAddDialog; + QMdiArea *m_mdi; + +private slots: + void addRxDevice(); + void addTxDevice(); + void addMIMODevice(); + void addFeatureDialog(); + void featurePresetsDialog(); + void cascadeSubWindows(); + void tileSubWindows(); + void addFeatureEmitted(int featureIndex); + void toggleFloating(); + +signals: + void addFeature(Workspace*, int); + void featurePresetsDialogRequested(QPoint, Workspace*); +}; + + +#endif // SDRGUI_GUI_WORKSPACE_H_ diff --git a/sdrgui/gui/workspaceselectiondialog.cpp b/sdrgui/gui/workspaceselectiondialog.cpp new file mode 100644 index 000000000..ef0cff326 --- /dev/null +++ b/sdrgui/gui/workspaceselectiondialog.cpp @@ -0,0 +1,47 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2022 F4EXB // +// written by Edouard Griffiths // +// // +// This program 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 as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program 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 V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#include + +#include "workspaceselectiondialog.h" +#include "ui_workspaceselectiondialog.h" + +WorkspaceSelectionDialog::WorkspaceSelectionDialog(int numberOfWorkspaces, QWidget *parent) : + QDialog(parent), + ui(new Ui::WorkspaceSelectionDialog), + m_numberOfWorkspaces(numberOfWorkspaces), + m_hasChanged(false) +{ + ui->setupUi(this); + + for (int i = 0; i < m_numberOfWorkspaces; i++) { + ui->workspaceList->addItem(tr("W:%1").arg(i)); + } +} + +WorkspaceSelectionDialog::~WorkspaceSelectionDialog() +{ + delete ui; +} + +void WorkspaceSelectionDialog::accept() +{ + m_selectedRow = ui->workspaceList->currentRow(); + m_hasChanged = true; + QDialog::accept(); +} diff --git a/sdrgui/gui/workspaceselectiondialog.h b/sdrgui/gui/workspaceselectiondialog.h new file mode 100644 index 000000000..6ac68bfbc --- /dev/null +++ b/sdrgui/gui/workspaceselectiondialog.h @@ -0,0 +1,50 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2022 F4EXB // +// written by Edouard Griffiths // +// // +// This program 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 as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program 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 V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef SDRGUI_GUI_WORKSPACESELECTIONDIALOG_H_ +#define SDRGUI_GUI_WORKSPACESELECTIONDIALOG_H_ + +#include + +#include "export.h" + +namespace Ui { + class WorkspaceSelectionDialog; +} + +class SDRGUI_API WorkspaceSelectionDialog : public QDialog +{ + Q_OBJECT +public: + explicit WorkspaceSelectionDialog(int numberOfWorkspaces, QWidget *parent = nullptr); + ~WorkspaceSelectionDialog(); + + bool hasChanged() const { return m_hasChanged; } + int getSelectedIndex() const { return m_selectedRow; } + +private: + Ui::WorkspaceSelectionDialog *ui; + int m_numberOfWorkspaces; + int m_selectedRow; + bool m_hasChanged; + +private slots: + void accept(); +}; + +#endif // SDRGUI_GUI_WORKSPACESELECTIONDIALOG_H_ diff --git a/sdrgui/gui/workspaceselectiondialog.ui b/sdrgui/gui/workspaceselectiondialog.ui new file mode 100644 index 000000000..56d37e4f6 --- /dev/null +++ b/sdrgui/gui/workspaceselectiondialog.ui @@ -0,0 +1,67 @@ + + + WorkspaceSelectionDialog + + + + 0 + 0 + 209 + 201 + + + + Workspace + + + true + + + + + + + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + WorkspaceSelectionDialog + accept() + + + 104 + 177 + + + 104 + 100 + + + + + buttonBox + rejected() + WorkspaceSelectionDialog + reject() + + + 104 + 177 + + + 104 + 100 + + + + + diff --git a/sdrgui/mainwindow.cpp b/sdrgui/mainwindow.cpp index 6190dffa2..595067bdd 100644 --- a/sdrgui/mainwindow.cpp +++ b/sdrgui/mainwindow.cpp @@ -31,6 +31,10 @@ #include #include +#include +#include +#include + #include "device/devicegui.h" #include "device/deviceapi.h" #include "device/deviceuiset.h" @@ -41,6 +45,7 @@ #include "feature/featureuiset.h" #include "feature/featureset.h" #include "feature/feature.h" +#include "feature/featuregui.h" #include "commands/commandkeyreceiver.h" #include "gui/indicator.h" #include "gui/presetitem.h" @@ -60,6 +65,8 @@ #include "gui/mypositiondialog.h" #include "gui/fftwisdomdialog.h" #include "gui/ambedevicesdialog.h" +#include "gui/workspace.h" +#include "gui/featurepresetsdialog.h" #include "dsp/dspengine.h" #include "dsp/spectrumvis.h" #include "dsp/dspcommands.h" @@ -82,7 +89,8 @@ #include
+ + + 2 + + + E&xit @@ -995,6 +1001,7 @@ channelDock commandsDock featureDock + workspaceDock @@ -1021,6 +1028,12 @@
gui/featuresdock.h
1
+ + Workspace + QDockWidget +
gui/workspace.h
+ 1 +
presetTree diff --git a/sdrgui/resources/cascade.png b/sdrgui/resources/cascade.png new file mode 100644 index 0000000000000000000000000000000000000000..be5f32e4da2ead10fb7558b864791f295fbbc012 GIT binary patch literal 6300 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 literal 0 HcmV?d00001 diff --git a/sdrgui/resources/corner_botright.png b/sdrgui/resources/corner_botright.png new file mode 100644 index 0000000000000000000000000000000000000000..d2934d18e217c0810291b2dac398b23559a4c22d GIT binary patch literal 5104 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 G-2VdVv7_w( literal 0 HcmV?d00001 diff --git a/sdrgui/resources/corner_topright.png b/sdrgui/resources/corner_topright.png new file mode 100644 index 0000000000000000000000000000000000000000..8dec8d88cf07c7d1b51cc270249fb9e789c8e9ad GIT binary patch literal 5112 zcmeHKYg7~079J2t2qK_>3Zfxc3f3euNeD?G$WuTKL_i)&g-J4j0C|`M645BAmsV6n zM5G8sD#eO|C?JY*@qvQ2wpFoI%R>dB6d#~avB>pK0xGWCKdyEAǎZKSUy}z^1 zp3HV`kiRL;1_uCuX<&eF2zs5PJ+MaT`JReB3IGP2)UZfp2&}@(W7)FrkqQ1_19Fl{D;Bz3!76O9T;#bw#f6uy7;ZqvmSZ< zAG=|*8O9#j5j*VvRds*x>Zezon#6YN8=v*Q7^r6!5AoR@^ReagGe^|i`3n|qZrUB3o|V<^yU?k|fy>foMwyLbv)igpWq;FQRx-SA!Beu0?B=4)TjsF=OMZkJW;v#A3wOXfF~_T4Y+n?t-gN3)AG1}wV-eP`=d zot9ykdv?4wO~9~1ehc-^G`&O z@50++gMF`7xHm=1uls!&Wq;hKqP=RcQ+oI8(R@>J;?BH_*elC7y`=Vw4?<-Rzp7Jr z<4y}!TY_cs=b7Q=+nW0wI*dJTJ%^H$xm;C&gHe9nNpH)Cm)6|!m(0FfQbLGP?5eQS z^R)OYJS}~IEln?y-@ER+bFTZ+-}hQL@oomB8ys6?>U7y{Ha;l#j%z2>`CI0-@+$>~ z_N|6-*^XzXSGZf#N1Hcp`{E>Pq}-;JmT+_4ygUz6`^fwqG4#03)-7N8Ezdi&!y{q( z4PI$JFDPf8wLix`D%1Jf!W*KcJv-~DHa*d$ryuOd;~s0uSyovUJhCeByKslwVT!%I zHot7QE~x!A#Er^n_)VDK~pF3vM>7d=l5O_PbwtC84hVXT23RK_RZLy+L_HW30bM!VQ*6LE%l zq@=DrUi9mROhaJht9^+DC#+%*nP<8DNS^-6*f%#ngE!mp$5~OdVG^ZAKf9K9sQD%H zB5~1%qAsh(mhC5?ny0nnl*3=(9>s)r1ZjBR=U-_U4)10E`K%~cHQHaTcdj{jw$X4} zv4`#X4X=jp0G)l>Q}^M48LLP8>$ipnwb|7S-b%(a$p4fwku>ubXP2@IPxwq>jJrurub`9qCQ4jwOSkzzgD?39|J&F6#L?1nEtc+Ce!=P#E% ze%;!lm(#GQstN$~wuyXvxPd-CZyF2QP6{=9*a4S3=Ut8oUGBGNhP8#oRc_JZFEd#Q zR+(J;R$K)uKl(32j!ll`Z2t?j_wF_GZeO?@6@iKHGJgfEQ_4e>duG1gZ@IcDRo1>+ z^kL+cub)@ z8nr{uV~;v-ZW-sCmwToBEm#Iewl!x@RD>Sz7po7sObjc04+v%1%YI2jqNo!7^ogMFeuQr^9B zfm_hH!Qhf<5vjLgD)p$vr4a{K_h5g&V??SvP;e3x{R9{f&Pcje*>l*rM}B^6t|rvz z_XflIU(R*fj4?DoNb9NxMhu3*(r391oxL4J6%FGX%5HR-Wo?n4+!TG)9DM=Uh|oqK z$yveTOT{EuAmt$>wOEF}2mru+iCPBp6A>kzhs22_Y{JvCjRd?%z$Qeva6pdC2Z3mJavXb>6e0%Oa}C~GZbOy`;r`jD5S}9KH`^xNR$p!Df|OD+_wzc zl;T8UnJy_*y!V_5_-}EtWOh8PZL`pNu(RX<8@`rg836p6mSY6 z&^bcqO$_nlVM!c{61P0k7xx1(Y+1Qh#(~lf>tiWOQg?snGgGkVSK`QifW;YbD82uD7;w|_01zrO` z0Kn0lS#7ArSQZea003-|_R!l20gq54Mj6QQ!}J>H8`GWlFPZTGHKhdldW8ivKh8U0 zoaQ;*uj{H+ebzKA-MSJV$!dPQpULDw(4o{mEsmhFx_~?VD`u#)q_j08Qo=t c70=dVZ2ZkTH=m;N&1szSWq5uE@ literal 0 HcmV?d00001 diff --git a/sdrgui/resources/cross.png b/sdrgui/resources/cross.png new file mode 100644 index 0000000000000000000000000000000000000000..043ec1f2f9ed1884d5e21354d1007976ce5989e4 GIT binary patch literal 5731 zcmeHLc~lek79JFZf(TksL98)aMQYYbLK2CH>}rrrkVSMzW+0NyBtTH4rxvQB6|qVk zkm8PrqOw#`IeiqDLZ|i7y4Ag6m0Go0tzs4C-HEuJ*MB_c^}K(aa{~Fzy}$2%-*@l) zAXy$65$fXP>4YGNOL$mNH2faUyd3+&&ofouwIN8~_Jy&tEzwvu%V;tX8ZFMU%MqU`r$#l z__K2=mfi_FGylYin|Ckq`2{&nXFC%fh^9O|8sMXU(CM+no;W(|-(`NeeU=`+QQ6hD z^X|&F#i66_R!k|j{Ly!~p`6tJ#jUut_{P$fqkG~?^7cIa#Pxo6&ZMG@-Nh}5_b<#j zW&ip1jF|n|4_)j>By*3~=U+c?E3zpm;9I-)^LCGyDJ1m~1;h7FjP{JxwELx34t#gr z_(|U8G?_JFgLC$BJL#|b`m-OMT0MZ0UvW1+{<2_I=C}KjLnSMvj%#K_hKzsuRD3%y zIOF!}@r92cy5G7watB{EX8)ovJGaClX>Ch$(Zi?b4>-Biw|wU@V%NTMpVD^sq)Dr; zA&IQ?jcGN-Mbqt)^BY`_twugtAGUIEa9S3-OVGE%DcB>t#l>y4hwJQCpEXFt^(M)6 z>Bw4bZf#T@k{>?UK@>J_-ZpRTvQcMN4&OO$J5fS$lJ=F3)zq$CQ#t49&K1>VEX5BLY1xjE}*8BTg{*xPe*a*?MZ zvZHSmr_*=-`kOtG0f#o6JvLz3zQJyfK1KSJ`VxZAe3rUgJT`G?NZjz2Y3HvDIJe9$ zp!~>}oyYFQK#%25! z*05_``Ym>Sef|6OP{WtTt815!G5hqN+SVdqH*GYCYpSY#K2o^gVdIM6ixr7qZU4D$30=MU(p-s0 zN)~yue*Tt2!`O$*uCP{VHs&4Wc-JNHS~5Qzc%^YtuAS4s>xA=@5evLhR<%zK{rQPt z@4P>{gtf-6Ix%Zgjv2$R;Vx0U<V5hp!GbnBMJ3iT3Vq zpW<~xHQez|pFekY%M!0ScK+0#TT@nA>yUg0>7MzC zwyow`<%Dade?9S79MiwEso%lVhc9_PkuFA}PtLm9Un;c^`D1OLqc$!A9VE=yT zD>h4`RlA&PFRz|V{4JMo=;)=L@POw#zC1g)_V)>cWggiPZq!$8K3U<21VwCfE z+1c6LY?Ny-rSSwZnT*F5@`OSTWN^$mdJAUd=*?poh-Vlp4H2QK@_ZuQ&I)0DkbSn2{&o@_9NP@6{bP4P0)1&p=d zeI*k;6=U@65;G3!bf>Jpx*dEyQBk z##WB0o)s};S-9F(2=+Y-snRih8V=8Q??64julc)X;Zj1JDof?ad@*R2T0n55skoSf z2?a6<;ma2gQv7vvvw^T=VQ^jW|z~;4?MUY{tL>6k2AB&j$MYY zq>#uNvB90gr*Q07Jv=VfCZZ&n`Y`T7lEQAic%;_ zGi6~#5GX;S1ceeb!vcaNfs!OjQYc9?q#&0fP>Mt;3Z-b^2wPE_Kxq=CDU^n)z#71S zG_V9(5QD;i4bXrEn`uT6vxbW#2#O$RMjw=g6-g2#MUXTj7g#`$A_qoQDaa*g zlAtMq26^BI(1hJ!2j~Y9pb%IC7?1{*kP1AYFkk~TV9|_oOc>U1Q6xz-MuI*l3oD8u zDH?nQxxfN~Bu$Yt_y|se0l*8=!DvtmR)QZu6Ly0gzzGC{LSPMGKpJ)eE#Lu#0UMxc zrbSFPgkcRA4IKj`K_8Tb6-Cob18wbRSlBws^wZWPhLp{BroA>R8ICr0m~PtyGn8!_ znC9DPF$!#z02;;tx&-EdCMdNhPU!{XZ)b^)h(V@YKMVas>{6$P1|iG(jSPduJI1gC zGlC3kVP1B}4Lj6u)4>w140gC~-^Xbr`d&u;4!9K_9uyGkeWGc#tPjF+Z0VvaJ2rn4 z6@u5cBByViK^~EhTismrERP881!YI;gF|Co7xiyGQcc#J+v;&*M@89=vGwOm*6-@R YH_=^JhIesEHh?1G!4W|6tE9qcStimek95D-{t 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

L!>Fwj>Zb|2x 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 literal 0 HcmV?d00001 diff --git a/sdrgui/resources/mimo.png b/sdrgui/resources/mimo.png new file mode 100644 index 0000000000000000000000000000000000000000..f44b7752ce8d9ae33fcb172ecda842d368e51a38 GIT binary patch literal 4992 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.png zoomin.png zoomout.png + rx.png + tx.png + mimo.png + tool.png + gear.png + corner_topleft.png + corner_topright.png + corner_botleft.png + corner_botright.png + cross.png + cascade.png + tiles.png + dock.png + help.png + shrink.png + exit.png LiberationMono-Regular.ttf LiberationSans-Regular.ttf diff --git a/sdrgui/resources/rx.png b/sdrgui/resources/rx.png new file mode 100644 index 0000000000000000000000000000000000000000..348aa693ef5138ec383102556a960395601fead0 GIT binary patch literal 5009 zcmeHLc~}$I77zPUpemOvob3qymV@Ly-cZEcP}!~P#FdezX) z$9C5T)s=3Ww%^{Q?bsalQDV=a-aoD0tGX@VS>m!k_PndJ`egT#&mYksA z@au#OKfmU#^_wEHR+!!pocKL?-(#DY2%v9wZbWvbkN2$`f2G@8;rz;dW^Hz@q2|{@ zW&2~*>ZJHHq%(q0;?k`c2e{i0Otzm3u2lQ%{upk(waC%*_sFsvuJuP5-nz8`J+n6w zM}DyKoR(Xn8mTD%L%6~;pWmU9ZB0M-WGCfj`3?6IcXu>?woU6+TkA!t97#OCuc*!9 zn7w=PvV;)Yp)Z>6RqfOtxv$uHsA~$lC?wDM{V$95L{|yn;&m>G^ZhRZC9bEx)zZjU zXIKsv#Z{b~72Lu(k-9T}Xa5za%eTSDnEj<6V2=d4NrA)Y$ho2gIkyrQmwS#*^?uap z?e)uXiSow=Q+HwkaZlVo9cXPT_bH07$pYSF;e#VONIMl>A}yB8*s?s^ac zR~?+(y&*eio3Mx;46}$GJ>M^|*g!({W)_vFrY30|PXb*TXV=j}T0!sWOUo7lfip=P z-OOXvThtBE^90SmD{6KLV;i@(O!;P^=G1m=^C5@!rIM59zbiR7xt3g>$ZtJpMM-+c z>HEGtGo8GX!oHroISz;~_?EgS?WU-?X<#e+N|yIx#l-;7S8;IR@DE|NJ6f@_NqpVi zYvJ{|md(APf_c+eSw)c^&6kTxs=NIaqdtw->LNMG%i<4ryL?OTRIo|6x50Hi(5_%G&!5)qVL_UENxwKhCetZY81To zNJ93x^UPceMLx9AKFD~Sz3=d078rK%!$s(Ul`a+oR=ZEFpMF@lq`0r$L2vRje2#@z zsIYXwHvv~pF#5bhdn*#zK$}z_H)j@H`K~f4aH_g2?z=}vF6<>WhD839R=B9T9hYXs z+IsZw<=U4C8`Zy=4PUxIG^szNaCI>M`ioLV z;pY#Nk}nCjZGES8gDkdh^l4xJ;Hjj{-J)U#KPebdFP@} zg-)B4QL@S}Dr39x+lc@w(O*NF~An^*GOf&sGyK>2>gprOvW%Z zk3!MubYvZatkTFSG%lA*q0%XII*4n4+6*NI=|QEIWPljM2tu^5232FIN(mS+A&Dvt z;}eOv9C+cMLM;-#gjZ_ES-|S`Iy{3<$ zvR7oi=9@uc3}>PvxVe}3uW7%C-KdOfiA2006`W=;Dh%Qi4gT_^Dj1dWjIT1OFP#BF zT#&(}vp^=5Mgt)Z7Xcv#470dY78|BQ6QG1jEe0uJ!~lhZlTjRp#z43-29*YKq_8i@ zlu}_3qC*nUSH^}QD&kA!&{-287HCksDxuVgUKyaIIFv7oE0wZYY>>lcNkArBLIWi* zf`Du)LnfgkTn?MTHbNQlz?&-;@`-dZ_0@7mPTD;&4m^3Px&NNP!!n_b15yA@xRX}otqE^a{0z+DOcrZAz zkfBa-WaBs<3s0ayAWWr+QK?e-M1!w@0n%9Bfd5!hLQyTQkzuI#@tTiDJ{|iSTLP)5 zQ3L?SvgJYWm=P_Mj!2D$@Ofh*I2lsP5qy1*7u1V!^ly^Iq0;G628|9PY!03*8NvZ2 zz6>siNazsEV#v50#($w}RWeKmX^^>cJW@Owynu|+0G>vUydP~#J7MwjiY4G*Hk{{`vr?aclI zK1_ToWGV@t7eqK+?Z+`3?%1e97vg6*?@Th>CWP%}hEpCZOehMrx@9)q+KPC=eV`5B zi>3;L=EiVNZz*aIFOB>l|4RxgnX~6ZpFM)Ly>=gmx7_KCWMX@<-((w8iQQFsR$P~XreM7>Z*HF3MtfA9o&hJpw5d zvV1n|;4E+J?Cy9ne?PyjrUkfBF?z0G^~gnsk-s*UFN&|N?pRr4vC(q5eO0lm`@e!? zi#rP9@1%Fde!bB?ck9Xj6g4xi<73ae}9*~Yy&a0V1QdJfbuGhBqE*J!&zI*pce|TNN i{<9UX$ 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 literal 0 HcmV?d00001 From 44a90939ef6011a90fd56ca7649c09853d56c2cc Mon Sep 17 00:00:00 2001 From: f4exb Date: Mon, 4 Apr 2022 16:13:42 +0200 Subject: [PATCH 002/115] Massive UI revamping (v7): commands dialog --- sdrbase/commands/command.h | 2 +- sdrbase/maincore.h | 1 + sdrgui/CMakeLists.txt | 3 + sdrgui/gui/commandsdialog.cpp | 318 ++++++++++++++++++++++++++++++++++ sdrgui/gui/commandsdialog.h | 68 ++++++++ sdrgui/gui/commandsdialog.ui | 252 +++++++++++++++++++++++++++ sdrgui/mainwindow.cpp | 291 +++++-------------------------- sdrgui/mainwindow.h | 9 +- 8 files changed, 686 insertions(+), 258 deletions(-) create mode 100644 sdrgui/gui/commandsdialog.cpp create mode 100644 sdrgui/gui/commandsdialog.h create mode 100644 sdrgui/gui/commandsdialog.ui diff --git a/sdrbase/commands/command.h b/sdrbase/commands/command.h index 194f99d3f..8010b2360 100644 --- a/sdrbase/commands/command.h +++ b/sdrbase/commands/command.h @@ -58,7 +58,7 @@ public: bool getRelease() const { return m_release; } QString getKeyLabel() const; - void run(const QString& apiAddress, int apiPort, int deviceSetIndex); + void run(const QString& apiAddress, int apiPort, int deviceSetIndex = 0); void kill(); QProcess::ProcessState getLastProcessState() const; bool getLastProcessError(QProcess::ProcessError& error) const; diff --git a/sdrbase/maincore.h b/sdrbase/maincore.h index 37ca9d563..1ed670e70 100644 --- a/sdrbase/maincore.h +++ b/sdrbase/maincore.h @@ -742,6 +742,7 @@ public: friend class MainServer; friend class MainWindow; friend class WebAPIAdapter; + friend class CommandsDialog; signals: void deviceSetAdded(int index, DeviceAPI *device); diff --git a/sdrgui/CMakeLists.txt b/sdrgui/CMakeLists.txt index 2b8a85c4a..3a58139d0 100644 --- a/sdrgui/CMakeLists.txt +++ b/sdrgui/CMakeLists.txt @@ -21,6 +21,7 @@ set(sdrgui_SOURCES gui/clickablelabel.cpp gui/colormapper.cpp gui/commanditem.cpp + gui/commandsdialog.cpp gui/commandoutputdialog.cpp gui/crightclickenabler.cpp gui/customtextedit.cpp @@ -117,6 +118,7 @@ set(sdrgui_HEADERS gui/channelwindow.h gui/colormapper.h gui/commanditem.h + gui/commandsdialog.h gui/commandoutputdialog.h gui/crightclickenabler.h gui/customtextedit.h @@ -208,6 +210,7 @@ set(sdrgui_FORMS gui/basicdevicesettingsdialog.ui gui/basicfeaturesettingsdialog.ui gui/channeladddialog.ui + gui/commandsdialog.ui gui/commandoutputdialog.ui gui/cwkeyergui.ui gui/devicestreamselectiondialog.ui diff --git a/sdrgui/gui/commandsdialog.cpp b/sdrgui/gui/commandsdialog.cpp new file mode 100644 index 000000000..6efab2cb5 --- /dev/null +++ b/sdrgui/gui/commandsdialog.cpp @@ -0,0 +1,318 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2022 F4EXB // +// written by Edouard Griffiths // +// // +// This program 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 as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program 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 V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#include + +#include "commands/command.h" +#include "commands/commandkeyreceiver.h" +#include "editcommanddialog.h" +#include "addpresetdialog.h" +#include "commandoutputdialog.h" +#include "commanditem.h" +#include "maincore.h" +#include "mainwindow.h" + +#include "commandsdialog.h" +#include "ui_commandsdialog.h" + +CommandsDialog::CommandsDialog(QWidget* parent) : + QDialog(parent), + ui(new Ui::CommandsDialog), + m_apiHost("127.0.0.1"), + m_apiPort(8091), + m_commandKeyReceiver(nullptr) +{ + ui->setupUi(this); + ui->commandKeyboardConnect->hide(); // FIXME +} + +CommandsDialog::~CommandsDialog() +{ + delete ui; +} + +void CommandsDialog::populateTree() +{ + MainCore::instance()->m_settings.sortCommands(); + ui->commandTree->clear(); + QTreeWidgetItem *treeItem; + + for (int i = 0; i < MainCore::instance()->m_settings.getCommandCount(); ++i) { + treeItem = addCommandToTree(MainCore::instance()->m_settings.getCommand(i)); + } +} + +void CommandsDialog::on_commandNew_clicked() +{ + QStringList groups; + QString group = ""; + QString description = ""; + + for(int i = 0; i < ui->commandTree->topLevelItemCount(); i++) { + groups.append(ui->commandTree->topLevelItem(i)->text(0)); + } + + QTreeWidgetItem* item = ui->commandTree->currentItem(); + + if(item != 0) + { + if(item->type() == PGroup) { + group = item->text(0); + } else if(item->type() == PItem) { + group = item->parent()->text(0); + description = item->text(0); + } + } + + Command *command = new Command(); + command->setGroup(group); + command->setDescription(description); + EditCommandDialog editCommandDialog(groups, group, this); + editCommandDialog.fromCommand(*command); + + if (editCommandDialog.exec() == QDialog::Accepted) + { + editCommandDialog.toCommand(*command); + MainCore::instance()->m_settings.addCommand(command); + ui->commandTree->setCurrentItem(addCommandToTree(command)); + MainCore::instance()->m_settings.sortCommands(); + } +} + +void CommandsDialog::on_commandDuplicate_clicked() +{ + QTreeWidgetItem* item = ui->commandTree->currentItem(); + const Command* command = qvariant_cast(item->data(0, Qt::UserRole)); + Command *commandCopy = new Command(*command); + MainCore::instance()->m_settings.addCommand(commandCopy); + ui->commandTree->setCurrentItem(addCommandToTree(commandCopy)); + MainCore::instance()->m_settings.sortCommands(); +} + +void CommandsDialog::on_commandEdit_clicked() +{ + QTreeWidgetItem* item = ui->commandTree->currentItem(); + bool change = false; + const Command *changedCommand = 0; + QString newGroupName; + + QStringList groups; + + for(int i = 0; i < ui->commandTree->topLevelItemCount(); i++) { + groups.append(ui->commandTree->topLevelItem(i)->text(0)); + } + + if(item != 0) + { + if (item->type() == PItem) + { + const Command* command = qvariant_cast(item->data(0, Qt::UserRole)); + + if (command != 0) + { + EditCommandDialog editCommandDialog(groups, command->getGroup(), this); + editCommandDialog.fromCommand(*command); + + if (editCommandDialog.exec() == QDialog::Accepted) + { + Command* command_mod = const_cast(command); + editCommandDialog.toCommand(*command_mod); + change = true; + changedCommand = command; + } + } + } + else if (item->type() == PGroup) + { + AddPresetDialog dlg(groups, item->text(0), this); + dlg.showGroupOnly(); + dlg.setDialogTitle("Edit command group"); + dlg.setDescriptionBoxTitle("Command details"); + + if (dlg.exec() == QDialog::Accepted) + { + MainCore::instance()->m_settings.renameCommandGroup(item->text(0), dlg.group()); + newGroupName = dlg.group(); + change = true; + } + } + } + + if (change) + { + MainCore::instance()->m_settings.sortCommands(); + ui->commandTree->clear(); + + for (int i = 0; i < MainCore::instance()->m_settings.getCommandCount(); ++i) + { + QTreeWidgetItem *item_x = addCommandToTree(MainCore::instance()->m_settings.getCommand(i)); + const Command* command_x = qvariant_cast(item_x->data(0, Qt::UserRole)); + if (changedCommand && (command_x == changedCommand)) { // set cursor on changed command + ui->commandTree->setCurrentItem(item_x); + } + } + + if (!changedCommand) // on group name change set cursor on the group that has been changed + { + for(int i = 0; i < ui->commandTree->topLevelItemCount(); i++) + { + QTreeWidgetItem* item = ui->commandTree->topLevelItem(i); + + if (item->text(0) == newGroupName) { + ui->commandTree->setCurrentItem(item); + } + } + } + } +} + +void CommandsDialog::on_commandRun_clicked() +{ + QTreeWidgetItem* item = ui->commandTree->currentItem(); + + if (item) + { + if (item->type() == PItem) // run individual command + { + const Command* command = qvariant_cast(item->data(0, Qt::UserRole)); + Command* command_mod = const_cast(command); + command_mod->run(m_apiHost, m_apiPort); + } + else if (item->type() == PGroup) // run all commands in this group + { + QString group = item->text(0); + + for (int i = 0; i < MainCore::instance()->m_settings.getCommandCount(); ++i) + { + Command *command_mod = const_cast(MainCore::instance()->m_settings.getCommand(i)); + + if (command_mod->getGroup() == group) { + command_mod->run(m_apiHost, m_apiPort); + } + } + } + } +} + +void CommandsDialog::on_commandOutput_clicked() +{ + QTreeWidgetItem* item = ui->commandTree->currentItem(); + + if ((item != 0) && (item->type() == PItem)) + { + const Command* command = qvariant_cast(item->data(0, Qt::UserRole)); + Command* command_mod = const_cast(command); + CommandOutputDialog commandOutputDialog(*command_mod); + commandOutputDialog.exec(); + } +} + +void CommandsDialog::on_commandsSave_clicked() +{ + MainCore::instance()->m_settings.save(); +} + +void CommandsDialog::on_commandDelete_clicked() +{ + QTreeWidgetItem* item = ui->commandTree->currentItem(); + + if (item != 0) + { + if (item->type() == PItem) // delete individual command + { + const Command* command = qvariant_cast(item->data(0, Qt::UserRole)); + + if(command) + { + if (QMessageBox::question(this, + tr("Delete command"), + tr("Do you want to delete command '%1'?") + .arg(command->getDescription()), QMessageBox::No | QMessageBox::Yes, QMessageBox::No) == QMessageBox::Yes) + { + delete item; + MainCore::instance()->m_settings.deleteCommand(command); + } + } + } + else if (item->type() == PGroup) // delete all commands in this group + { + if (QMessageBox::question(this, + tr("Delete command group"), + tr("Do you want to delete command group '%1'?") + .arg(item->text(0)), QMessageBox::No | QMessageBox::Yes, QMessageBox::No) == QMessageBox::Yes) + { + MainCore::instance()->m_settings.deleteCommandGroup(item->text(0)); + + ui->commandTree->clear(); + + for (int i = 0; i < MainCore::instance()->m_settings.getCommandCount(); ++i) { + addCommandToTree(MainCore::instance()->m_settings.getCommand(i)); + } + } + } + } +} + +void CommandsDialog::on_commandKeyboardConnect_toggled(bool checked) +{ + qDebug("CommandsDialog::on_commandKeyboardConnect_toggled: %s", checked ? "true" : "false"); + + if (checked) { + MainWindow::getInstance()->commandKeysConnect(MainWindow::getInstance(), SLOT(commandKeyPressed(Qt::Key, Qt::KeyboardModifiers, bool))); + } else { + MainWindow::getInstance()->commandKeysDisconnect(MainWindow::getInstance(), SLOT(commandKeyPressed(Qt::Key, Qt::KeyboardModifiers, bool))); + } +} + +QTreeWidgetItem* CommandsDialog::addCommandToTree(const Command* command) +{ + QTreeWidgetItem* group = 0; + + for(int i = 0; i < ui->commandTree->topLevelItemCount(); i++) + { + if(ui->commandTree->topLevelItem(i)->text(0) == command->getGroup()) + { + group = ui->commandTree->topLevelItem(i); + break; + } + } + + if(group == 0) + { + QStringList sl; + sl.append(command->getGroup()); + group = new QTreeWidgetItem(ui->commandTree, sl, PGroup); + group->setFirstColumnSpanned(true); + group->setExpanded(true); + ui->commandTree->sortByColumn(0, Qt::AscendingOrder); + } + + QStringList sl; + sl.append(QString("%1").arg(command->getDescription())); // Descriptions column + sl.append(QString("%1").arg(command->getAssociateKey() ? command->getRelease() ? "R" : "P" : "-")); // key press/release column + sl.append(QString("%1").arg(command->getKeyLabel())); // key column + CommandItem* item = new CommandItem(group, sl, command->getDescription(), PItem); + item->setData(0, Qt::UserRole, QVariant::fromValue(command)); + item->setTextAlignment(0, Qt::AlignLeft); + ui->commandTree->resizeColumnToContents(0); // Resize description column to minimum + ui->commandTree->resizeColumnToContents(1); // Resize key column to minimum + ui->commandTree->resizeColumnToContents(2); // Resize key press/release column to minimum + + //updatePresetControls(); + return item; +} diff --git a/sdrgui/gui/commandsdialog.h b/sdrgui/gui/commandsdialog.h new file mode 100644 index 000000000..508bf97e4 --- /dev/null +++ b/sdrgui/gui/commandsdialog.h @@ -0,0 +1,68 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2022 F4EXB // +// written by Edouard Griffiths // +// // +// This program 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 as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program 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 V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef SDRGUI_GUI_COMMANDSDIALOG_H_ +#define SDRGUI_GUI_COMMANDSDIALOG_H_ + +#include + +#include "export.h" + +class CommandKeyReceiver; + +namespace Ui { + class CommandsDialog; +} + +class SDRGUI_API CommandsDialog : public QDialog +{ + Q_OBJECT +public: + explicit CommandsDialog(QWidget* parent = nullptr); + ~CommandsDialog(); + + void setApiHost(const QString& apiHost) { m_apiHost = apiHost; } + void setApiPort(int apiPort) { m_apiPort = apiPort; } + void setCommandKeyReceiver(CommandKeyReceiver *commandKeyReceiver) { m_commandKeyReceiver = commandKeyReceiver; } + void populateTree(); + +private: + enum { + PGroup, + PItem + }; + + Ui::CommandsDialog* ui; + QString m_apiHost; + int m_apiPort; + CommandKeyReceiver *m_commandKeyReceiver; + + QTreeWidgetItem* addCommandToTree(const Command* command); + +private slots: + void on_commandNew_clicked(); + void on_commandDuplicate_clicked(); + void on_commandEdit_clicked(); + void on_commandRun_clicked(); + void on_commandOutput_clicked(); + void on_commandsSave_clicked(); + void on_commandDelete_clicked(); + void on_commandKeyboardConnect_toggled(bool checked); +}; + +#endif // SDRGUI_GUI_COMMANDSDIALOG_H_ diff --git a/sdrgui/gui/commandsdialog.ui b/sdrgui/gui/commandsdialog.ui new file mode 100644 index 000000000..10e606ee3 --- /dev/null +++ b/sdrgui/gui/commandsdialog.ui @@ -0,0 +1,252 @@ + + + CommandsDialog + + + + 0 + 0 + 400 + 300 + + + + + Liberation Sans + 9 + + + + Commands + + + + + + 5 + + + true + + + 3 + + + 5 + + + + Description + + + + + P/R + + + + + Key + + + + + + + + + + Create new command + + + + + + + :/create.png:/create.png + + + + + + + Duplicate command + + + + + + + :/duplicate.png:/duplicate.png + + + + + + + Edit command details + + + + + + + :/edit.png:/edit.png + + + + + + + Run command + + + + + + + :/play.png:/play.png + + + + + + + View last run status and output + + + + + + + :/listing.png:/listing.png + + + + + + + Save commands in settings + + + + + + + :/save.png:/save.png + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Delete selected command + + + + + + + :/bin.png:/bin.png + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Toggle keyboard to command connection + + + + + + + :/keyboard.png:/keyboard.png + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Close + + + + + + + + ButtonSwitch + QToolButton +

gui/buttonswitch.h
+ + + + + + + + buttonBox + accepted() + CommandsDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + CommandsDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + +
diff --git a/sdrgui/mainwindow.cpp b/sdrgui/mainwindow.cpp index 595067bdd..a98ae3a54 100644 --- a/sdrgui/mainwindow.cpp +++ b/sdrgui/mainwindow.cpp @@ -30,6 +30,7 @@ #include #include #include +#include #include #include @@ -49,10 +50,7 @@ #include "commands/commandkeyreceiver.h" #include "gui/indicator.h" #include "gui/presetitem.h" -#include "gui/commanditem.h" #include "gui/addpresetdialog.h" -#include "gui/editcommanddialog.h" -#include "gui/commandoutputdialog.h" #include "gui/pluginsdialog.h" #include "gui/aboutdialog.h" #include "gui/rollupwidget.h" @@ -67,6 +65,7 @@ #include "gui/ambedevicesdialog.h" #include "gui/workspace.h" #include "gui/featurepresetsdialog.h" +#include "gui/commandsdialog.h" #include "dsp/dspengine.h" #include "dsp/spectrumvis.h" #include "dsp/dspcommands.h" @@ -975,6 +974,9 @@ void MainWindow::createMenuBar() QAction *userArgumentsAction = devicesMenu->addAction("User arguments"); userArgumentsAction->setToolTip("Device custom user arguments"); QObject::connect(userArgumentsAction, &QAction::triggered, this, &MainWindow::on_action_DeviceUserArguments_triggered); + QAction *commandsAction = preferencesMenu->addAction("Commands"); + commandsAction->setToolTip("External commands dialog"); + QObject::connect(commandsAction, &QAction::triggered, this, &MainWindow::on_action_commands_triggered); QMenu *helpMenu = menuBar->addMenu("Help"); QAction *quickStartAction = helpMenu->addAction("Quick start"); @@ -1427,245 +1429,25 @@ void MainWindow::on_action_View_Fullscreen_toggled(bool checked) } } -void MainWindow::on_commandNew_clicked() -{ - // QStringList groups; - // QString group = ""; - // QString description = ""; - - // for(int i = 0; i < ui->commandTree->topLevelItemCount(); i++) { - // groups.append(ui->commandTree->topLevelItem(i)->text(0)); - // } - - // QTreeWidgetItem* item = ui->commandTree->currentItem(); - - // if(item != 0) - // { - // if(item->type() == PGroup) { - // group = item->text(0); - // } else if(item->type() == PItem) { - // group = item->parent()->text(0); - // description = item->text(0); - // } - // } - - // Command *command = new Command(); - // command->setGroup(group); - // command->setDescription(description); - // EditCommandDialog editCommandDialog(groups, group, this); - // editCommandDialog.fromCommand(*command); - - // if (editCommandDialog.exec() == QDialog::Accepted) - // { - // editCommandDialog.toCommand(*command); - // m_mainCore->m_settings.addCommand(command); - // ui->commandTree->setCurrentItem(addCommandToTree(command)); - // m_mainCore->m_settings.sortCommands(); - // } -} - -void MainWindow::on_commandDuplicate_clicked() -{ - // QTreeWidgetItem* item = ui->commandTree->currentItem(); - // const Command* command = qvariant_cast(item->data(0, Qt::UserRole)); - // Command *commandCopy = new Command(*command); - // m_mainCore->m_settings.addCommand(commandCopy); - // ui->commandTree->setCurrentItem(addCommandToTree(commandCopy)); - // m_mainCore->m_settings.sortCommands(); -} - -void MainWindow::on_commandEdit_clicked() -{ - // QTreeWidgetItem* item = ui->commandTree->currentItem(); - // bool change = false; - // const Command *changedCommand = 0; - // QString newGroupName; - - // QStringList groups; - - // for(int i = 0; i < ui->commandTree->topLevelItemCount(); i++) { - // groups.append(ui->commandTree->topLevelItem(i)->text(0)); - // } - - // if(item != 0) - // { - // if (item->type() == PItem) - // { - // const Command* command = qvariant_cast(item->data(0, Qt::UserRole)); - - // if (command != 0) - // { - // EditCommandDialog editCommandDialog(groups, command->getGroup(), this); - // editCommandDialog.fromCommand(*command); - - // if (editCommandDialog.exec() == QDialog::Accepted) - // { - // Command* command_mod = const_cast(command); - // editCommandDialog.toCommand(*command_mod); - // change = true; - // changedCommand = command; - // } - // } - // } - // else if (item->type() == PGroup) - // { - // AddPresetDialog dlg(groups, item->text(0), this); - // dlg.showGroupOnly(); - // dlg.setDialogTitle("Edit command group"); - // dlg.setDescriptionBoxTitle("Command details"); - - // if (dlg.exec() == QDialog::Accepted) - // { - // m_mainCore->m_settings.renameCommandGroup(item->text(0), dlg.group()); - // newGroupName = dlg.group(); - // change = true; - // } - // } - // } - - // if (change) - // { - // m_mainCore->m_settings.sortCommands(); - // ui->commandTree->clear(); - - // for (int i = 0; i < m_mainCore->m_settings.getCommandCount(); ++i) - // { - // QTreeWidgetItem *item_x = addCommandToTree(m_mainCore->m_settings.getCommand(i)); - // const Command* command_x = qvariant_cast(item_x->data(0, Qt::UserRole)); - // if (changedCommand && (command_x == changedCommand)) { // set cursor on changed command - // ui->commandTree->setCurrentItem(item_x); - // } - // } - - // if (!changedCommand) // on group name change set cursor on the group that has been changed - // { - // for(int i = 0; i < ui->commandTree->topLevelItemCount(); i++) - // { - // QTreeWidgetItem* item = ui->commandTree->topLevelItem(i); - - // if (item->text(0) == newGroupName) { - // ui->commandTree->setCurrentItem(item); - // } - // } - // } - // } -} - -void MainWindow::on_commandDelete_clicked() -{ - // QTreeWidgetItem* item = ui->commandTree->currentItem(); - - // if (item != 0) - // { - // if (item->type() == PItem) // delete individual command - // { - // const Command* command = qvariant_cast(item->data(0, Qt::UserRole)); - - // if(command) - // { - // if (QMessageBox::question(this, - // tr("Delete command"), - // tr("Do you want to delete command '%1'?") - // .arg(command->getDescription()), QMessageBox::No | QMessageBox::Yes, QMessageBox::No) == QMessageBox::Yes) - // { - // delete item; - // m_mainCore->m_settings.deleteCommand(command); - // } - // } - // } - // else if (item->type() == PGroup) // delete all commands in this group - // { - // if (QMessageBox::question(this, - // tr("Delete command group"), - // tr("Do you want to delete command group '%1'?") - // .arg(item->text(0)), QMessageBox::No | QMessageBox::Yes, QMessageBox::No) == QMessageBox::Yes) - // { - // m_mainCore->m_settings.deleteCommandGroup(item->text(0)); - - // ui->commandTree->clear(); - - // for (int i = 0; i < m_mainCore->m_settings.getCommandCount(); ++i) { - // addCommandToTree(m_mainCore->m_settings.getCommand(i)); - // } - // } - // } - // } -} - -void MainWindow::on_commandRun_clicked() -{ - // QTreeWidgetItem* item = ui->commandTree->currentItem(); - - // if (item != 0) - // { - // int currentDeviceSetIndex = ui->tabInputsView->currentIndex(); - - // if (item->type() == PItem) // run individual command - // { - // const Command* command = qvariant_cast(item->data(0, Qt::UserRole)); - // Command* command_mod = const_cast(command); - // command_mod->run(m_apiServer->getHost(), m_apiServer->getPort(), currentDeviceSetIndex); - // } - // else if (item->type() == PGroup) // run all commands in this group - // { - // QString group = item->text(0); - - // for (int i = 0; i < m_mainCore->m_settings.getCommandCount(); ++i) - // { - // Command *command_mod = const_cast(m_mainCore->m_settings.getCommand(i)); - - // if (command_mod->getGroup() == group) { - // command_mod->run(m_apiServer->getHost(), m_apiServer->getPort(), currentDeviceSetIndex); - // } - // } - // } - // } -} - -void MainWindow::on_commandOutput_clicked() -{ - // QTreeWidgetItem* item = ui->commandTree->currentItem(); - - // if ((item != 0) && (item->type() == PItem)) - // { - // const Command* command = qvariant_cast(item->data(0, Qt::UserRole)); - // Command* command_mod = const_cast(command); - // CommandOutputDialog commandOutputDialog(*command_mod); - // commandOutputDialog.exec(); - // } -} - -void MainWindow::on_commandsSave_clicked() -{ - saveCommandSettings(); - m_mainCore->m_settings.save(); -} - void MainWindow::commandKeysConnect(QObject *object, const char *slot) { setFocus(); - connect(m_commandKeyReceiver, SIGNAL(capturedKey(Qt::Key, Qt::KeyboardModifiers, bool)), - object, slot); + connect( + m_commandKeyReceiver, + SIGNAL(capturedKey(Qt::Key, Qt::KeyboardModifiers, bool)), + object, + slot + ); } void MainWindow::commandKeysDisconnect(QObject *object, const char *slot) { - disconnect(m_commandKeyReceiver, SIGNAL(capturedKey(Qt::Key, Qt::KeyboardModifiers, bool)), - object, slot); -} - -void MainWindow::on_commandKeyboardConnect_toggled(bool checked) -{ - qDebug("on_commandKeyboardConnect_toggled: %s", checked ? "true" : "false"); - - if (checked) - { - commandKeysConnect(this, SLOT(commandKeyPressed(Qt::Key, Qt::KeyboardModifiers, bool))); - } - else - { - commandKeysDisconnect(this, SLOT(commandKeyPressed(Qt::Key, Qt::KeyboardModifiers, bool))); - } + disconnect( + m_commandKeyReceiver, + SIGNAL(capturedKey(Qt::Key, Qt::KeyboardModifiers, bool)), + object, + slot + ); } void MainWindow::on_presetSave_clicked() @@ -2021,6 +1803,17 @@ void MainWindow::on_action_DeviceUserArguments_triggered() deviceUserArgsDialog.exec(); } +void MainWindow::on_action_commands_triggered() +{ + qDebug("MainWindow::on_action_commands_triggered"); + CommandsDialog commandsDialog(this); + commandsDialog.setApiHost(m_apiServer->getHost()); + commandsDialog.setApiPort(m_apiServer->getPort()); + commandsDialog.setCommandKeyReceiver(m_commandKeyReceiver); + commandsDialog.populateTree(); + commandsDialog.exec(); +} + void MainWindow::on_action_FFT_triggered() { qDebug("MainWindow::on_action_FFT_triggered"); @@ -2659,22 +2452,22 @@ void MainWindow::updateStatus() void MainWindow::commandKeyPressed(Qt::Key key, Qt::KeyboardModifiers keyModifiers, bool release) { - //qDebug("MainWindow::commandKeyPressed: key: %x mod: %x %s", (int) key, (int) keyModifiers, release ? "release" : "press"); - // int currentDeviceSetIndex = ui->tabInputsView->currentIndex(); + qDebug("MainWindow::commandKeyPressed: key: %x mod: %x %s", (int) key, (int) keyModifiers, release ? "release" : "press"); + int currentDeviceSetIndex = 0; - // for (int i = 0; i < m_mainCore->m_settings.getCommandCount(); ++i) - // { - // const Command* command = m_mainCore->m_settings.getCommand(i); + for (int i = 0; i < m_mainCore->m_settings.getCommandCount(); ++i) + { + const Command* command = m_mainCore->m_settings.getCommand(i); - // if (command->getAssociateKey() - // && (command->getRelease() == release) - // && (command->getKey() == key) - // && (command->getKeyModifiers() == keyModifiers)) - // { - // Command* command_mod = const_cast(command); - // command_mod->run(m_apiServer->getHost(), m_apiServer->getPort(), currentDeviceSetIndex); - // } - // } + if (command->getAssociateKey() + && (command->getRelease() == release) + && (command->getKey() == key) + && (command->getKeyModifiers() == keyModifiers)) + { + Command* command_mod = const_cast(command); + command_mod->run(m_apiServer->getHost(), m_apiServer->getPort(), currentDeviceSetIndex); + } + } } void MainWindow::restoreDeviceTabs() diff --git a/sdrgui/mainwindow.h b/sdrgui/mainwindow.h index 83415be6d..13f077016 100644 --- a/sdrgui/mainwindow.h +++ b/sdrgui/mainwindow.h @@ -174,14 +174,6 @@ private slots: void on_presetDelete_clicked(); void on_presetTree_currentItemChanged(QTreeWidgetItem *current, QTreeWidgetItem *previous); void on_presetTree_itemActivated(QTreeWidgetItem *item, int column); - void on_commandNew_clicked(); - void on_commandDuplicate_clicked(); - void on_commandEdit_clicked(); - void on_commandDelete_clicked(); - void on_commandRun_clicked(); - void on_commandOutput_clicked(); - void on_commandsSave_clicked(); - void on_commandKeyboardConnect_toggled(bool checked); void on_action_Audio_triggered(); void on_action_Logging_triggered(); void on_action_FFT_triggered(); @@ -189,6 +181,7 @@ private slots: void on_action_LimeRFE_triggered(); void on_action_My_Position_triggered(); void on_action_DeviceUserArguments_triggered(); + void on_action_commands_triggered(); void samplingDeviceChanged(int deviceType, int tabIndex, int newDeviceIndex); void channelAddClicked(int channelIndex); void featureAddClicked(Workspace *workspace, int featureIndex); From 43f53fe26af593817d0f8ece0852a28a1d978a94 Mon Sep 17 00:00:00 2001 From: f4exb Date: Tue, 5 Apr 2022 16:26:57 +0200 Subject: [PATCH 003/115] Massive UI revamping (v7): created configurations with just Features for now --- doc/img/hide.xcf | Bin 0 -> 13160 bytes plugins/feature/afc/afcgui.h | 4 + plugins/feature/afc/afcsettings.cpp | 7 + plugins/feature/afc/afcsettings.h | 2 + plugins/feature/ais/aisgui.h | 4 + plugins/feature/ais/aissettings.cpp | 7 + plugins/feature/ais/aissettings.h | 2 + .../feature/antennatools/antennatoolsgui.h | 4 + .../antennatools/antennatoolssettings.cpp | 7 + .../antennatools/antennatoolssettings.h | 2 + plugins/feature/aprs/aprsgui.h | 4 + plugins/feature/aprs/aprssettings.cpp | 7 + plugins/feature/aprs/aprssettings.h | 2 + .../feature/demodanalyzer/demodanalyzergui.h | 4 + .../demodanalyzer/demodanalyzersettings.cpp | 7 + .../demodanalyzer/demodanalyzersettings.h | 2 + .../gs232controller/gs232controllergui.h | 4 + .../gs232controllersettings.cpp | 7 + .../gs232controller/gs232controllersettings.h | 2 + .../jogdialcontroller/jogdialcontrollergui.h | 4 + .../jogdialcontrollersettings.cpp | 7 + .../jogdialcontrollersettings.h | 2 + plugins/feature/map/mapgui.h | 4 + plugins/feature/map/mapsettings.cpp | 5 + plugins/feature/map/mapsettings.h | 2 + plugins/feature/pertester/pertestergui.h | 4 + .../feature/pertester/pertestersettings.cpp | 6 + plugins/feature/pertester/pertestersettings.h | 2 + plugins/feature/radiosonde/radiosondegui.h | 4 + .../feature/radiosonde/radiosondesettings.cpp | 4 + .../feature/radiosonde/radiosondesettings.h | 2 + .../feature/rigctlserver/rigctlservergui.h | 4 + .../rigctlserver/rigctlserversettings.cpp | 7 + .../rigctlserver/rigctlserversettings.h | 2 + .../satellitetracker/satellitetrackergui.h | 4 + .../satellitetrackersettings.cpp | 6 + .../satellitetrackersettings.h | 2 + plugins/feature/simpleptt/simplepttgui.h | 4 + .../feature/simpleptt/simplepttsettings.cpp | 7 +- plugins/feature/simpleptt/simplepttsettings.h | 2 + plugins/feature/startracker/startrackergui.h | 4 + .../startracker/startrackersettings.cpp | 7 + .../feature/startracker/startrackersettings.h | 2 + .../feature/vorlocalizer/vorlocalizergui.h | 4 + .../vorlocalizer/vorlocalizersettings.cpp | 8 +- .../vorlocalizer/vorlocalizersettings.h | 3 +- sdrbase/CMakeLists.txt | 2 + sdrbase/pipes/messagepipeslegacy.cpp | 78 ---- sdrbase/pipes/messagepipeslegacy.h | 59 --- sdrbase/settings/configuration.cpp | 102 +++++ sdrbase/settings/configuration.h | 78 ++++ sdrbase/settings/mainsettings.cpp | 118 ++++- sdrbase/settings/mainsettings.h | 20 +- sdrgui/CMakeLists.txt | 3 + sdrgui/feature/featuregui.cpp | 4 +- sdrgui/feature/featuregui.h | 9 +- sdrgui/feature/featureuiset.cpp | 88 +++- sdrgui/feature/featureuiset.h | 10 +- sdrgui/gui/commandsdialog.cpp | 8 +- sdrgui/gui/commandsdialog.h | 1 - sdrgui/gui/commandsdialog.ui | 17 +- sdrgui/gui/configurationsdialog.cpp | 403 ++++++++++++++++++ sdrgui/gui/configurationsdialog.h | 78 ++++ sdrgui/gui/configurationsdialog.ui | 244 +++++++++++ sdrgui/gui/featurepresetsdialog.cpp | 12 +- sdrgui/gui/featurepresetsdialog.h | 3 +- sdrgui/gui/featurepresetsdialog.ui | 20 - sdrgui/gui/workspace.cpp | 18 +- sdrgui/gui/workspace.h | 3 + sdrgui/mainwindow.cpp | 142 +++++- sdrgui/mainwindow.h | 7 +- sdrgui/resources/hide.png | Bin 0 -> 4867 bytes sdrgui/resources/res.qrc | 1 + 73 files changed, 1496 insertions(+), 222 deletions(-) create mode 100644 doc/img/hide.xcf delete mode 100644 sdrbase/pipes/messagepipeslegacy.cpp delete mode 100644 sdrbase/pipes/messagepipeslegacy.h create mode 100644 sdrbase/settings/configuration.cpp create mode 100644 sdrbase/settings/configuration.h create mode 100644 sdrgui/gui/configurationsdialog.cpp create mode 100644 sdrgui/gui/configurationsdialog.h create mode 100644 sdrgui/gui/configurationsdialog.ui create mode 100644 sdrgui/resources/hide.png diff --git a/doc/img/hide.xcf b/doc/img/hide.xcf new file mode 100644 index 0000000000000000000000000000000000000000..022857dd4ce2a47bf2125af0fd9d117644af4997 GIT binary patch literal 13160 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 literal 0 HcmV?d00001 diff --git a/plugins/feature/afc/afcgui.h b/plugins/feature/afc/afcgui.h index 1b376f9a0..83c73a537 100644 --- a/plugins/feature/afc/afcgui.h +++ b/plugins/feature/afc/afcgui.h @@ -43,6 +43,10 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } + virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; } + virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; } + virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; } + virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; } private: Ui::AFCGUI* ui; diff --git a/plugins/feature/afc/afcsettings.cpp b/plugins/feature/afc/afcsettings.cpp index 7eec426c9..d8dc8eed5 100644 --- a/plugins/feature/afc/afcsettings.cpp +++ b/plugins/feature/afc/afcsettings.cpp @@ -44,6 +44,7 @@ void AFCSettings::resetToDefaults() m_reverseAPIPort = 8888; m_reverseAPIFeatureSetIndex = 0; m_reverseAPIFeatureIndex = 0; + m_workspaceIndex = 0; } QByteArray AFCSettings::serialize() const @@ -69,6 +70,9 @@ QByteArray AFCSettings::serialize() const s.writeBlob(15, m_rollupState->serialize()); } + s.writeS32(16, m_workspaceIndex); + s.writeBlob(17, m_geometryBytes); + return s.final(); } @@ -118,6 +122,9 @@ bool AFCSettings::deserialize(const QByteArray& data) m_rollupState->deserialize(bytetmp); } + d.readS32(16, &m_workspaceIndex, 0); + d.readBlob(17, &m_geometryBytes); + return true; } else diff --git a/plugins/feature/afc/afcsettings.h b/plugins/feature/afc/afcsettings.h index 115cdfebf..fb3d750a4 100644 --- a/plugins/feature/afc/afcsettings.h +++ b/plugins/feature/afc/afcsettings.h @@ -40,6 +40,8 @@ struct AFCSettings uint16_t m_reverseAPIFeatureSetIndex; uint16_t m_reverseAPIFeatureIndex; Serializable *m_rollupState; + int m_workspaceIndex; + QByteArray m_geometryBytes; AFCSettings(); void resetToDefaults(); diff --git a/plugins/feature/ais/aisgui.h b/plugins/feature/ais/aisgui.h index a1e43584a..7bc3651a6 100644 --- a/plugins/feature/ais/aisgui.h +++ b/plugins/feature/ais/aisgui.h @@ -57,6 +57,10 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } + virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; } + virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; } + virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; } + virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; } private: Ui::AISGUI* ui; diff --git a/plugins/feature/ais/aissettings.cpp b/plugins/feature/ais/aissettings.cpp index 7d902d105..bdc7145f9 100644 --- a/plugins/feature/ais/aissettings.cpp +++ b/plugins/feature/ais/aissettings.cpp @@ -47,6 +47,7 @@ void AISSettings::resetToDefaults() m_reverseAPIPort = 8888; m_reverseAPIFeatureSetIndex = 0; m_reverseAPIFeatureIndex = 0; + m_workspaceIndex = 0; for (int i = 0; i < AIS_VESSEL_COLUMNS; i++) { @@ -71,6 +72,9 @@ QByteArray AISSettings::serialize() const s.writeBlob(27, m_rollupState->serialize()); } + s.writeS32(27, m_workspaceIndex); + s.writeBlob(28, m_geometryBytes); + for (int i = 0; i < AIS_VESSEL_COLUMNS; i++) { s.writeS32(300 + i, m_vesselColumnIndexes[i]); } @@ -122,6 +126,9 @@ bool AISSettings::deserialize(const QByteArray& data) m_rollupState->deserialize(bytetmp); } + d.readS32(27, &m_workspaceIndex, 0); + d.readBlob(28, &m_geometryBytes); + for (int i = 0; i < AIS_VESSEL_COLUMNS; i++) { d.readS32(300 + i, &m_vesselColumnIndexes[i], i); } diff --git a/plugins/feature/ais/aissettings.h b/plugins/feature/ais/aissettings.h index d29fe3f84..2cf555800 100644 --- a/plugins/feature/ais/aissettings.h +++ b/plugins/feature/ais/aissettings.h @@ -39,6 +39,8 @@ struct AISSettings uint16_t m_reverseAPIFeatureSetIndex; uint16_t m_reverseAPIFeatureIndex; Serializable *m_rollupState; + int m_workspaceIndex; + QByteArray m_geometryBytes; int m_vesselColumnIndexes[AIS_VESSEL_COLUMNS]; int m_vesselColumnSizes[AIS_VESSEL_COLUMNS]; diff --git a/plugins/feature/antennatools/antennatoolsgui.h b/plugins/feature/antennatools/antennatoolsgui.h index c21c8654b..95823f9a3 100644 --- a/plugins/feature/antennatools/antennatoolsgui.h +++ b/plugins/feature/antennatools/antennatoolsgui.h @@ -46,6 +46,10 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } + virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; } + virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; } + virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; } + virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; } private: Ui::AntennaToolsGUI* ui; diff --git a/plugins/feature/antennatools/antennatoolssettings.cpp b/plugins/feature/antennatools/antennatoolssettings.cpp index 2dd559bbe..2dce3f633 100644 --- a/plugins/feature/antennatools/antennatoolssettings.cpp +++ b/plugins/feature/antennatools/antennatoolssettings.cpp @@ -49,6 +49,7 @@ void AntennaToolsSettings::resetToDefaults() m_reverseAPIPort = 8888; m_reverseAPIFeatureSetIndex = 0; m_reverseAPIFeatureIndex = 0; + m_workspaceIndex = 0; } QByteArray AntennaToolsSettings::serialize() const @@ -78,6 +79,9 @@ QByteArray AntennaToolsSettings::serialize() const s.writeBlob(19, m_rollupState->serialize()); } + s.writeS32(20, m_workspaceIndex); + s.writeBlob(21, m_geometryBytes); + return s.final(); } @@ -131,6 +135,9 @@ bool AntennaToolsSettings::deserialize(const QByteArray& data) m_rollupState->deserialize(bytetmp); } + d.readS32(20, &m_workspaceIndex, 0); + d.readBlob(21, &m_geometryBytes); + return true; } else diff --git a/plugins/feature/antennatools/antennatoolssettings.h b/plugins/feature/antennatools/antennatoolssettings.h index 704f17501..3302cdfd4 100644 --- a/plugins/feature/antennatools/antennatoolssettings.h +++ b/plugins/feature/antennatools/antennatoolssettings.h @@ -51,6 +51,8 @@ struct AntennaToolsSettings uint16_t m_reverseAPIFeatureSetIndex; uint16_t m_reverseAPIFeatureIndex; Serializable *m_rollupState; + int m_workspaceIndex; + QByteArray m_geometryBytes; AntennaToolsSettings(); void resetToDefaults(); diff --git a/plugins/feature/aprs/aprsgui.h b/plugins/feature/aprs/aprsgui.h index 412387164..f1a89d1e5 100644 --- a/plugins/feature/aprs/aprsgui.h +++ b/plugins/feature/aprs/aprsgui.h @@ -105,6 +105,10 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } + virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; } + virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; } + virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; } + virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; } protected: void resizeEvent(QResizeEvent* size); diff --git a/plugins/feature/aprs/aprssettings.cpp b/plugins/feature/aprs/aprssettings.cpp index 5e843f04b..b9b2036af 100644 --- a/plugins/feature/aprs/aprssettings.cpp +++ b/plugins/feature/aprs/aprssettings.cpp @@ -77,6 +77,7 @@ void APRSSettings::resetToDefaults() m_reverseAPIPort = 8888; m_reverseAPIFeatureSetIndex = 0; m_reverseAPIFeatureIndex = 0; + m_workspaceIndex = 0; for (int i = 0; i < APRS_PACKETS_TABLE_COLUMNS; i++) { @@ -143,6 +144,9 @@ QByteArray APRSSettings::serialize() const s.writeBlob(20, m_rollupState->serialize()); } + s.writeS32(21, m_workspaceIndex); + s.writeBlob(22, m_geometryBytes); + for (int i = 0; i < APRS_PACKETS_TABLE_COLUMNS; i++) s.writeS32(100 + i, m_packetsTableColumnIndexes[i]); for (int i = 0; i < APRS_PACKETS_TABLE_COLUMNS; i++) @@ -222,6 +226,9 @@ bool APRSSettings::deserialize(const QByteArray& data) m_rollupState->deserialize(bytetmp); } + d.readS32(21, &m_workspaceIndex, 0); + d.readBlob(22, &m_geometryBytes); + for (int i = 0; i < APRS_PACKETS_TABLE_COLUMNS; i++) d.readS32(100 + i, &m_packetsTableColumnIndexes[i], i); for (int i = 0; i < APRS_PACKETS_TABLE_COLUMNS; i++) diff --git a/plugins/feature/aprs/aprssettings.h b/plugins/feature/aprs/aprssettings.h index 509bdd160..99c58894f 100644 --- a/plugins/feature/aprs/aprssettings.h +++ b/plugins/feature/aprs/aprssettings.h @@ -77,6 +77,8 @@ struct APRSSettings uint16_t m_reverseAPIFeatureSetIndex; uint16_t m_reverseAPIFeatureIndex; Serializable *m_rollupState; + int m_workspaceIndex; + QByteArray m_geometryBytes; int m_packetsTableColumnIndexes[APRS_PACKETS_TABLE_COLUMNS];//!< How the columns are ordered in the table int m_packetsTableColumnSizes[APRS_PACKETS_TABLE_COLUMNS]; //!< Size of the columns in the table diff --git a/plugins/feature/demodanalyzer/demodanalyzergui.h b/plugins/feature/demodanalyzer/demodanalyzergui.h index b11100ecf..e26fddd60 100644 --- a/plugins/feature/demodanalyzer/demodanalyzergui.h +++ b/plugins/feature/demodanalyzer/demodanalyzergui.h @@ -49,6 +49,10 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } + virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; } + virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; } + virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; } + virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; } private: Ui::DemodAnalyzerGUI* ui; diff --git a/plugins/feature/demodanalyzer/demodanalyzersettings.cpp b/plugins/feature/demodanalyzer/demodanalyzersettings.cpp index 12419b2cc..d261e5417 100644 --- a/plugins/feature/demodanalyzer/demodanalyzersettings.cpp +++ b/plugins/feature/demodanalyzer/demodanalyzersettings.cpp @@ -76,6 +76,7 @@ void DemodAnalyzerSettings::resetToDefaults() m_reverseAPIPort = 8888; m_reverseAPIFeatureSetIndex = 0; m_reverseAPIFeatureIndex = 0; + m_workspaceIndex = 0; } QByteArray DemodAnalyzerSettings::serialize() const @@ -103,6 +104,9 @@ QByteArray DemodAnalyzerSettings::serialize() const s.writeBlob(12, m_rollupState->serialize()); } + s.writeS32(13, m_workspaceIndex); + s.writeBlob(14, m_geometryBytes); + return s.final(); } @@ -158,6 +162,9 @@ bool DemodAnalyzerSettings::deserialize(const QByteArray& data) m_rollupState->deserialize(bytetmp); } + d.readS32(13, &m_workspaceIndex, 0); + d.readBlob(14, &m_geometryBytes); + return true; } else diff --git a/plugins/feature/demodanalyzer/demodanalyzersettings.h b/plugins/feature/demodanalyzer/demodanalyzersettings.h index c4ba8516a..d3670d181 100644 --- a/plugins/feature/demodanalyzer/demodanalyzersettings.h +++ b/plugins/feature/demodanalyzer/demodanalyzersettings.h @@ -52,6 +52,8 @@ struct DemodAnalyzerSettings Serializable *m_spectrumGUI; Serializable *m_scopeGUI; Serializable *m_rollupState; + int m_workspaceIndex; + QByteArray m_geometryBytes; DemodAnalyzerSettings(); void resetToDefaults(); diff --git a/plugins/feature/gs232controller/gs232controllergui.h b/plugins/feature/gs232controller/gs232controllergui.h index 1fbd42d03..887d5d783 100644 --- a/plugins/feature/gs232controller/gs232controllergui.h +++ b/plugins/feature/gs232controller/gs232controllergui.h @@ -45,6 +45,10 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } + virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; } + virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; } + virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; } + virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; } private: Ui::GS232ControllerGUI* ui; diff --git a/plugins/feature/gs232controller/gs232controllersettings.cpp b/plugins/feature/gs232controller/gs232controllersettings.cpp index 0e9f3d756..fd9a34278 100644 --- a/plugins/feature/gs232controller/gs232controllersettings.cpp +++ b/plugins/feature/gs232controller/gs232controllersettings.cpp @@ -69,6 +69,7 @@ void GS232ControllerSettings::resetToDefaults() m_connection = SERIAL; m_host = "127.0.0.1"; m_port = 4533; + m_workspaceIndex = 0; } QByteArray GS232ControllerSettings::serialize() const @@ -104,6 +105,9 @@ QByteArray GS232ControllerSettings::serialize() const s.writeBlob(26, m_rollupState->serialize()); } + s.writeS32(27, m_workspaceIndex); + s.writeBlob(28, m_geometryBytes); + return s.final(); } @@ -163,6 +167,9 @@ bool GS232ControllerSettings::deserialize(const QByteArray& data) m_rollupState->deserialize(bytetmp); } + d.readS32(27, &m_workspaceIndex, 0); + d.readBlob(28, &m_geometryBytes); + return true; } else diff --git a/plugins/feature/gs232controller/gs232controllersettings.h b/plugins/feature/gs232controller/gs232controllersettings.h index f922022f8..3db11f2ce 100644 --- a/plugins/feature/gs232controller/gs232controllersettings.h +++ b/plugins/feature/gs232controller/gs232controllersettings.h @@ -68,6 +68,8 @@ struct GS232ControllerSettings uint16_t m_reverseAPIPort; uint16_t m_reverseAPIFeatureSetIndex; uint16_t m_reverseAPIFeatureIndex; + int m_workspaceIndex; + QByteArray m_geometryBytes; GS232ControllerSettings(); void resetToDefaults(); diff --git a/plugins/feature/jogdialcontroller/jogdialcontrollergui.h b/plugins/feature/jogdialcontroller/jogdialcontrollergui.h index d7d27633c..932b5592d 100644 --- a/plugins/feature/jogdialcontroller/jogdialcontrollergui.h +++ b/plugins/feature/jogdialcontroller/jogdialcontrollergui.h @@ -47,6 +47,10 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } + virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; } + virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; } + virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; } + virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; } protected: void focusInEvent(QFocusEvent* e); diff --git a/plugins/feature/jogdialcontroller/jogdialcontrollersettings.cpp b/plugins/feature/jogdialcontroller/jogdialcontrollersettings.cpp index 8893cbe5e..01900a482 100644 --- a/plugins/feature/jogdialcontroller/jogdialcontrollersettings.cpp +++ b/plugins/feature/jogdialcontroller/jogdialcontrollersettings.cpp @@ -71,6 +71,7 @@ void JogdialControllerSettings::resetToDefaults() m_reverseAPIPort = 8888; m_reverseAPIFeatureSetIndex = 0; m_reverseAPIFeatureIndex = 0; + m_workspaceIndex = 0; } QByteArray JogdialControllerSettings::serialize() const @@ -89,6 +90,9 @@ QByteArray JogdialControllerSettings::serialize() const s.writeBlob(12, m_rollupState->serialize()); } + s.writeS32(13, m_workspaceIndex); + s.writeBlob(14, m_geometryBytes); + return s.final(); } @@ -131,6 +135,9 @@ bool JogdialControllerSettings::deserialize(const QByteArray& data) m_rollupState->deserialize(bytetmp); } + d.readS32(13, &m_workspaceIndex, 0); + d.readBlob(14, &m_geometryBytes); + return true; } else diff --git a/plugins/feature/jogdialcontroller/jogdialcontrollersettings.h b/plugins/feature/jogdialcontroller/jogdialcontrollersettings.h index 5d1b82773..fecb052a4 100644 --- a/plugins/feature/jogdialcontroller/jogdialcontrollersettings.h +++ b/plugins/feature/jogdialcontroller/jogdialcontrollersettings.h @@ -52,6 +52,8 @@ struct JogdialControllerSettings uint16_t m_reverseAPIFeatureSetIndex; uint16_t m_reverseAPIFeatureIndex; Serializable *m_rollupState; + int m_workspaceIndex; + QByteArray m_geometryBytes; JogdialControllerSettings(); void resetToDefaults(); diff --git a/plugins/feature/map/mapgui.h b/plugins/feature/map/mapgui.h index 16ad02a19..774810f8d 100644 --- a/plugins/feature/map/mapgui.h +++ b/plugins/feature/map/mapgui.h @@ -72,6 +72,10 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } + virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; } + virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; } + virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; } + virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; } AzEl *getAzEl() { return &m_azEl; } Map *getMap() { return m_map; } QQuickItem *getMapItem(); diff --git a/plugins/feature/map/mapsettings.cpp b/plugins/feature/map/mapsettings.cpp index 732e58502..8df0b3501 100644 --- a/plugins/feature/map/mapsettings.cpp +++ b/plugins/feature/map/mapsettings.cpp @@ -106,6 +106,7 @@ void MapSettings::resetToDefaults() m_eciCamera = false; m_modelDir = HttpDownloadManager::downloadDir() + "/3d"; m_antiAliasing = "None"; + m_workspaceIndex = 0; } QByteArray MapSettings::serialize() const @@ -144,6 +145,8 @@ QByteArray MapSettings::serialize() const s.writeBool(30, m_eciCamera); s.writeString(31, m_cesiumIonAPIKey); s.writeString(32, m_antiAliasing); + s.writeS32(33, m_workspaceIndex); + s.writeBlob(34, m_geometryBytes); return s.final(); } @@ -210,6 +213,8 @@ bool MapSettings::deserialize(const QByteArray& data) d.readBool(30, &m_eciCamera, false); d.readString(31, &m_cesiumIonAPIKey, ""); d.readString(32, &m_antiAliasing, "None"); + d.readS32(33, &m_workspaceIndex, 0); + d.readBlob(34, &m_geometryBytes); return true; } diff --git a/plugins/feature/map/mapsettings.h b/plugins/feature/map/mapsettings.h index e190f770a..7ce04b65f 100644 --- a/plugins/feature/map/mapsettings.h +++ b/plugins/feature/map/mapsettings.h @@ -90,6 +90,8 @@ struct MapSettings Serializable *m_rollupState; bool m_map2DEnabled; QString m_mapType; // "Street Map", "Satellite Map", etc.. as selected in combobox + int m_workspaceIndex; + QByteArray m_geometryBytes; // 3D Map settings bool m_map3DEnabled; diff --git a/plugins/feature/pertester/pertestergui.h b/plugins/feature/pertester/pertestergui.h index b5fe9b0fa..2306bf792 100644 --- a/plugins/feature/pertester/pertestergui.h +++ b/plugins/feature/pertester/pertestergui.h @@ -45,6 +45,10 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } + virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; } + virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; } + virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; } + virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; } private: Ui::PERTesterGUI* ui; diff --git a/plugins/feature/pertester/pertestersettings.cpp b/plugins/feature/pertester/pertestersettings.cpp index 7499902d4..0121b1d8d 100644 --- a/plugins/feature/pertester/pertestersettings.cpp +++ b/plugins/feature/pertester/pertestersettings.cpp @@ -50,6 +50,7 @@ void PERTesterSettings::resetToDefaults() m_reverseAPIPort = 8888; m_reverseAPIFeatureSetIndex = 0; m_reverseAPIFeatureIndex = 0; + m_workspaceIndex = 0; } QByteArray PERTesterSettings::serialize() const @@ -78,6 +79,8 @@ QByteArray PERTesterSettings::serialize() const s.writeBlob(27, m_rollupState->serialize()); } + s.writeS32(28, m_workspaceIndex); + return s.final(); } @@ -146,6 +149,9 @@ bool PERTesterSettings::deserialize(const QByteArray& data) m_rollupState->deserialize(bytetmp); } + d.readS32(28, &m_workspaceIndex, 0); + d.readBlob(29, &m_geometryBytes); + return true; } else diff --git a/plugins/feature/pertester/pertestersettings.h b/plugins/feature/pertester/pertestersettings.h index ea2cfcc83..119aebbcd 100644 --- a/plugins/feature/pertester/pertestersettings.h +++ b/plugins/feature/pertester/pertestersettings.h @@ -48,6 +48,8 @@ struct PERTesterSettings uint16_t m_reverseAPIFeatureSetIndex; uint16_t m_reverseAPIFeatureIndex; Serializable *m_rollupState; + int m_workspaceIndex; + QByteArray m_geometryBytes; PERTesterSettings(); void resetToDefaults(); diff --git a/plugins/feature/radiosonde/radiosondegui.h b/plugins/feature/radiosonde/radiosondegui.h index 18202ab61..1bd135232 100644 --- a/plugins/feature/radiosonde/radiosondegui.h +++ b/plugins/feature/radiosonde/radiosondegui.h @@ -74,6 +74,10 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } + virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; } + virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; } + virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; } + virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; } private: Ui::RadiosondeGUI* ui; diff --git a/plugins/feature/radiosonde/radiosondesettings.cpp b/plugins/feature/radiosonde/radiosondesettings.cpp index 565d44877..2e2f016b6 100644 --- a/plugins/feature/radiosonde/radiosondesettings.cpp +++ b/plugins/feature/radiosonde/radiosondesettings.cpp @@ -76,6 +76,8 @@ QByteArray RadiosondeSettings::serialize() const s.writeS32(10, (int)m_y1); s.writeS32(11, (int)m_y2); + s.writeS32(12, m_workspaceIndex); + s.writeBlob(13, m_geometryBytes); for (int i = 0; i < RADIOSONDES_COLUMNS; i++) { s.writeS32(300 + i, m_radiosondesColumnIndexes[i]); @@ -130,6 +132,8 @@ bool RadiosondeSettings::deserialize(const QByteArray& data) d.readS32(10, (int *)&m_y1, (int)ALTITUDE); d.readS32(11, (int *)&m_y2, (int)TEMPERATURE); + d.readS32(12, &m_workspaceIndex, 0); + d.readBlob(13, &m_geometryBytes); for (int i = 0; i < RADIOSONDES_COLUMNS; i++) { d.readS32(300 + i, &m_radiosondesColumnIndexes[i], i); diff --git a/plugins/feature/radiosonde/radiosondesettings.h b/plugins/feature/radiosonde/radiosondesettings.h index 4c199c731..878b39bfc 100644 --- a/plugins/feature/radiosonde/radiosondesettings.h +++ b/plugins/feature/radiosonde/radiosondesettings.h @@ -39,6 +39,8 @@ struct RadiosondeSettings uint16_t m_reverseAPIFeatureSetIndex; uint16_t m_reverseAPIFeatureIndex; Serializable *m_rollupState; + int m_workspaceIndex; + QByteArray m_geometryBytes; enum ChartData { NONE, diff --git a/plugins/feature/rigctlserver/rigctlservergui.h b/plugins/feature/rigctlserver/rigctlservergui.h index 679b61dd6..51f429a09 100644 --- a/plugins/feature/rigctlserver/rigctlservergui.h +++ b/plugins/feature/rigctlserver/rigctlservergui.h @@ -45,6 +45,10 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } + virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; } + virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; } + virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; } + virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; } private: Ui::RigCtlServerGUI* ui; diff --git a/plugins/feature/rigctlserver/rigctlserversettings.cpp b/plugins/feature/rigctlserver/rigctlserversettings.cpp index a875dc5d1..ad00529cb 100644 --- a/plugins/feature/rigctlserver/rigctlserversettings.cpp +++ b/plugins/feature/rigctlserver/rigctlserversettings.cpp @@ -44,6 +44,7 @@ void RigCtlServerSettings::resetToDefaults() m_reverseAPIPort = 8888; m_reverseAPIFeatureSetIndex = 0; m_reverseAPIFeatureIndex = 0; + m_workspaceIndex = 0; } QByteArray RigCtlServerSettings::serialize() const @@ -66,6 +67,9 @@ QByteArray RigCtlServerSettings::serialize() const s.writeBlob(12, m_rollupState->serialize()); } + s.writeS32(13, m_workspaceIndex); + s.writeBlob(14, m_geometryBytes); + return s.final(); } @@ -119,6 +123,9 @@ bool RigCtlServerSettings::deserialize(const QByteArray& data) m_rollupState->deserialize(bytetmp); } + d.readS32(13, &m_workspaceIndex, 0); + d.readBlob(14, &m_geometryBytes); + return true; } else diff --git a/plugins/feature/rigctlserver/rigctlserversettings.h b/plugins/feature/rigctlserver/rigctlserversettings.h index 70bdabf65..b76562b38 100644 --- a/plugins/feature/rigctlserver/rigctlserversettings.h +++ b/plugins/feature/rigctlserver/rigctlserversettings.h @@ -60,6 +60,8 @@ struct RigCtlServerSettings uint16_t m_reverseAPIFeatureSetIndex; uint16_t m_reverseAPIFeatureIndex; Serializable *m_rollupState; + int m_workspaceIndex; + QByteArray m_geometryBytes; RigCtlServerSettings(); void resetToDefaults(); diff --git a/plugins/feature/satellitetracker/satellitetrackergui.h b/plugins/feature/satellitetracker/satellitetrackergui.h index 2d192f48b..143d81bd6 100644 --- a/plugins/feature/satellitetracker/satellitetrackergui.h +++ b/plugins/feature/satellitetracker/satellitetrackergui.h @@ -52,6 +52,10 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } + virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; } + virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; } + virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; } + virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; } private: Ui::SatelliteTrackerGUI* ui; diff --git a/plugins/feature/satellitetracker/satellitetrackersettings.cpp b/plugins/feature/satellitetracker/satellitetrackersettings.cpp index 1d7eaa9ad..9afab8bd5 100644 --- a/plugins/feature/satellitetracker/satellitetrackersettings.cpp +++ b/plugins/feature/satellitetracker/satellitetrackersettings.cpp @@ -78,6 +78,8 @@ void SatelliteTrackerSettings::resetToDefaults() m_dateTimeSelect = NOW; m_mapFeature = ""; m_fileInputDevice = ""; + m_workspaceIndex = 0; + for (int i = 0; i < SAT_COL_COLUMNS; i++) { m_columnIndexes[i] = i; @@ -134,6 +136,8 @@ QByteArray SatelliteTrackerSettings::serialize() const s.writeS32(42, (int)m_dateTimeSelect); s.writeString(43, m_mapFeature); s.writeString(44, m_fileInputDevice); + s.writeS32(45, m_workspaceIndex); + s.writeBlob(46, m_geometryBytes); for (int i = 0; i < SAT_COL_COLUMNS; i++) { s.writeS32(100 + i, m_columnIndexes[i]); @@ -225,6 +229,8 @@ bool SatelliteTrackerSettings::deserialize(const QByteArray& data) d.readS32(42, (int *)&m_dateTimeSelect, (int)NOW); d.readString(43, &m_mapFeature, ""); d.readString(44, &m_fileInputDevice, ""); + d.readS32(45, &m_workspaceIndex, 0); + d.readBlob(46, &m_geometryBytes); for (int i = 0; i < SAT_COL_COLUMNS; i++) { d.readS32(100 + i, &m_columnIndexes[i], i); diff --git a/plugins/feature/satellitetracker/satellitetrackersettings.h b/plugins/feature/satellitetracker/satellitetrackersettings.h index 238d1f5c7..34495e41b 100644 --- a/plugins/feature/satellitetracker/satellitetrackersettings.h +++ b/plugins/feature/satellitetracker/satellitetrackersettings.h @@ -94,6 +94,8 @@ struct SatelliteTrackerSettings uint16_t m_reverseAPIFeatureSetIndex; uint16_t m_reverseAPIFeatureIndex; Serializable *m_rollupState; + int m_workspaceIndex; + QByteArray m_geometryBytes; SatelliteTrackerSettings(); void resetToDefaults(); diff --git a/plugins/feature/simpleptt/simplepttgui.h b/plugins/feature/simpleptt/simplepttgui.h index 3062bde6a..bee5244d8 100644 --- a/plugins/feature/simpleptt/simplepttgui.h +++ b/plugins/feature/simpleptt/simplepttgui.h @@ -44,6 +44,10 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } + virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; } + virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; } + virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; } + virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; } private: Ui::SimplePTTGUI* ui; diff --git a/plugins/feature/simpleptt/simplepttsettings.cpp b/plugins/feature/simpleptt/simplepttsettings.cpp index 1029d312f..efbafdbd8 100644 --- a/plugins/feature/simpleptt/simplepttsettings.cpp +++ b/plugins/feature/simpleptt/simplepttsettings.cpp @@ -47,6 +47,7 @@ void SimplePTTSettings::resetToDefaults() m_reverseAPIPort = 8888; m_reverseAPIFeatureSetIndex = 0; m_reverseAPIFeatureIndex = 0; + m_workspaceIndex = 0; } QByteArray SimplePTTSettings::serialize() const @@ -74,6 +75,8 @@ QByteArray SimplePTTSettings::serialize() const s.writeBool(15, m_vox); s.writeBool(16, m_voxEnable); s.writeS32(17, m_voxHold); + s.writeS32(18, m_workspaceIndex); + s.writeBlob(19, m_geometryBytes); return s.final(); } @@ -125,7 +128,9 @@ bool SimplePTTSettings::deserialize(const QByteArray& data) d.readS32(14, &m_voxLevel, -20); d.readBool(15, &m_vox, false); d.readBool(16, &m_voxEnable, false); - d.readS32(16, &m_voxHold, 500); + d.readS32(17, &m_voxHold, 500); + d.readS32(18, &m_workspaceIndex, 0); + d.readBlob(19, &m_geometryBytes); return true; } diff --git a/plugins/feature/simpleptt/simplepttsettings.h b/plugins/feature/simpleptt/simplepttsettings.h index 00a12a77c..39c9a2fdb 100644 --- a/plugins/feature/simpleptt/simplepttsettings.h +++ b/plugins/feature/simpleptt/simplepttsettings.h @@ -42,6 +42,8 @@ struct SimplePTTSettings uint16_t m_reverseAPIFeatureSetIndex; uint16_t m_reverseAPIFeatureIndex; Serializable *m_rollupState; + int m_workspaceIndex; + QByteArray m_geometryBytes; SimplePTTSettings(); void resetToDefaults(); diff --git a/plugins/feature/startracker/startrackergui.h b/plugins/feature/startracker/startrackergui.h index 2d41484da..802cfae69 100644 --- a/plugins/feature/startracker/startrackergui.h +++ b/plugins/feature/startracker/startrackergui.h @@ -66,6 +66,10 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } + virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; } + virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; } + virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; } + virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; } private: Ui::StarTrackerGUI* ui; diff --git a/plugins/feature/startracker/startrackersettings.cpp b/plugins/feature/startracker/startrackersettings.cpp index f15aad679..b83e3d802 100644 --- a/plugins/feature/startracker/startrackersettings.cpp +++ b/plugins/feature/startracker/startrackersettings.cpp @@ -82,6 +82,7 @@ void StarTrackerSettings::resetToDefaults() m_weatherUpdatePeriod = 60; m_drawSunOnSkyTempChart = true; m_drawMoonOnSkyTempChart = true; + m_workspaceIndex = 0; } QByteArray StarTrackerSettings::serialize() const @@ -136,6 +137,9 @@ QByteArray StarTrackerSettings::serialize() const s.writeBlob(44, m_rollupState->serialize()); } + s.writeS32(45, m_workspaceIndex); + s.writeBlob(46, m_geometryBytes); + return s.final(); } @@ -221,6 +225,9 @@ bool StarTrackerSettings::deserialize(const QByteArray& data) m_rollupState->deserialize(bytetmp); } + d.readS32(45, &m_workspaceIndex, 0); + d.readBlob(46, &m_geometryBytes); + return true; } else diff --git a/plugins/feature/startracker/startrackersettings.h b/plugins/feature/startracker/startrackersettings.h index 9232eea9c..eec6f2627 100644 --- a/plugins/feature/startracker/startrackersettings.h +++ b/plugins/feature/startracker/startrackersettings.h @@ -72,6 +72,8 @@ struct StarTrackerSettings bool m_drawSunOnSkyTempChart; bool m_drawMoonOnSkyTempChart; Serializable *m_rollupState; + int m_workspaceIndex; + QByteArray m_geometryBytes; StarTrackerSettings(); void resetToDefaults(); diff --git a/plugins/feature/vorlocalizer/vorlocalizergui.h b/plugins/feature/vorlocalizer/vorlocalizergui.h index b2d1f34f0..0f498a930 100644 --- a/plugins/feature/vorlocalizer/vorlocalizergui.h +++ b/plugins/feature/vorlocalizer/vorlocalizergui.h @@ -214,6 +214,10 @@ public: bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } void selectVOR(VORGUI *vorGUI, bool selected); + virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; } + virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; } + virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; } + virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; } private: friend class VORGUI; diff --git a/plugins/feature/vorlocalizer/vorlocalizersettings.cpp b/plugins/feature/vorlocalizer/vorlocalizersettings.cpp index 760a450d4..a6e4da6c7 100644 --- a/plugins/feature/vorlocalizer/vorlocalizersettings.cpp +++ b/plugins/feature/vorlocalizer/vorlocalizersettings.cpp @@ -41,7 +41,7 @@ void VORLocalizerSettings::resetToDefaults() m_reverseAPIPort = 8888; m_reverseAPIFeatureSetIndex = 0; m_reverseAPIFeatureIndex = 0; - + m_workspaceIndex = 0; for (int i = 0; i < VORDEMOD_COLUMNS; i++) { @@ -69,6 +69,9 @@ QByteArray VORLocalizerSettings::serialize() const s.writeBlob(19, m_rollupState->serialize()); } + s.writeS32(20, m_workspaceIndex); + s.writeBlob(21, m_geometryBytes); + for (int i = 0; i < VORDEMOD_COLUMNS; i++) { s.writeS32(100 + i, m_columnIndexes[i]); } @@ -123,6 +126,9 @@ bool VORLocalizerSettings::deserialize(const QByteArray& data) m_rollupState->deserialize(bytetmp); } + d.readS32(20, &m_workspaceIndex, 0); + d.readBlob(21, &m_geometryBytes); + for (int i = 0; i < VORDEMOD_COLUMNS; i++) { d.readS32(100 + i, &m_columnIndexes[i], i); } diff --git a/plugins/feature/vorlocalizer/vorlocalizersettings.h b/plugins/feature/vorlocalizer/vorlocalizersettings.h index 410c96331..c34a0f790 100644 --- a/plugins/feature/vorlocalizer/vorlocalizersettings.h +++ b/plugins/feature/vorlocalizer/vorlocalizersettings.h @@ -74,7 +74,8 @@ struct VORLocalizerSettings uint16_t m_reverseAPIFeatureSetIndex; uint16_t m_reverseAPIFeatureIndex; Serializable *m_rollupState; - + int m_workspaceIndex; + QByteArray m_geometryBytes; static const int VORDEMOD_COLUMNS = 11; static const int VOR_COL_NAME = 0; diff --git a/sdrbase/CMakeLists.txt b/sdrbase/CMakeLists.txt index 899bd7bdc..c8be0d4b8 100644 --- a/sdrbase/CMakeLists.txt +++ b/sdrbase/CMakeLists.txt @@ -176,6 +176,7 @@ set(sdrbase_SOURCES pipes/objectpipe.cpp pipes/objectpipesregistrations.cpp + settings/configuration.cpp settings/featuresetpreset.cpp settings/preferences.cpp settings/preset.cpp @@ -389,6 +390,7 @@ set(sdrbase_HEADERS plugin/pluginapi.h plugin/pluginmanager.h + settings/configuration.h settings/featuresetpreset.h settings/preferences.h settings/preset.h diff --git a/sdrbase/pipes/messagepipeslegacy.cpp b/sdrbase/pipes/messagepipeslegacy.cpp deleted file mode 100644 index 6d9355bba..000000000 --- a/sdrbase/pipes/messagepipeslegacy.cpp +++ /dev/null @@ -1,78 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////////// -// Copyright (C) 2020 Edouard Griffiths, F4EXB // -// // -// This program 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 as version 3 of the License, or // -// (at your option) any later version. // -// // -// This program 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 V3 for more details. // -// // -// You should have received a copy of the GNU General Public License // -// along with this program. If not, see . // -/////////////////////////////////////////////////////////////////////////////////// - -#include - -#include "util/messagequeue.h" - -#include "messagepipeslegacygcworker.h" -#include "messagepipeslegacy.h" -#include "pipeendpoint.h" - -MessagePipesLegacy::MessagePipesLegacy() -{ - m_gcWorker = new MessagePipesLegacyGCWorker(); - m_gcWorker->setC2FRegistrations( - m_registrations.getMutex(), - m_registrations.getElements(), - m_registrations.getConsumers() - ); - m_gcWorker->moveToThread(&m_gcThread); - startGC(); -} - -MessagePipesLegacy::~MessagePipesLegacy() -{ - if (m_gcWorker->isRunning()) { - stopGC(); - } -} - -MessageQueue *MessagePipesLegacy::registerChannelToFeature(const PipeEndPoint *source, PipeEndPoint *dest, const QString& type) -{ - qDebug("MessagePipesLegacy::registerChannelToFeature: %p %p %s", source, dest, qPrintable(type)); - return m_registrations.registerProducerToConsumer(source, dest, type); -} - -MessageQueue *MessagePipesLegacy::unregisterChannelToFeature(const PipeEndPoint *source, PipeEndPoint *dest, const QString& type) -{ - qDebug("MessagePipesLegacy::unregisterChannelToFeature: %p %p %s", source, dest, qPrintable(type)); - MessageQueue *messageQueue = m_registrations.unregisterProducerToConsumer(source, dest, type); - m_gcWorker->addMessageQueueToDelete(messageQueue); - return messageQueue; -} - -QList* MessagePipesLegacy::getMessageQueues(const PipeEndPoint *source, const QString& type) -{ - //qDebug("MessagePipesLegacy::getMessageQueues: %p %s", source, qPrintable(type)); - return m_registrations.getElements(source, type); -} - -void MessagePipesLegacy::startGC() -{ - qDebug("MessagePipesLegacy::startGC"); - m_gcWorker->startWork(); - m_gcThread.start(); -} - -void MessagePipesLegacy::stopGC() -{ - qDebug("MessagePipesLegacy::stopGC"); - m_gcWorker->stopWork(); - m_gcThread.quit(); - m_gcThread.wait(); -} diff --git a/sdrbase/pipes/messagepipeslegacy.h b/sdrbase/pipes/messagepipeslegacy.h deleted file mode 100644 index 562e582a8..000000000 --- a/sdrbase/pipes/messagepipeslegacy.h +++ /dev/null @@ -1,59 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////////// -// Copyright (C) 2020 Edouard Griffiths, F4EXB // -// // -// This program 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 as version 3 of the License, or // -// (at your option) any later version. // -// // -// This program 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 V3 for more details. // -// // -// You should have received a copy of the GNU General Public License // -// along with this program. If not, see . // -/////////////////////////////////////////////////////////////////////////////////// - -#ifndef SDRBASE_PIPES_MESSAGEPIPESLEGACY_H_ -#define SDRBASE_PIPES_MESSAGEPIPESLEGACY_H_ - -#include -#include -#include -#include -#include - -#include "export.h" - -#include "messagepipeslegacycommon.h" -#include "elementpipesregistrations.h" - -class PipeEndPoint; -class MessagePipesLegacyGCWorker; -class MessageQueue; - -class SDRBASE_API MessagePipesLegacy : public QObject -{ - Q_OBJECT -public: - MessagePipesLegacy(); - MessagePipesLegacy(const MessagePipesLegacy&) = delete; - MessagePipesLegacy& operator=(const MessagePipesLegacy&) = delete; - ~MessagePipesLegacy(); - - // FIXME: Names of these functions should probably change, as we now support channel or feature at either end - MessageQueue *registerChannelToFeature(const PipeEndPoint *source, PipeEndPoint *dest, const QString& type); - MessageQueue *unregisterChannelToFeature(const PipeEndPoint *source, PipeEndPoint *dest, const QString& type); - QList* getMessageQueues(const PipeEndPoint *source, const QString& type); - -private: - ElementPipesRegistrations m_registrations; - QThread m_gcThread; //!< Garbage collector thread - MessagePipesLegacyGCWorker *m_gcWorker; //!< Garbage collector - - void startGC(); //!< Start garbage collector - void stopGC(); //!< Stop garbage collector -}; - -#endif // SDRBASE_PIPES_MESSAGEPIPESLEGACY_H_ diff --git a/sdrbase/settings/configuration.cpp b/sdrbase/settings/configuration.cpp new file mode 100644 index 000000000..91210fa91 --- /dev/null +++ b/sdrbase/settings/configuration.cpp @@ -0,0 +1,102 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2022 Edouard Griffiths, F4EXB. // +// // +// Swagger server adapter interface // +// // +// This program 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 as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program 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 V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#include "util/simpleserializer.h" +#include "configuration.h" + +Configuration::Configuration() +{ + resetToDefaults(); +} + +Configuration::~Configuration() +{ } + +void Configuration::resetToDefaults() +{ + m_group = "default"; + m_description = "no name"; + m_workspaceGeometries.clear(); +} + +QByteArray Configuration::serialize() const +{ + SimpleSerializer s(1); + + s.writeString(1, m_group); + s.writeString(2, m_description); + QByteArray b = m_featureSetPreset.serialize(); + s.writeBlob(3, b); + + s.writeS32(100, m_workspaceGeometries.size()); + + for(int i = 0; i < m_workspaceGeometries.size(); i++) { + s.writeBlob(101 + i, m_workspaceGeometries[i]); + } + + return s.final(); +} + +bool Configuration::deserialize(const QByteArray& data) +{ + SimpleDeserializer d(data); + + if (!d.isValid()) + { + resetToDefaults(); + return false; + } + + if (d.getVersion() == 1) + { + d.readString(1, &m_group, "default"); + d.readString(2, &m_description, "no name"); + + QByteArray b; + d.readBlob(3, &b); + m_featureSetPreset.deserialize(b); + + int nbWorkspaces; + d.readS32(100, &nbWorkspaces, 0); + + for(int i = 0; i < nbWorkspaces; i++) + { + m_workspaceGeometries.push_back(QByteArray()); + d.readBlob(101 + i, &m_workspaceGeometries.back()); + } + + return true; + } + else + { + resetToDefaults(); + return false; + } +} + +int Configuration::getNumberOfWorkspaces() const +{ + return m_workspaceGeometries.size(); +} + +void Configuration::clearData() +{ + m_featureSetPreset.clearFeatures(); + m_workspaceGeometries.clear(); +} diff --git a/sdrbase/settings/configuration.h b/sdrbase/settings/configuration.h new file mode 100644 index 000000000..cbdbc37ed --- /dev/null +++ b/sdrbase/settings/configuration.h @@ -0,0 +1,78 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2022 Edouard Griffiths, F4EXB. // +// // +// Swagger server adapter interface // +// // +// This program 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 as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program 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 V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef INCLUDE_CONFIGURATION_H +#define INCLUDE_CONFIGURATION_H + +#include +#include +#include +#include + +#include "featuresetpreset.h" +#include "export.h" + +class SDRBASE_API WorkspaceConfiguration { +public: +}; + +class SDRBASE_API Configuration { +public: + Configuration(); + ~Configuration(); + void resetToDefaults(); + + QByteArray serialize() const; + bool deserialize(const QByteArray& data); + + void setGroup(const QString& group) { m_group = group; } + const QString& getGroup() const { return m_group; } + void setDescription(const QString& description) { m_description = description; } + const QString& getDescription() const { return m_description; } + + int getNumberOfWorkspaces() const; + FeatureSetPreset& getFeatureSetPreset() { return m_featureSetPreset; } + const FeatureSetPreset& getFeatureSetPreset() const { return m_featureSetPreset; } + QList& getWorkspaceGeometries() { return m_workspaceGeometries; } + const QList& getWorkspaceGeometries() const { return m_workspaceGeometries; } + void clearData(); + + static bool configCompare(const Configuration *p1, Configuration *p2) + { + if (p1->m_group != p2->m_group) + { + return p1->m_group < p2->m_group; + } + else + { + return p1->m_description < p2->m_description; + } + } + +private: + QString m_group; + QString m_description; + QList m_workspaceGeometries; + FeatureSetPreset m_featureSetPreset; +}; + +Q_DECLARE_METATYPE(const Configuration*) +Q_DECLARE_METATYPE(Configuration*) + +#endif // INCLUDE_CONFIGURATION_H diff --git a/sdrbase/settings/mainsettings.cpp b/sdrbase/settings/mainsettings.cpp index 51d6ad8d9..560b7ecd1 100644 --- a/sdrbase/settings/mainsettings.cpp +++ b/sdrbase/settings/mainsettings.cpp @@ -52,8 +52,9 @@ void MainSettings::load() QSettings s; m_preferences.deserialize(qUncompress(QByteArray::fromBase64(s.value("preferences").toByteArray()))); - m_workingPreset.deserialize(qUncompress(QByteArray::fromBase64(s.value("current").toByteArray()))); - m_workingFeatureSetPreset.deserialize(qUncompress(QByteArray::fromBase64(s.value("current-featureset").toByteArray()))); + // m_workingPreset.deserialize(qUncompress(QByteArray::fromBase64(s.value("current").toByteArray()))); + // m_workingFeatureSetPreset.deserialize(qUncompress(QByteArray::fromBase64(s.value("current-featureset").toByteArray()))); + m_workingConfiguration.deserialize(qUncompress(QByteArray::fromBase64(s.value("current-configuration").toByteArray()))); if (m_audioDeviceManager) { m_audioDeviceManager->deserialize(qUncompress(QByteArray::fromBase64(s.value("audio").toByteArray()))); @@ -113,6 +114,22 @@ void MainSettings::load() delete featureSetPreset; } + s.endGroup(); + } + else if (groups[i].startsWith("configuration")) + { + s.beginGroup(groups[i]); + Configuration* configuration = new Configuration; + + if (configuration->deserialize(qUncompress(QByteArray::fromBase64(s.value("data").toByteArray())))) + { + m_configurations.append(configuration); + } + else + { + delete configuration; + } + s.endGroup(); } } @@ -126,8 +143,9 @@ void MainSettings::save() const QSettings s; s.setValue("preferences", qCompress(m_preferences.serialize()).toBase64()); - s.setValue("current", qCompress(m_workingPreset.serialize()).toBase64()); - s.setValue("current-featureset", qCompress(m_workingFeatureSetPreset.serialize()).toBase64()); + // s.setValue("current", qCompress(m_workingPreset.serialize()).toBase64()); + // s.setValue("current-featureset", qCompress(m_workingFeatureSetPreset.serialize()).toBase64()); + s.setValue("current-configuration", qCompress(m_workingConfiguration.serialize()).toBase64()); if (m_audioDeviceManager) { s.setValue("audio", qCompress(m_audioDeviceManager->serialize()).toBase64()); @@ -171,6 +189,14 @@ void MainSettings::save() const s.endGroup(); } + for (int i = 0; i < m_configurations.count(); ++i) + { + QString group = QString("configuration-%1").arg(i + 1); + s.beginGroup(group); + s.setValue("data", qCompress(m_configurations[i]->serialize()).toBase64()); + s.endGroup(); + } + s.setValue("hwDeviceUserArgs", qCompress(m_hardwareDeviceUserArgs.serialize()).toBase64()); s.setValue("limeRFEUSBCalib", qCompress(m_limeRFEUSBCalib.serialize()).toBase64()); } @@ -181,6 +207,7 @@ void MainSettings::initialize() clearCommands(); clearPresets(); clearFeatureSetPresets(); + clearConfigurations(); } void MainSettings::resetToDefaults() @@ -188,6 +215,7 @@ void MainSettings::resetToDefaults() m_preferences.resetToDefaults(); m_workingPreset.resetToDefaults(); m_workingFeatureSetPreset.resetToDefaults(); + m_workingConfiguration.resetToDefaults(); } // DeviceSet presets @@ -395,7 +423,7 @@ void MainSettings::renameFeatureSetPresetGroup(const QString& oldGroupName, cons for (int i = 0; i < nbPresets; i++) { - if (getPreset(i)->getGroup() == oldGroupName) + if (getFeatureSetPreset(i)->getGroup() == oldGroupName) { FeatureSetPreset *preset_mod = const_cast(getFeatureSetPreset(i)); preset_mod->setGroup(newGroupName); @@ -427,3 +455,83 @@ void MainSettings::clearFeatureSetPresets() m_featureSetPresets.clear(); } + +// Configurations + +Configuration* MainSettings::newConfiguration(const QString& group, const QString& description) +{ + Configuration* configuration = new Configuration(); + configuration->setGroup(group); + configuration->setDescription(description); + m_configurations.append(configuration); + return configuration; +} + +void MainSettings::addConfiguration(Configuration *configuration) +{ + m_configurations.append(configuration); +} + +void MainSettings::deleteConfiguration(const Configuration *configuration) +{ + m_configurations.removeAll((Configuration*) configuration); + delete (Configuration*) configuration; +} + +const Configuration* MainSettings::getConfiguration(const QString& groupName, const QString& description) const +{ + int nbConfigurations = getConfigurationCount(); + + for (int i = 0; i < nbConfigurations; i++) + { + if ((getConfiguration(i)->getGroup() == groupName) && + (getConfiguration(i)->getDescription() == description)) + { + return getConfiguration(i); + } + } + + return nullptr; +} + +void MainSettings::sortConfigurations() +{ + std::sort(m_configurations.begin(), m_configurations.end(), Configuration::configCompare); +} + +void MainSettings::renameConfigurationGroup(const QString& oldGroupName, const QString& newGroupName) +{ + int nbConfigurations = getConfigurationCount(); + + for (int i = 0; i < nbConfigurations; i++) + { + if (getConfiguration(i)->getGroup() == oldGroupName) + { + Configuration *configuration_mod = const_cast(getConfiguration(i)); + configuration_mod->setGroup(newGroupName); + } + } +} + +void MainSettings::deleteConfigurationGroup(const QString& groupName) +{ + Configurations::iterator it = m_configurations.begin(); + + while (it != m_configurations.end()) + { + if ((*it)->getGroup() == groupName) { + it = m_configurations.erase(it); + } else { + ++it; + } + } +} + +void MainSettings::clearConfigurations() +{ + foreach (Configuration *configuration, m_configurations) { + delete configuration; + } + + m_configurations.clear(); +} diff --git a/sdrbase/settings/mainsettings.h b/sdrbase/settings/mainsettings.h index aa67fbe6a..cf9a7fa85 100644 --- a/sdrbase/settings/mainsettings.h +++ b/sdrbase/settings/mainsettings.h @@ -8,6 +8,7 @@ #include "preferences.h" #include "preset.h" #include "featuresetpreset.h" +#include "configuration.h" #include "export.h" #include "plugin/pluginmanager.h" @@ -70,6 +71,20 @@ public: FeatureSetPreset* getWorkingFeatureSetPreset() { return &m_workingFeatureSetPreset; } QList *getFeatureSetPresets() { return &m_featureSetPresets; } + Configuration* newConfiguration(const QString& group, const QString& description); + void addConfiguration(Configuration *configuration); + void deleteConfiguration(const Configuration *configuration); + int getConfigurationCount() const { return m_configurations.size(); } + const Configuration* getConfiguration(int index) const { return m_configurations[index]; } + const Configuration* getConfiguration(const QString& groupName, const QString& description) const; + void sortConfigurations(); + void renameConfigurationGroup(const QString& oldGroupName, const QString& newGroupName); + void deleteConfigurationGroup(const QString& groupName); + void clearConfigurations(); + const Configuration& getWorkingConfigurationConst() const { return m_workingConfiguration; } + Configuration* getWorkingConfiguration() { return &m_workingConfiguration; } + QList *getConfigurations() { return &m_configurations; } + const QString& getSourceDevice() const { return m_preferences.getSourceDevice(); } void setSourceDevice(const QString& value) { @@ -174,13 +189,16 @@ protected: Preferences m_preferences; AudioDeviceManager *m_audioDeviceManager; Preset m_workingPreset; - FeatureSetPreset m_workingFeatureSetPreset; typedef QList Presets; Presets m_presets; typedef QList Commands; Commands m_commands; + FeatureSetPreset m_workingFeatureSetPreset; typedef QList FeatureSetPresets; FeatureSetPresets m_featureSetPresets; + Configuration m_workingConfiguration; + typedef QList Configurations; + Configurations m_configurations; DeviceUserArgs m_hardwareDeviceUserArgs; LimeRFEUSBCalib m_limeRFEUSBCalib; AMBEEngine *m_ambeEngine; diff --git a/sdrgui/CMakeLists.txt b/sdrgui/CMakeLists.txt index 3a58139d0..4ed80997d 100644 --- a/sdrgui/CMakeLists.txt +++ b/sdrgui/CMakeLists.txt @@ -23,6 +23,7 @@ set(sdrgui_SOURCES gui/commanditem.cpp gui/commandsdialog.cpp gui/commandoutputdialog.cpp + gui/configurationsdialog.cpp gui/crightclickenabler.cpp gui/customtextedit.cpp gui/cwkeyergui.cpp @@ -120,6 +121,7 @@ set(sdrgui_HEADERS gui/commanditem.h gui/commandsdialog.h gui/commandoutputdialog.h + gui/configurationsdialog.h gui/crightclickenabler.h gui/customtextedit.h gui/cwkeyergui.h @@ -212,6 +214,7 @@ set(sdrgui_FORMS gui/channeladddialog.ui gui/commandsdialog.ui gui/commandoutputdialog.ui + gui/configurationsdialog.ui gui/cwkeyergui.ui gui/devicestreamselectiondialog.ui gui/deviceuserargsdialog.ui diff --git a/sdrgui/feature/featuregui.cpp b/sdrgui/feature/featuregui.cpp index 18128e2c1..05e5f8d02 100644 --- a/sdrgui/feature/featuregui.cpp +++ b/sdrgui/feature/featuregui.cpp @@ -33,7 +33,6 @@ FeatureGUI::FeatureGUI(QWidget *parent) : QMdiSubWindow(parent), m_featureIndex(0), - m_workspaceIndex(0), m_contextMenuType(ContextMenuNone), m_drag(false) { @@ -81,7 +80,7 @@ FeatureGUI::FeatureGUI(QWidget *parent) : m_closeButton->setToolTip("Close feature"); m_statusLabel = new QLabel(); - m_statusLabel->setText("OK"); // for future use + // m_statusLabel->setText("OK"); // for future use m_statusLabel->setFixedHeight(20); m_statusLabel->setToolTip("Feature status"); @@ -111,6 +110,7 @@ FeatureGUI::FeatureGUI(QWidget *parent) : m_bottomLayout->addWidget(m_statusLabel); m_sizeGripBottomRight = new QSizeGrip(this); m_sizeGripBottomRight->setStyleSheet("QSizeGrip { background-color: rgb(128, 128, 128); width: 10px; height: 10px; }"); + m_bottomLayout->addStretch(1); m_bottomLayout->addWidget(m_sizeGripBottomRight, 0, Qt::AlignBottom | Qt::AlignRight); m_layouts->addLayout(m_topLayout); diff --git a/sdrgui/feature/featuregui.h b/sdrgui/feature/featuregui.h index c51691acb..ce185df40 100644 --- a/sdrgui/feature/featuregui.h +++ b/sdrgui/feature/featuregui.h @@ -49,6 +49,11 @@ public: virtual void resetToDefaults() = 0; virtual QByteArray serialize() const = 0; virtual bool deserialize(const QByteArray& data) = 0; + // Data saved in the derived settings + virtual void setWorkspaceIndex(int index)= 0; + virtual int getWorkspaceIndex() const = 0; + virtual void setGeometryBytes(const QByteArray& blob) = 0; + virtual QByteArray getGeometryBytes() const = 0; virtual MessageQueue* getInputMessageQueue() = 0; @@ -56,8 +61,7 @@ public: void setTitleColor(const QColor&) {} // not implemented for a feature void setTitle(const QString& title); void setIndex(int index); - void setWorkspaceIndex(int index) { m_workspaceIndex = index; } - int getWorkspaceIndex() const { return m_workspaceIndex; } + int getIndex() const { return m_featureIndex; } protected: void closeEvent(QCloseEvent *event); @@ -66,7 +70,6 @@ protected: void resetContextMenuType() { m_contextMenuType = ContextMenuNone; } int m_featureIndex; - int m_workspaceIndex; QString m_helpURL; RollupContents m_rollupContents; ContextMenuType m_contextMenuType; diff --git a/sdrgui/feature/featureuiset.cpp b/sdrgui/feature/featureuiset.cpp index 621d811c1..e09d2d0fb 100644 --- a/sdrgui/feature/featureuiset.cpp +++ b/sdrgui/feature/featureuiset.cpp @@ -112,6 +112,24 @@ Feature *FeatureUISet::getFeatureAt(int featureIndex) } } +const FeatureGUI *FeatureUISet::getFeatureGuiAt(int featureIndex) const +{ + if ((featureIndex >= 0) && (featureIndex < m_featureInstanceRegistrations.count())) { + return m_featureInstanceRegistrations.at(featureIndex).m_gui; + } else{ + return nullptr; + } +} + +FeatureGUI *FeatureUISet::getFeatureGuiAt(int featureIndex) +{ + if ((featureIndex >= 0) && (featureIndex < m_featureInstanceRegistrations.count())) { + return m_featureInstanceRegistrations.at(featureIndex).m_gui; + } else{ + return nullptr; + } +} + void FeatureUISet::loadFeatureSetSettings( const FeatureSetPreset *preset, PluginAPI *pluginAPI, @@ -147,6 +165,7 @@ void FeatureUISet::loadFeatureSetSettings( { const FeatureSetPreset::FeatureConfig& featureConfig = preset->getFeatureConfig(i); FeatureGUI *featureGUI = nullptr; + Feature *feature = nullptr; // create feature instance @@ -158,14 +177,9 @@ void FeatureUISet::loadFeatureSetSettings( qPrintable((*featureRegistrations)[i].m_featureIdURI), qPrintable(featureConfig.m_featureIdURI) ); - Feature *feature = - (*featureRegistrations)[i].m_plugin->createFeature(apiAdapter); - featureGUI = - (*featureRegistrations)[i].m_plugin->createFeatureGUI(this, feature); + feature = (*featureRegistrations)[i].m_plugin->createFeature(apiAdapter); + featureGUI = (*featureRegistrations)[i].m_plugin->createFeatureGUI(this, feature); registerFeatureInstance(featureGUI, feature); - featureGUI->setIndex(feature->getIndexInFeatureSet()); - featureGUI->setWorkspaceIndex(workspace->getIndex()); - workspace->addToMdiArea((QMdiSubWindow*) featureGUI); break; } } @@ -173,9 +187,63 @@ void FeatureUISet::loadFeatureSetSettings( if (featureGUI) { qDebug("FeatureUISet::loadFeatureSetSettings: deserializing feature [%s]", - qPrintable(featureConfig.m_featureIdURI) - ); + qPrintable(featureConfig.m_featureIdURI)); featureGUI->deserialize(featureConfig.m_config); + + if (workspace) // restore in current workspace + { + featureGUI->setIndex(feature->getIndexInFeatureSet()); + featureGUI->setWorkspaceIndex(workspace->getIndex()); + workspace->addToMdiArea((QMdiSubWindow*) featureGUI); + } + } + } +} + +void FeatureUISet::loadFeatureSetSettings( + const FeatureSetPreset* preset, + PluginAPI *pluginAPI, + WebAPIAdapterInterface *apiAdapter, + QList& workspaces +) +{ + // This method loads from scratch - load from configuration + qDebug("FeatureUISet::loadFeatureSetSettings: %d feature(s) in preset", preset->getFeatureCount()); + + // Available feature plugins + PluginAPI::FeatureRegistrations *featureRegistrations = pluginAPI->getFeatureRegistrations(); + + for (int i = 0; i < preset->getFeatureCount(); i++) + { + const FeatureSetPreset::FeatureConfig& featureConfig = preset->getFeatureConfig(i); + FeatureGUI *featureGUI = nullptr; + Feature *feature = nullptr; + + // create feature instance + + for(int i = 0; i < featureRegistrations->count(); i++) + { + if (FeatureUtils::compareFeatureURIs((*featureRegistrations)[i].m_featureIdURI, featureConfig.m_featureIdURI)) + { + qDebug("FeatureUISet::loadFeatureSetSettings: creating new feature [%s] from config [%s]", + qPrintable((*featureRegistrations)[i].m_featureIdURI), + qPrintable(featureConfig.m_featureIdURI) + ); + feature = (*featureRegistrations)[i].m_plugin->createFeature(apiAdapter); + featureGUI = (*featureRegistrations)[i].m_plugin->createFeatureGUI(this, feature); + registerFeatureInstance(featureGUI, feature); + break; + } + } + + if (featureGUI) + { + qDebug("FeatureUISet::loadFeatureSetSettings: deserializing feature [%s]", + qPrintable(featureConfig.m_featureIdURI)); + featureGUI->deserialize(featureConfig.m_config); + featureGUI->setIndex(feature->getIndexInFeatureSet()); + workspaces[featureGUI->getWorkspaceIndex()]->addToMdiArea((QMdiSubWindow*) featureGUI); + featureGUI->restoreGeometry(featureGUI->getGeometryBytes()); } } } @@ -187,6 +255,8 @@ void FeatureUISet::saveFeatureSetSettings(FeatureSetPreset *preset) qDebug("FeatureUISet::saveFeatureSetSettings: saving feature [%s]", qPrintable(m_featureInstanceRegistrations.at(i).m_feature->getURI()) ); + FeatureGUI *featureGUI = m_featureInstanceRegistrations.at(i).m_gui; + featureGUI->setGeometryBytes(featureGUI->saveGeometry()); preset->addFeature( m_featureInstanceRegistrations.at(i).m_feature->getURI(), m_featureInstanceRegistrations.at(i).m_gui->serialize() diff --git a/sdrgui/feature/featureuiset.h b/sdrgui/feature/featureuiset.h index b82ff1e82..372a3cb3e 100644 --- a/sdrgui/feature/featureuiset.h +++ b/sdrgui/feature/featureuiset.h @@ -46,13 +46,22 @@ public: void deleteFeature(int featureIndex); const Feature *getFeatureAt(int featureIndex) const; Feature *getFeatureAt(int featureIndex); + const FeatureGUI *getFeatureGuiAt(int featureIndex) const; + FeatureGUI *getFeatureGuiAt(int featureIndex); void loadFeatureSetSettings( const FeatureSetPreset* preset, PluginAPI *pluginAPI, WebAPIAdapterInterface *apiAdapter, Workspace *workspace ); + void loadFeatureSetSettings( + const FeatureSetPreset* preset, + PluginAPI *pluginAPI, + WebAPIAdapterInterface *apiAdapter, + QList& workspaces + ); void saveFeatureSetSettings(FeatureSetPreset* preset); + void freeFeatures(); private: struct FeatureInstanceRegistration @@ -79,7 +88,6 @@ private: int m_featureTabIndex; FeatureSet *m_featureSet; - void freeFeatures(); private slots: void handleClosingFeatureGUI(FeatureGUI *featureGUI); diff --git a/sdrgui/gui/commandsdialog.cpp b/sdrgui/gui/commandsdialog.cpp index 6efab2cb5..faae90c80 100644 --- a/sdrgui/gui/commandsdialog.cpp +++ b/sdrgui/gui/commandsdialog.cpp @@ -50,10 +50,9 @@ void CommandsDialog::populateTree() { MainCore::instance()->m_settings.sortCommands(); ui->commandTree->clear(); - QTreeWidgetItem *treeItem; for (int i = 0; i < MainCore::instance()->m_settings.getCommandCount(); ++i) { - treeItem = addCommandToTree(MainCore::instance()->m_settings.getCommand(i)); + addCommandToTree(MainCore::instance()->m_settings.getCommand(i)); } } @@ -222,11 +221,6 @@ void CommandsDialog::on_commandOutput_clicked() } } -void CommandsDialog::on_commandsSave_clicked() -{ - MainCore::instance()->m_settings.save(); -} - void CommandsDialog::on_commandDelete_clicked() { QTreeWidgetItem* item = ui->commandTree->currentItem(); diff --git a/sdrgui/gui/commandsdialog.h b/sdrgui/gui/commandsdialog.h index 508bf97e4..30655b701 100644 --- a/sdrgui/gui/commandsdialog.h +++ b/sdrgui/gui/commandsdialog.h @@ -60,7 +60,6 @@ private slots: void on_commandEdit_clicked(); void on_commandRun_clicked(); void on_commandOutput_clicked(); - void on_commandsSave_clicked(); void on_commandDelete_clicked(); void on_commandKeyboardConnect_toggled(bool checked); }; diff --git a/sdrgui/gui/commandsdialog.ui b/sdrgui/gui/commandsdialog.ui index 10e606ee3..91dc4183f 100644 --- a/sdrgui/gui/commandsdialog.ui +++ b/sdrgui/gui/commandsdialog.ui @@ -123,20 +123,6 @@
- - - - Save commands in settings - - - - - - - :/save.png:/save.png - - - @@ -179,6 +165,9 @@ + + false + Toggle keyboard to command connection diff --git a/sdrgui/gui/configurationsdialog.cpp b/sdrgui/gui/configurationsdialog.cpp new file mode 100644 index 000000000..eb9266ded --- /dev/null +++ b/sdrgui/gui/configurationsdialog.cpp @@ -0,0 +1,403 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2022 F4EXB // +// written by Edouard Griffiths // +// // +// This program 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 as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program 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 V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#include +#include + +#include "gui/addpresetdialog.h" +#include "configurationsdialog.h" +#include "ui_configurationsdialog.h" + +#include "settings/configuration.h" + +ConfigurationsDialog::ConfigurationsDialog(QWidget* parent) : + QDialog(parent), + ui(new Ui::ConfigurationsDialog) +{ + ui->setupUi(this); +} + +ConfigurationsDialog::~ConfigurationsDialog() +{ + delete ui; +} + +void ConfigurationsDialog::populateTree() +{ + if (!m_configurations) { + return; + } + + QList::const_iterator it = m_configurations->begin(); + int middleIndex = m_configurations->size() / 2; + QTreeWidgetItem *treeItem; + ui->configurationsTree->clear(); + + for (int i = 0; it != m_configurations->end(); ++it, i++) + { + treeItem = addConfigurationToTree(*it); + + if (i == middleIndex) { + ui->configurationsTree->setCurrentItem(treeItem); + } + } + + updateConfigurationControls(); +} + +QTreeWidgetItem* ConfigurationsDialog::addConfigurationToTree(const Configuration* configuration) +{ + QTreeWidgetItem* group = nullptr; + + for (int i = 0; i < ui->configurationsTree->topLevelItemCount(); i++) + { + if (ui->configurationsTree->topLevelItem(i)->text(0) == configuration->getGroup()) + { + group = ui->configurationsTree->topLevelItem(i); + break; + } + } + + if (!group) + { + QStringList sl; + sl.append(configuration->getGroup()); + group = new QTreeWidgetItem(ui->configurationsTree, sl, PGroup); + group->setFirstColumnSpanned(true); + group->setExpanded(true); + ui->configurationsTree->sortByColumn(0, Qt::AscendingOrder); + } + + QStringList sl; + sl.append(configuration->getDescription()); + QTreeWidgetItem* item = new QTreeWidgetItem(group, sl, PItem); // description column + item->setTextAlignment(0, Qt::AlignLeft); + item->setData(0, Qt::UserRole, QVariant::fromValue(configuration)); + + updateConfigurationControls(); + return item; +} + +void ConfigurationsDialog::updateConfigurationControls() +{ + ui->configurationsTree->resizeColumnToContents(0); + + if (ui->configurationsTree->currentItem()) + { + ui->configurationDelete->setEnabled(true); + ui->configurationLoad->setEnabled(true); + } + else + { + ui->configurationDelete->setEnabled(false); + ui->configurationLoad->setEnabled(false); + } +} + +void ConfigurationsDialog::on_configurationSave_clicked() +{ + QStringList groups; + QString group; + QString description = ""; + + for (int i = 0; i < ui->configurationsTree->topLevelItemCount(); i++) { + groups.append(ui->configurationsTree->topLevelItem(i)->text(0)); + } + + QTreeWidgetItem* item = ui->configurationsTree->currentItem(); + + if (item) + { + if (item->type() == PGroup) + { + group = item->text(0); + } + else if (item->type() == PItem) + { + group = item->parent()->text(0); + description = item->text(0); + } + } + + AddPresetDialog dlg(groups, group, this); + + if (description.length() > 0) { + dlg.setDescription(description); + } + + if (dlg.exec() == QDialog::Accepted) + { + Configuration* configuration = newConfiguration(dlg.group(), dlg.description()); + emit saveConfiguration(configuration); + ui->configurationsTree->setCurrentItem(addConfigurationToTree(configuration)); + } + + sortConfigurations(); +} + +void ConfigurationsDialog::on_configurationUpdate_clicked() +{ + QTreeWidgetItem* item = ui->configurationsTree->currentItem(); + const Configuration* changedConfiguration = nullptr; + + if (item) + { + if( item->type() == PItem) + { + const Configuration* configuration = qvariant_cast(item->data(0, Qt::UserRole)); + + if (configuration) + { + Configuration* configuration_mod = const_cast(configuration); + emit saveConfiguration(configuration_mod); + changedConfiguration = configuration; + } + } + } + + sortConfigurations(); + ui->configurationsTree->clear(); + + for (int i = 0; i < m_configurations->size(); ++i) + { + QTreeWidgetItem *item_x = addConfigurationToTree(m_configurations->at(i)); + const Configuration* configuration_x = qvariant_cast(item_x->data(0, Qt::UserRole)); + + if (changedConfiguration && (configuration_x == changedConfiguration)) { // set cursor on changed configuration + ui->configurationsTree->setCurrentItem(item_x); + } + } +} + +void ConfigurationsDialog::on_configurationEdit_clicked() +{ + QTreeWidgetItem* item = ui->configurationsTree->currentItem(); + QStringList groups; + bool change = false; + const Configuration *changedConfiguration = nullptr; + QString newGroupName; + + for (int i = 0; i < ui->configurationsTree->topLevelItemCount(); i++) { + groups.append(ui->configurationsTree->topLevelItem(i)->text(0)); + } + + if (item) + { + if (item->type() == PItem) + { + const Configuration* configuration = qvariant_cast(item->data(0, Qt::UserRole)); + AddPresetDialog dlg(groups, configuration->getGroup(), this); + dlg.setDescription(configuration->getDescription()); + + if (dlg.exec() == QDialog::Accepted) + { + Configuration* configuration_mod = const_cast(configuration); + configuration_mod->setGroup(dlg.group()); + configuration_mod->setDescription(dlg.description()); + change = true; + changedConfiguration = configuration; + } + } + else if (item->type() == PGroup) + { + AddPresetDialog dlg(groups, item->text(0), this); + dlg.showGroupOnly(); + dlg.setDialogTitle("Edit configuration group"); + + if (dlg.exec() == QDialog::Accepted) + { + renameConfigurationGroup(item->text(0), dlg.group()); + newGroupName = dlg.group(); + change = true; + } + } + } + + if (change) + { + sortConfigurations(); + ui->configurationsTree->clear(); + + for (int i = 0; i < m_configurations->size(); ++i) + { + QTreeWidgetItem *item_x = addConfigurationToTree(m_configurations->at(i)); + const Configuration* configuration_x = qvariant_cast(item_x->data(0, Qt::UserRole)); + + if (changedConfiguration && (configuration_x == changedConfiguration)) { // set cursor on changed configuration + ui->configurationsTree->setCurrentItem(item_x); + } + } + + if (!changedConfiguration) // on group name change set cursor on the group that has been changed + { + for(int i = 0; i < ui->configurationsTree->topLevelItemCount(); i++) + { + QTreeWidgetItem* item = ui->configurationsTree->topLevelItem(i); + + if (item->text(0) == newGroupName) { + ui->configurationsTree->setCurrentItem(item); + } + } + } + } +} + +void ConfigurationsDialog::on_configurationDelete_clicked() +{ + QTreeWidgetItem* item = ui->configurationsTree->currentItem(); + + if (item == 0) + { + updateConfigurationControls(); + return; + } + else + { + if (item->type() == PItem) + { + const Configuration* configuration = qvariant_cast(item->data(0, Qt::UserRole)); + + if (configuration) + { + if ( + QMessageBox::question( + this, + tr("Delete Configuration"), + tr("Do you want to delete configuration '%1'?").arg(configuration->getDescription()), + QMessageBox::No | QMessageBox::Yes, + QMessageBox::No + ) == QMessageBox::Yes + ) + { + delete item; + deleteConfiguration(configuration); + } + } + } + else if (item->type() == PGroup) + { + if ( + QMessageBox::question( + this, + tr("Delete configuration group"), + tr("Do you want to delete configuration group '%1'?").arg(item->text(0)), + QMessageBox::No | QMessageBox::Yes, + QMessageBox::No + ) == QMessageBox::Yes + ) + { + deleteConfigurationGroup(item->text(0)); + + ui->configurationsTree->clear(); + + for (int i = 0; i < m_configurations->size(); ++i) { + addConfigurationToTree(m_configurations->at(i)); + } + } + } + } +} + +void ConfigurationsDialog::on_configurationLoad_clicked() +{ + qDebug() << "ConfigurationsDialog::on_configurationLoad_clicked"; + + QTreeWidgetItem* item = ui->configurationsTree->currentItem(); + + if (!item) + { + qDebug("ConfigurationsDialog::on_configurationLoad_clicked: item null"); + updateConfigurationControls(); + return; + } + + const Configuration* configuration = qvariant_cast(item->data(0, Qt::UserRole)); + + if (!configuration) + { + qDebug("ConfigurationsDialog::on_configurationLoad_clicked: configuration null"); + return; + } + + emit loadConfiguration(configuration); +} + +void ConfigurationsDialog::on_configurationTree_currentItemChanged(QTreeWidgetItem *current, QTreeWidgetItem *previous) +{ + (void) current; + (void) previous; + updateConfigurationControls(); +} + +void ConfigurationsDialog::on_configurationTree_itemActivated(QTreeWidgetItem *item, int column) +{ + (void) item; + (void) column; + on_configurationLoad_clicked(); +} + +Configuration* ConfigurationsDialog::newConfiguration(const QString& group, const QString& description) +{ + Configuration* configuration = new Configuration(); + configuration->setGroup(group); + configuration->setDescription(description); + addConfiguration(configuration); + return configuration; +} + +void ConfigurationsDialog::addConfiguration(Configuration *configuration) +{ + m_configurations->append(configuration); +} + +void ConfigurationsDialog::sortConfigurations() +{ + std::sort(m_configurations->begin(), m_configurations->end(), Configuration::configCompare); +} + +void ConfigurationsDialog::renameConfigurationGroup(const QString& oldGroupName, const QString& newGroupName) +{ + for (int i = 0; i < m_configurations->size(); i++) + { + if (m_configurations->at(i)->getGroup() == oldGroupName) + { + Configuration *configuration_mod = const_cast(m_configurations->at(i)); + configuration_mod->setGroup(newGroupName); + } + } +} + +void ConfigurationsDialog::deleteConfiguration(const Configuration* configuration) +{ + m_configurations->removeAll((Configuration*) configuration); + delete (Configuration*) configuration; +} + +void ConfigurationsDialog::deleteConfigurationGroup(const QString& groupName) +{ + QList::iterator it = m_configurations->begin(); + + while (it != m_configurations->end()) + { + if ((*it)->getGroup() == groupName) { + it = m_configurations->erase(it); + } else { + ++it; + } + } +} diff --git a/sdrgui/gui/configurationsdialog.h b/sdrgui/gui/configurationsdialog.h new file mode 100644 index 000000000..12489e533 --- /dev/null +++ b/sdrgui/gui/configurationsdialog.h @@ -0,0 +1,78 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2022 F4EXB // +// written by Edouard Griffiths // +// // +// This program 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 as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program 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 V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef SDRGUI_GUI_CONFIGURATIONSDIALOG_H_ +#define SDRGUI_GUI_CONFIGURATIONSDIALOG_H_ + +#include +#include +#include + +#include "export.h" + +class Configuration; +class FeatureUISet; +class WebAPIAdapterInterface; +class PluginAPI; +class Workspace; + +namespace Ui { + class ConfigurationsDialog; +} + +class SDRGUI_API ConfigurationsDialog : public QDialog { + Q_OBJECT +public: + explicit ConfigurationsDialog(QWidget* parent = nullptr); + ~ConfigurationsDialog(); + void setConfigurations(QList* configurations) { m_configurations = configurations; } + void populateTree(); + +private: + enum { + PGroup, + PItem + }; + + Ui::ConfigurationsDialog* ui; + QList *m_configurations; + + QTreeWidgetItem* addConfigurationToTree(const Configuration* configuration); + void updateConfigurationControls(); + Configuration* newConfiguration(const QString& group, const QString& description); + void addConfiguration(Configuration *configuration); + void sortConfigurations(); + void renameConfigurationGroup(const QString& oldGroupName, const QString& newGroupName); + void deleteConfiguration(const Configuration* configuration); + void deleteConfigurationGroup(const QString& groupName); + +private slots: + void on_configurationSave_clicked(); + void on_configurationUpdate_clicked(); + void on_configurationEdit_clicked(); + void on_configurationDelete_clicked(); + void on_configurationLoad_clicked(); + void on_configurationTree_currentItemChanged(QTreeWidgetItem *current, QTreeWidgetItem *previous); + void on_configurationTree_itemActivated(QTreeWidgetItem *item, int column); + +signals: + void saveConfiguration(Configuration*); + void loadConfiguration(const Configuration*); +}; + +#endif // SDRGUI_GUI_CONFIGURATIONSDIALOG_H_ diff --git a/sdrgui/gui/configurationsdialog.ui b/sdrgui/gui/configurationsdialog.ui new file mode 100644 index 000000000..d7938bdcc --- /dev/null +++ b/sdrgui/gui/configurationsdialog.ui @@ -0,0 +1,244 @@ + + + ConfigurationsDialog + + + + 0 + 0 + 392 + 414 + + + + + Liberation Sans + 9 + + + + Configurations + + + + + 40 + 380 + 341 + 32 + + + + Qt::Horizontal + + + QDialogButtonBox::Close + + + + + + 0 + 10 + 392 + 310 + + + + + + + List of configurations + + + 5 + + + true + + + 1 + + + 5 + + + + Description + + + + + + + + + + 0 + 330 + 392 + 34 + + + + + + + Save current workspaces configuration as new configuration + + + ... + + + + :/create.png:/create.png + + + + 16 + 16 + + + + + + + + Update selected configuration with current workspaces configuration + + + ... + + + + :/recycle.png:/recycle.png + + + + 16 + 16 + + + + + + + + Edit configuration details + + + + + + + :/edit.png:/edit.png + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Delete selected configuration + + + ... + + + + :/bin.png:/bin.png + + + + 16 + 16 + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Load selected configuration + + + ... + + + + :/load.png:/load.png + + + + 16 + 16 + + + + + + + + + + + + + buttonBox + accepted() + ConfigurationsDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + ConfigurationsDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/sdrgui/gui/featurepresetsdialog.cpp b/sdrgui/gui/featurepresetsdialog.cpp index 9d842ac07..6458a43e1 100644 --- a/sdrgui/gui/featurepresetsdialog.cpp +++ b/sdrgui/gui/featurepresetsdialog.cpp @@ -21,8 +21,10 @@ #include "gui/addpresetdialog.h" #include "feature/featureuiset.h" -#include "featurepresetsdialog.h" #include "settings/featuresetpreset.h" +#include "maincore.h" + +#include "featurepresetsdialog.h" #include "ui_featurepresetsdialog.h" FeaturePresetsDialog::FeaturePresetsDialog(QWidget* parent) : @@ -31,7 +33,8 @@ FeaturePresetsDialog::FeaturePresetsDialog(QWidget* parent) : m_featureSetPresets(nullptr), m_featureUISet(nullptr), m_pluginAPI(nullptr), - m_apiAdapter(nullptr) + m_apiAdapter(nullptr), + m_presetLoaded(false) { ui->setupUi(this); } @@ -188,10 +191,6 @@ void FeaturePresetsDialog::on_presetUpdate_clicked() } } -void FeaturePresetsDialog::on_settingsSave_clicked() -{ -} - void FeaturePresetsDialog::on_presetEdit_clicked() { QTreeWidgetItem* item = ui->presetsTree->currentItem(); @@ -376,6 +375,7 @@ void FeaturePresetsDialog::loadPresetSettings(const FeatureSetPreset* preset) qPrintable(preset->getDescription())); m_featureUISet->loadFeatureSetSettings(preset, m_pluginAPI, m_apiAdapter, m_workspace); + m_presetLoaded = true; } void FeaturePresetsDialog::sortFeatureSetPresets() diff --git a/sdrgui/gui/featurepresetsdialog.h b/sdrgui/gui/featurepresetsdialog.h index 7785be0f2..1addbf404 100644 --- a/sdrgui/gui/featurepresetsdialog.h +++ b/sdrgui/gui/featurepresetsdialog.h @@ -46,6 +46,7 @@ public: void setWebAPIAdapter(WebAPIAdapterInterface *apiAdapter) { m_apiAdapter = apiAdapter; } void setWorkspace(Workspace *workspace) { m_workspace = workspace; } void populateTree(); + bool wasPresetLoaded() const { return m_presetLoaded; } private: enum { @@ -59,6 +60,7 @@ private: PluginAPI *m_pluginAPI; WebAPIAdapterInterface *m_apiAdapter; Workspace *m_workspace; + bool m_presetLoaded; QTreeWidgetItem* addPresetToTree(const FeatureSetPreset* preset); void updatePresetControls(); @@ -74,7 +76,6 @@ private: private slots: void on_presetSave_clicked(); void on_presetUpdate_clicked(); - void on_settingsSave_clicked(); void on_presetEdit_clicked(); void on_presetDelete_clicked(); void on_presetLoad_clicked(); diff --git a/sdrgui/gui/featurepresetsdialog.ui b/sdrgui/gui/featurepresetsdialog.ui index 55de482eb..1ee9c22f2 100644 --- a/sdrgui/gui/featurepresetsdialog.ui +++ b/sdrgui/gui/featurepresetsdialog.ui @@ -118,26 +118,6 @@ - - - - Save the current settings (inc. presets) - - - ... - - - - :/save.png:/save.png - - - - 16 - 16 - - - - diff --git a/sdrgui/gui/workspace.cpp b/sdrgui/gui/workspace.cpp index 3509a6810..fc7eed526 100644 --- a/sdrgui/gui/workspace.cpp +++ b/sdrgui/gui/workspace.cpp @@ -37,6 +37,7 @@ Workspace::Workspace(int index, QWidget *parent, Qt::WindowFlags flags) : setWidget(m_mdi); setWindowTitle(tr("W%1").arg(m_index)); + setObjectName(tr("W%1").arg(m_index)); m_titleBar = new QWidget(); m_titleBarLayout = new QHBoxLayout(); @@ -105,7 +106,7 @@ Workspace::Workspace(int index, QWidget *parent, Qt::WindowFlags flags) : m_normalButton->setFixedSize(20, 20); m_closeButton = new QPushButton(); - QIcon closeIcon(":/cross.png"); + QIcon closeIcon(":/hide.png"); m_closeButton->setIcon(closeIcon); m_closeButton->setToolTip("Hide workspace"); m_closeButton->setFixedSize(20, 20); @@ -269,3 +270,18 @@ void Workspace::removeFromMdiArea(QMdiSubWindow *sub) { m_mdi->removeSubWindow(sub); } + +int Workspace::getNumberOfSubWindows() const +{ + return m_mdi->subWindowList().size(); +} + +QByteArray Workspace::saveMdiGeometry() +{ + return qCompress(m_mdi->saveGeometry()); +} + +void Workspace::restoreMdiGeometry(const QByteArray& blob) +{ + m_mdi->restoreGeometry(qUncompress(blob)); +} diff --git a/sdrgui/gui/workspace.h b/sdrgui/gui/workspace.h index 56b307fad..3944ddfad 100644 --- a/sdrgui/gui/workspace.h +++ b/sdrgui/gui/workspace.h @@ -44,6 +44,9 @@ public: void addAvailableFeatures(const QStringList& featureNames) { m_featureAddDialog.addFeatureNames(featureNames); } void addToMdiArea(QMdiSubWindow *sub); void removeFromMdiArea(QMdiSubWindow *sub); + int getNumberOfSubWindows() const; + QByteArray saveMdiGeometry(); + void restoreMdiGeometry(const QByteArray& blob); private: int m_index; diff --git a/sdrgui/mainwindow.cpp b/sdrgui/mainwindow.cpp index a98ae3a54..65a464ee5 100644 --- a/sdrgui/mainwindow.cpp +++ b/sdrgui/mainwindow.cpp @@ -66,6 +66,7 @@ #include "gui/workspace.h" #include "gui/featurepresetsdialog.h" #include "gui/commandsdialog.h" +#include "gui/configurationsdialog.h" #include "dsp/dspengine.h" #include "dsp/spectrumvis.h" #include "dsp/dspcommands.h" @@ -263,6 +264,7 @@ MainWindow::MainWindow(qtwebapp::LoggerWithFile *logger, const MainParser& parse // loadPresetSettings(m_mainCore->m_settings.getWorkingPreset(), 0); m_apiAdapter = new WebAPIAdapter(); // loadFeatureSetPresetSettings(m_mainCore->m_settings.getWorkingFeatureSetPreset(), 0); + loadConfiguration(m_mainCore->m_settings.getWorkingConfiguration()); splash->showStatusMessage("update preset controls...", Qt::white); qDebug() << "MainWindow::MainWindow: update preset controls..."; @@ -324,6 +326,7 @@ MainWindow::MainWindow(qtwebapp::LoggerWithFile *logger, const MainParser& parse MainWindow::~MainWindow() { + m_mainCore->m_settings.save(); m_apiServer->stop(); delete m_apiServer; delete m_requestMapper; @@ -773,8 +776,8 @@ void MainWindow::loadSettings() m_mainCore->m_settings.load(); m_mainCore->m_settings.sortPresets(); - int middleIndex = m_mainCore->m_settings.getPresetCount() / 2; - QTreeWidgetItem *treeItem; + // int middleIndex = m_mainCore->m_settings.getPresetCount() / 2; + // QTreeWidgetItem *treeItem; // for (int i = 0; i < m_mainCore->m_settings.getPresetCount(); ++i) // { @@ -787,10 +790,10 @@ void MainWindow::loadSettings() m_mainCore->m_settings.sortCommands(); - for (int i = 0; i < m_mainCore->m_settings.getCommandCount(); ++i) - { - treeItem = addCommandToTree(m_mainCore->m_settings.getCommand(i)); - } + // for (int i = 0; i < m_mainCore->m_settings.getCommandCount(); ++i) + // { + // treeItem = addCommandToTree(m_mainCore->m_settings.getCommand(i)); + // } m_mainCore->setLoggingOptions(); } @@ -894,8 +897,64 @@ void MainWindow::saveFeatureSetPresetSettings(FeatureSetPreset* preset, int feat featureUI->saveFeatureSetSettings(preset); } -void MainWindow::saveCommandSettings() +void MainWindow::loadConfiguration(const Configuration *configuration) { + qDebug("MainWindow::loadConfiguration: configuration [%s | %s] %d workspaces", + qPrintable(configuration->getGroup()), + qPrintable(configuration->getDescription()), + configuration->getNumberOfWorkspaces() + ); + + // Wipe out everything first + + // Features + m_featureUIs[0]->freeFeatures(); + // Workspaces + for (const auto& workspace : m_workspaces) { + delete workspace; + } + m_workspaces.clear(); + + // Reconstruct + + // Workspaces + for (int i = 0; i < configuration->getNumberOfWorkspaces(); i++) { + addWorkspace(); + } + // Features + m_featureUIs[0]->loadFeatureSetSettings(&configuration->getFeatureSetPreset(), m_pluginManager->getPluginAPI(), m_apiAdapter, m_workspaces); + + for (int i = 0; i < m_featureUIs[0]->getNumberOfFeatures(); i++) + { + FeatureGUI *gui = m_featureUIs[0]->getFeatureGuiAt(i); + QObject::connect( + gui, + &FeatureGUI::moveToWorkspace, + this, + [=](int wsIndexDest){ this->featureMove(gui, wsIndexDest); } + ); + } + + // Lastly restore workspaces geometry + for (int i = 0; i < configuration->getNumberOfWorkspaces(); i++) { + m_workspaces[i]->restoreGeometry(configuration->getWorkspaceGeometries()[i]); + } +} + +void MainWindow::saveConfiguration(Configuration *configuration) +{ + qDebug("MainWindow::saveConfiguration: configuration [%s | %s] %d workspaces", + qPrintable(configuration->getGroup()), + qPrintable(configuration->getDescription()), + m_workspaces.size() + ); + + configuration->clearData(); + m_featureUIs[0]->saveFeatureSetSettings(&configuration->getFeatureSetPreset()); + + for (const auto& workspace : m_workspaces) { + configuration->getWorkspaceGeometries().push_back(workspace->saveGeometry()); + } } QString MainWindow::openGLVersion() @@ -950,8 +1009,14 @@ void MainWindow::createMenuBar() QAction *viewAllWorkspacesAction = workspacesMenu->addAction("View all"); viewAllWorkspacesAction->setToolTip("View all workspaces"); QObject::connect(viewAllWorkspacesAction, &QAction::triggered, this, &MainWindow::viewAllWorkspaces); + QAction *removeEmptyWorkspacesAction = workspacesMenu->addAction("Remove empty"); + removeEmptyWorkspacesAction->setToolTip("Remove empty workspaces"); + QObject::connect(removeEmptyWorkspacesAction, &QAction::triggered, this, &MainWindow::removeEmptyWorkspaces); QMenu *preferencesMenu = menuBar->addMenu("Preferences"); + QAction *configurationsAction = preferencesMenu->addAction("Configurations"); + configurationsAction->setToolTip("Manage configurations"); + QObject::connect(configurationsAction, &QAction::triggered, this, &MainWindow::on_action_Configurations_triggered); QAction *audioAction = preferencesMenu->addAction("Audio"); audioAction->setToolTip("Audio preferences"); QObject::connect(audioAction, &QAction::triggered, this, &MainWindow::on_action_Audio_triggered); @@ -977,6 +1042,9 @@ void MainWindow::createMenuBar() QAction *commandsAction = preferencesMenu->addAction("Commands"); commandsAction->setToolTip("External commands dialog"); QObject::connect(commandsAction, &QAction::triggered, this, &MainWindow::on_action_commands_triggered); + QAction *saveAllAction = preferencesMenu->addAction("Save all"); + saveAllAction->setToolTip("Save all current settings"); + QObject::connect(saveAllAction, &QAction::triggered, this, &MainWindow::on_action_saveAll_triggered); QMenu *helpMenu = menuBar->addMenu("Help"); QAction *quickStartAction = helpMenu->addAction("Quick start"); @@ -1021,7 +1089,8 @@ void MainWindow::closeEvent(QCloseEvent *closeEvent) // savePresetSettings(m_mainCore->m_settings.getWorkingPreset(), 0); // saveFeatureSetPresetSettings(m_mainCore->m_settings.getWorkingFeatureSetPreset(), 0); - // m_mainCore->m_settings.save(); + saveConfiguration(m_mainCore->m_settings.getWorkingConfiguration()); + m_mainCore->m_settings.save(); // while (m_deviceUIs.size() > 0) // { @@ -1128,6 +1197,7 @@ void MainWindow::applySettings() { // loadPresetSettings(m_mainCore->m_settings.getWorkingPreset(), 0); // loadFeatureSetPresetSettings(m_mainCore->m_settings.getWorkingFeatureSetPreset(), 0); + loadConfiguration(m_mainCore->m_settings.getWorkingConfiguration()); m_mainCore->m_settings.sortPresets(); int middleIndex = m_mainCore->m_settings.getPresetCount() / 2; @@ -1420,6 +1490,24 @@ void MainWindow::viewAllWorkspaces() } } +void MainWindow::removeEmptyWorkspaces() +{ + QList::iterator it = m_workspaces.begin(); + + while (it != m_workspaces.end()) + { + if ((*it)->getNumberOfSubWindows() == 0) + { + removeDockWidget(*it); + it = m_workspaces.erase(it); + } + else + { + ++it; + } + } +} + void MainWindow::on_action_View_Fullscreen_toggled(bool checked) { if(checked) { @@ -1674,11 +1762,13 @@ void MainWindow::on_presetImport_clicked() // } } -void MainWindow::on_settingsSave_clicked() +void MainWindow::on_action_saveAll_triggered() { // savePresetSettings(m_mainCore->m_settings.getWorkingPreset(), ui->tabInputsView->currentIndex()); // saveFeatureSetPresetSettings(m_mainCore->m_settings.getWorkingFeatureSetPreset(), ui->tabFeatures->currentIndex()); + saveConfiguration(m_mainCore->m_settings.getWorkingConfiguration()); m_mainCore->m_settings.save(); + QMessageBox::information(this, tr("Done"), tr("All curent settings saved")); } void MainWindow::on_presetLoad_clicked() @@ -1777,6 +1867,26 @@ void MainWindow::on_action_Loaded_Plugins_triggered() pluginsDialog.exec(); } +void MainWindow::on_action_Configurations_triggered() +{ + ConfigurationsDialog dialog(this); + dialog.setConfigurations(m_mainCore->m_settings.getConfigurations()); + dialog.populateTree(); + QObject::connect( + &dialog, + &ConfigurationsDialog::saveConfiguration, + this, + &MainWindow::saveConfiguration + ); + QObject::connect( + &dialog, + &ConfigurationsDialog::loadConfiguration, + this, + &MainWindow::loadConfiguration + ); + dialog.exec(); +} + void MainWindow::on_action_Audio_triggered() { AudioDialogX audioDialog(m_dspEngine->getAudioDeviceManager(), this); @@ -2329,6 +2439,20 @@ void MainWindow::openFeaturePresetsDialog(QPoint p, Workspace *workspace) dialog.populateTree(); dialog.move(p); dialog.exec(); + + if (dialog.wasPresetLoaded()) + { + for (int i = 0; i < m_featureUIs[0]->getNumberOfFeatures(); i++) + { + FeatureGUI *gui = m_featureUIs[0]->getFeatureGuiAt(i); + QObject::connect( + gui, + &FeatureGUI::moveToWorkspace, + this, + [=](int wsIndexDest){ this->featureMove(gui, wsIndexDest); } + ); + } + } } void MainWindow::deleteFeature(int featureSetIndex, int featureIndex) diff --git a/sdrgui/mainwindow.h b/sdrgui/mainwindow.h index 13f077016..0481bc8e0 100644 --- a/sdrgui/mainwindow.h +++ b/sdrgui/mainwindow.h @@ -128,7 +128,6 @@ private: void savePresetSettings(Preset* preset, int tabIndex); void loadFeatureSetPresetSettings(const FeatureSetPreset* preset, int featureSetIndex, Workspace *workspace); void saveFeatureSetPresetSettings(FeatureSetPreset* preset, int featureSetIndex); - void saveCommandSettings(); QString openGLVersion(); void createMenuBar(); @@ -162,6 +161,9 @@ private slots: void updateStatus(); void addWorkspace(); void viewAllWorkspaces(); + void removeEmptyWorkspaces(); + void loadConfiguration(const Configuration *configuration); + void saveConfiguration(Configuration *configuration); void on_action_View_Fullscreen_toggled(bool checked); void on_presetSave_clicked(); @@ -169,11 +171,12 @@ private slots: void on_presetEdit_clicked(); void on_presetExport_clicked(); void on_presetImport_clicked(); - void on_settingsSave_clicked(); + void on_action_saveAll_triggered(); void on_presetLoad_clicked(); void on_presetDelete_clicked(); void on_presetTree_currentItemChanged(QTreeWidgetItem *current, QTreeWidgetItem *previous); void on_presetTree_itemActivated(QTreeWidgetItem *item, int column); + void on_action_Configurations_triggered(); void on_action_Audio_triggered(); void on_action_Logging_triggered(); void on_action_FFT_triggered(); diff --git a/sdrgui/resources/hide.png b/sdrgui/resources/hide.png new file mode 100644 index 0000000000000000000000000000000000000000..d92ceae1567b825d8b00040b7ff73ac68703c167 GIT binary patch literal 4867 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 literal 0 HcmV?d00001 diff --git a/sdrgui/resources/res.qrc b/sdrgui/resources/res.qrc index 73b00a400..bb851fc62 100644 --- a/sdrgui/resources/res.qrc +++ b/sdrgui/resources/res.qrc @@ -129,6 +129,7 @@ help.png shrink.png exit.png + hide.png LiberationMono-Regular.ttf LiberationSans-Regular.ttf From aad90aeabc14e9c9f625e0d42c10fcd8ff7a54ae Mon Sep 17 00:00:00 2001 From: f4exb Date: Thu, 7 Apr 2022 16:32:03 +0200 Subject: [PATCH 004/115] Massive UI revamping (v7): devices basic --- .../beamsteeringcwmodsettings.cpp | 7 + .../beamsteeringcwmodsettings.h | 2 + .../interferometer/interferometersettings.cpp | 5 + .../interferometer/interferometersettings.h | 2 + .../bladerf2mimo/bladerf2mimogui.cpp | 30 +- .../samplemimo/bladerf2mimo/bladerf2mimogui.h | 5 + .../bladerf2mimo/bladerf2mimosettings.cpp | 8 + .../bladerf2mimo/bladerf2mimosettings.h | 2 + .../samplemimo/limesdrmimo/limesdrmimogui.cpp | 36 +- .../samplemimo/limesdrmimo/limesdrmimogui.h | 5 + .../limesdrmimo/limesdrmimosettings.cpp | 10 +- .../limesdrmimo/limesdrmimosettings.h | 5 +- plugins/samplemimo/metismiso/metismisogui.cpp | 30 +- plugins/samplemimo/metismiso/metismisogui.h | 5 + .../metismiso/metismisosettings.cpp | 5 + .../samplemimo/metismiso/metismisosettings.h | 2 + .../plutosdrmimo/plutosdrmimogui.cpp | 39 +- .../samplemimo/plutosdrmimo/plutosdrmimogui.h | 5 + .../plutosdrmimo/plutosdrmimosettings.cpp | 7 + .../plutosdrmimo/plutosdrmimosettings.h | 2 + plugins/samplemimo/testmi/testmigui.cpp | 32 +- plugins/samplemimo/testmi/testmigui.h | 5 + plugins/samplemimo/testmi/testmigui.ui | 23 +- plugins/samplemimo/testmi/testmisettings.cpp | 6 + plugins/samplemimo/testmi/testmisettings.h | 2 + .../samplemimo/testmosync/testmosyncgui.cpp | 16 +- plugins/samplemimo/testmosync/testmosyncgui.h | 5 + .../testmosync/testmosyncsettings.cpp | 7 +- .../testmosync/testmosyncsettings.h | 2 + plugins/samplemimo/xtrxmimo/xtrxmimogui.cpp | 34 +- plugins/samplemimo/xtrxmimo/xtrxmimogui.h | 5 + .../samplemimo/xtrxmimo/xtrxmimosettings.cpp | 5 + .../samplemimo/xtrxmimo/xtrxmimosettings.h | 3 + .../samplesink/audiooutput/audiooutputgui.cpp | 15 +- .../samplesink/audiooutput/audiooutputgui.h | 5 + .../audiooutput/audiooutputsettings.cpp | 5 + .../audiooutput/audiooutputsettings.h | 2 + .../bladerf1output/bladerf1outputgui.cpp | 17 +- .../bladerf1output/bladerf1outputgui.h | 5 + .../bladerf1output/bladerf1outputsettings.cpp | 5 + .../bladerf1output/bladerf1outputsettings.h | 2 + .../bladerf2output/bladerf2outputgui.cpp | 20 +- .../bladerf2output/bladerf2outputgui.h | 5 + .../bladerf2output/bladerf2outputsettings.cpp | 5 + .../bladerf2output/bladerf2outputsettings.h | 2 + .../samplesink/fileoutput/fileoutputgui.cpp | 16 +- plugins/samplesink/fileoutput/fileoutputgui.h | 5 + .../samplesink/fileoutput/fileoutputgui.ui | 25 +- .../fileoutput/fileoutputsettings.cpp | 5 + .../fileoutput/fileoutputsettings.h | 2 + .../hackrfoutput/hackrfoutputgui.cpp | 22 +- .../samplesink/hackrfoutput/hackrfoutputgui.h | 5 + .../hackrfoutput/hackrfoutputsettings.cpp | 5 + .../hackrfoutput/hackrfoutputsettings.h | 2 + .../limesdroutput/limesdroutputgui.cpp | 25 +- .../limesdroutput/limesdroutputgui.h | 5 + .../limesdroutput/limesdroutputsettings.cpp | 5 + .../limesdroutput/limesdroutputsettings.h | 2 + .../samplesink/localoutput/localoutputgui.cpp | 11 +- .../samplesink/localoutput/localoutputgui.h | 5 + .../localoutput/localoutputsettings.cpp | 6 + .../localoutput/localoutputsettings.h | 2 + .../plutosdroutput/plutosdroutputgui.cpp | 24 +- .../plutosdroutput/plutosdroutputgui.h | 5 + .../plutosdroutput/plutosdroutputsettings.cpp | 5 + .../plutosdroutput/plutosdroutputsettings.h | 2 + .../remoteoutput/remoteoutputgui.cpp | 22 +- .../samplesink/remoteoutput/remoteoutputgui.h | 5 + .../remoteoutput/remoteoutputsettings.cpp | 5 + .../remoteoutput/remoteoutputsettings.h | 2 + .../soapysdroutput/soapysdroutputgui.cpp | 15 +- .../soapysdroutput/soapysdroutputgui.h | 5 + .../soapysdroutput/soapysdroutputsettings.cpp | 5 + .../soapysdroutput/soapysdroutputsettings.h | 2 + plugins/samplesink/testsink/testsinkgui.cpp | 14 +- plugins/samplesink/testsink/testsinkgui.h | 5 + .../samplesink/testsink/testsinksettings.cpp | 7 + .../samplesink/testsink/testsinksettings.h | 2 + .../samplesink/usrpoutput/usrpoutputgui.cpp | 21 +- plugins/samplesink/usrpoutput/usrpoutputgui.h | 5 + .../usrpoutput/usrpoutputsettings.cpp | 5 + .../usrpoutput/usrpoutputsettings.h | 2 + .../samplesink/xtrxoutput/xtrxoutputgui.cpp | 23 +- plugins/samplesink/xtrxoutput/xtrxoutputgui.h | 5 + .../xtrxoutput/xtrxoutputsettings.cpp | 5 + .../xtrxoutput/xtrxoutputsettings.h | 2 + plugins/samplesource/airspy/airspygui.cpp | 24 +- plugins/samplesource/airspy/airspygui.h | 6 + .../samplesource/airspy/airspysettings.cpp | 5 + plugins/samplesource/airspy/airspysettings.h | 2 + plugins/samplesource/airspyhf/airspyhfgui.cpp | 24 +- plugins/samplesource/airspyhf/airspyhfgui.h | 6 + .../airspyhf/airspyhfsettings.cpp | 5 + .../samplesource/airspyhf/airspyhfsettings.h | 2 + .../samplesource/audioinput/audioinputgui.cpp | 16 +- .../samplesource/audioinput/audioinputgui.h | 5 + .../audioinput/audioinputsettings.cpp | 5 + .../audioinput/audioinputsettings.h | 2 + .../bladerf1input/bladerf1inputgui.cpp | 20 +- .../bladerf1input/bladerf1inputgui.h | 5 + .../bladerf1input/bladerf1inputsettings.cpp | 5 + .../bladerf1input/bladerf1inputsettings.h | 2 + .../bladerf2input/bladerf2inputgui.cpp | 24 +- .../bladerf2input/bladerf2inputgui.h | 5 + .../bladerf2input/bladerf2inputsettings.cpp | 5 + .../bladerf2input/bladerf2inputsettings.h | 2 + plugins/samplesource/fcdpro/fcdprogui.cpp | 29 +- plugins/samplesource/fcdpro/fcdprogui.h | 5 + .../samplesource/fcdpro/fcdprosettings.cpp | 5 + plugins/samplesource/fcdpro/fcdprosettings.h | 2 + .../samplesource/fcdproplus/fcdproplusgui.cpp | 24 +- .../samplesource/fcdproplus/fcdproplusgui.h | 5 + .../fcdproplus/fcdproplussettings.cpp | 5 + .../fcdproplus/fcdproplussettings.h | 2 + .../samplesource/fileinput/fileinputgui.cpp | 19 +- plugins/samplesource/fileinput/fileinputgui.h | 7 +- .../samplesource/fileinput/fileinputgui.ui | 25 +- .../fileinput/fileinputsettings.cpp | 5 + .../fileinput/fileinputsettings.h | 2 + .../hackrfinput/hackrfinputgui.cpp | 26 +- .../samplesource/hackrfinput/hackrfinputgui.h | 5 + .../hackrfinput/hackrfinputsettings.cpp | 5 + .../hackrfinput/hackrfinputsettings.h | 2 + plugins/samplesource/kiwisdr/kiwisdrgui.cpp | 17 +- plugins/samplesource/kiwisdr/kiwisdrgui.h | 5 + .../samplesource/kiwisdr/kiwisdrsettings.cpp | 6 + .../samplesource/kiwisdr/kiwisdrsettings.h | 3 + .../limesdrinput/limesdrinputgui.cpp | 31 +- .../limesdrinput/limesdrinputgui.h | 5 + .../limesdrinput/limesdrinputsettings.cpp | 6 + .../limesdrinput/limesdrinputsettings.h | 2 + .../samplesource/localinput/localinputgui.cpp | 13 +- .../samplesource/localinput/localinputgui.h | 5 + .../localinput/localinputsettings.cpp | 6 + .../localinput/localinputsettings.h | 2 + plugins/samplesource/perseus/perseusgui.cpp | 21 +- plugins/samplesource/perseus/perseusgui.h | 6 + .../samplesource/perseus/perseussettings.cpp | 5 + .../samplesource/perseus/perseussettings.h | 2 + .../plutosdrinput/plutosdrinputgui.cpp | 31 +- .../plutosdrinput/plutosdrinputgui.h | 5 + .../plutosdrinput/plutosdrinputsettings.cpp | 5 + .../plutosdrinput/plutosdrinputsettings.h | 2 + .../remoteinput/remoteinputgui.cpp | 25 +- .../samplesource/remoteinput/remoteinputgui.h | 5 + .../remoteinput/remoteinputsettings.cpp | 6 + .../remoteinput/remoteinputsettings.h | 2 + plugins/samplesource/rtlsdr/rtlsdrgui.cpp | 27 +- plugins/samplesource/rtlsdr/rtlsdrgui.h | 5 + plugins/samplesource/rtlsdr/rtlsdrgui.ui | 48 +- .../samplesource/rtlsdr/rtlsdrsettings.cpp | 5 + plugins/samplesource/rtlsdr/rtlsdrsettings.h | 2 + plugins/samplesource/sdrplay/sdrplaygui.cpp | 27 +- plugins/samplesource/sdrplay/sdrplaygui.h | 5 + .../samplesource/sdrplay/sdrplaysettings.cpp | 5 + .../samplesource/sdrplay/sdrplaysettings.h | 2 + .../samplesource/sdrplayv3/sdrplayv3gui.cpp | 31 +- plugins/samplesource/sdrplayv3/sdrplayv3gui.h | 5 + .../sdrplayv3/sdrplayv3settings.cpp | 5 + .../sdrplayv3/sdrplayv3settings.h | 2 + .../sigmffileinput/sigmffileinputgui.cpp | 21 +- .../sigmffileinput/sigmffileinputgui.h | 5 + .../sigmffileinput/sigmffileinputsettings.cpp | 5 + .../sigmffileinput/sigmffileinputsettings.h | 2 + .../soapysdrinput/soapysdrinputgui.cpp | 18 +- .../soapysdrinput/soapysdrinputgui.h | 5 + .../soapysdrinput/soapysdrinputsettings.cpp | 5 + .../soapysdrinput/soapysdrinputsettings.h | 2 + .../samplesource/testsource/testsourcegui.cpp | 28 +- .../samplesource/testsource/testsourcegui.h | 5 + .../testsource/testsourcesettings.cpp | 6 + .../testsource/testsourcesettings.h | 2 + .../samplesource/usrpinput/usrpinputgui.cpp | 24 +- plugins/samplesource/usrpinput/usrpinputgui.h | 7 +- .../usrpinput/usrpinputsettings.cpp | 6 + .../usrpinput/usrpinputsettings.h | 2 + .../samplesource/xtrxinput/xtrxinputgui.cpp | 29 +- plugins/samplesource/xtrxinput/xtrxinputgui.h | 5 + .../xtrxinput/xtrxinputsettings.cpp | 5 + .../xtrxinput/xtrxinputsettings.h | 2 + sdrbase/pipes/messagepipeslegacy.cpp | 78 ++ sdrbase/pipes/messagepipeslegacy.h | 59 ++ sdrgui/CMakeLists.txt | 4 +- sdrgui/device/devicegui.cpp | 313 ++++++ sdrgui/device/devicegui.h | 101 +- sdrgui/feature/featuregui.cpp | 2 + sdrgui/gui/samplingdevicedialog.cpp | 12 +- sdrgui/gui/samplingdevicedialog.h | 4 +- sdrgui/gui/samplingdevicesdock.cpp | 2 +- sdrgui/gui/workspace.cpp | 29 +- sdrgui/gui/workspace.h | 9 +- sdrgui/mainwindow.cpp | 932 ++++++++---------- sdrgui/mainwindow.h | 46 +- 193 files changed, 2598 insertions(+), 691 deletions(-) create mode 100644 sdrbase/pipes/messagepipeslegacy.cpp create mode 100644 sdrbase/pipes/messagepipeslegacy.h create mode 100644 sdrgui/device/devicegui.cpp diff --git a/plugins/channelmimo/beamsteeringcwmod/beamsteeringcwmodsettings.cpp b/plugins/channelmimo/beamsteeringcwmod/beamsteeringcwmodsettings.cpp index 95df8dab7..8c056d7e0 100644 --- a/plugins/channelmimo/beamsteeringcwmod/beamsteeringcwmodsettings.cpp +++ b/plugins/channelmimo/beamsteeringcwmod/beamsteeringcwmodsettings.cpp @@ -43,6 +43,7 @@ void BeamSteeringCWModSettings::resetToDefaults() m_reverseAPIPort = 8888; m_reverseAPIDeviceIndex = 0; m_reverseAPIChannelIndex = 0; + m_workspaceIndex = 0; } QByteArray BeamSteeringCWModSettings::serialize() const @@ -64,6 +65,9 @@ QByteArray BeamSteeringCWModSettings::serialize() const s.writeBlob(15, m_rollupState->serialize()); } + s.writeS32(16, m_workspaceIndex); + s.writeBlob(17, m_geometryBytes); + return s.final(); } @@ -114,6 +118,9 @@ bool BeamSteeringCWModSettings::deserialize(const QByteArray& data) m_rollupState->deserialize(bytetmp); } + d.readS32(16, &m_workspaceIndex); + d.readBlob(17, &m_geometryBytes); + return true; } else diff --git a/plugins/channelmimo/beamsteeringcwmod/beamsteeringcwmodsettings.h b/plugins/channelmimo/beamsteeringcwmod/beamsteeringcwmodsettings.h index cdac05bea..1dfbba1cd 100644 --- a/plugins/channelmimo/beamsteeringcwmod/beamsteeringcwmodsettings.h +++ b/plugins/channelmimo/beamsteeringcwmod/beamsteeringcwmodsettings.h @@ -36,6 +36,8 @@ struct BeamSteeringCWModSettings uint16_t m_reverseAPIPort; uint16_t m_reverseAPIDeviceIndex; uint16_t m_reverseAPIChannelIndex; + int m_workspaceIndex; + QByteArray m_geometryBytes; Serializable *m_channelMarker; Serializable *m_rollupState; diff --git a/plugins/channelmimo/interferometer/interferometersettings.cpp b/plugins/channelmimo/interferometer/interferometersettings.cpp index 128ba8528..e0c5846cf 100644 --- a/plugins/channelmimo/interferometer/interferometersettings.cpp +++ b/plugins/channelmimo/interferometer/interferometersettings.cpp @@ -44,6 +44,7 @@ void InterferometerSettings::resetToDefaults() m_reverseAPIPort = 8888; m_reverseAPIDeviceIndex = 0; m_reverseAPIChannelIndex = 0; + m_workspaceIndex = 0; } QByteArray InterferometerSettings::serialize() const @@ -61,6 +62,8 @@ QByteArray InterferometerSettings::serialize() const s.writeU32(10, m_reverseAPIDeviceIndex); s.writeU32(11, m_reverseAPIChannelIndex); s.writeS32(12, m_phase); + s.writeS32(13,m_workspaceIndex); + s.writeBlob(14, m_geometryBytes); if (m_spectrumGUI) { s.writeBlob(20, m_spectrumGUI->serialize()); @@ -117,6 +120,8 @@ bool InterferometerSettings::deserialize(const QByteArray& data) m_reverseAPIChannelIndex = utmp > 99 ? 99 : utmp; d.readS32(12, &tmp, 0); m_phase = tmp < -180 ? -180 : tmp > 180 ? 180 : tmp; + d.readS32(13, &m_workspaceIndex); + d.readBlob(14, &m_geometryBytes); if (m_spectrumGUI) { diff --git a/plugins/channelmimo/interferometer/interferometersettings.h b/plugins/channelmimo/interferometer/interferometersettings.h index 387902402..ead9dc259 100644 --- a/plugins/channelmimo/interferometer/interferometersettings.h +++ b/plugins/channelmimo/interferometer/interferometersettings.h @@ -48,6 +48,8 @@ struct InterferometerSettings uint16_t m_reverseAPIPort; uint16_t m_reverseAPIDeviceIndex; uint16_t m_reverseAPIChannelIndex; + int m_workspaceIndex; + QByteArray m_geometryBytes; Serializable *m_channelMarker; Serializable *m_spectrumGUI; diff --git a/plugins/samplemimo/bladerf2mimo/bladerf2mimogui.cpp b/plugins/samplemimo/bladerf2mimo/bladerf2mimogui.cpp index ecbc3ab34..608e7b388 100644 --- a/plugins/samplemimo/bladerf2mimo/bladerf2mimogui.cpp +++ b/plugins/samplemimo/bladerf2mimo/bladerf2mimogui.cpp @@ -66,7 +66,10 @@ BladeRF2MIMOGui::BladeRF2MIMOGui(DeviceUISet *deviceUISet, QWidget* parent) : m_sampleRateMode(true) { qDebug("BladeRF2MIMOGui::BladeRF2MIMOGui"); - ui->setupUi(this); + setAttribute(Qt::WA_DeleteOnClose, true); + ui->setupUi(getContents()); + getContents()->setStyleSheet("#BladeRF2MIMOGui { border: 1px solid #C06900 }"); + m_helpURL = "plugins/samplemimo/bladerf2mimo/readme.md"; m_sampleMIMO = (BladeRF2MIMO*) m_deviceUISet->m_deviceAPI->getSampleMIMO(); m_sampleMIMO->getRxFrequencyRange(m_fMinRx, m_fMaxRx, m_fStepRx, m_fScaleRx); @@ -100,6 +103,7 @@ BladeRF2MIMOGui::BladeRF2MIMOGui(DeviceUISet *deviceUISet, QWidget* parent) : connect(startStopRightClickEnabler, SIGNAL(rightClick(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &))); sendSettings(); + makeUIConnections(); } BladeRF2MIMOGui::~BladeRF2MIMOGui() @@ -821,3 +825,27 @@ int BladeRF2MIMOGui::getGainValue(float gainDB, int gainMin, int gainMax, int ga gainDB, gainMin, gainMax, gainStep, gainScale, gain); return gain; } + +void BladeRF2MIMOGui::makeUIConnections() +{ + QObject::connect(ui->streamSide, QOverload::of(&QComboBox::currentIndexChanged), this, &BladeRF2MIMOGui::on_streamSide_currentIndexChanged); + QObject::connect(ui->streamIndex, QOverload::of(&QComboBox::currentIndexChanged), this, &BladeRF2MIMOGui::on_streamIndex_currentIndexChanged); + QObject::connect(ui->spectrumSide, QOverload::of(&QComboBox::currentIndexChanged), this, &BladeRF2MIMOGui::on_spectrumSide_currentIndexChanged); + QObject::connect(ui->spectrumIndex, QOverload::of(&QComboBox::currentIndexChanged), this, &BladeRF2MIMOGui::on_spectrumIndex_currentIndexChanged); + QObject::connect(ui->startStopRx, &ButtonSwitch::toggled, this, &BladeRF2MIMOGui::on_startStopRx_toggled); + QObject::connect(ui->startStopTx, &ButtonSwitch::toggled, this, &BladeRF2MIMOGui::on_startStopTx_toggled); + QObject::connect(ui->centerFrequency, &ValueDial::changed, this, &BladeRF2MIMOGui::on_centerFrequency_changed); + QObject::connect(ui->LOppm, &QSlider::valueChanged, this, &BladeRF2MIMOGui::on_LOppm_valueChanged); + QObject::connect(ui->dcOffset, &ButtonSwitch::toggled, this, &BladeRF2MIMOGui::on_dcOffset_toggled); + QObject::connect(ui->iqImbalance, &ButtonSwitch::toggled, this, &BladeRF2MIMOGui::on_iqImbalance_toggled); + QObject::connect(ui->bandwidth, &ValueDial::changed, this, &BladeRF2MIMOGui::on_bandwidth_changed); + QObject::connect(ui->sampleRate, &ValueDial::changed, this, &BladeRF2MIMOGui::on_sampleRate_changed); + QObject::connect(ui->fcPos, QOverload::of(&QComboBox::currentIndexChanged), this, &BladeRF2MIMOGui::on_fcPos_currentIndexChanged); + QObject::connect(ui->decim, QOverload::of(&QComboBox::currentIndexChanged), this, &BladeRF2MIMOGui::on_decim_currentIndexChanged); + QObject::connect(ui->gainLock, &QToolButton::toggled, this, &BladeRF2MIMOGui::on_gainLock_toggled); + QObject::connect(ui->gainMode, QOverload::of(&QComboBox::currentIndexChanged), this, &BladeRF2MIMOGui::on_gainMode_currentIndexChanged); + QObject::connect(ui->gain, &QSlider::valueChanged, this, &BladeRF2MIMOGui::on_gain_valueChanged); + QObject::connect(ui->biasTee, &ButtonSwitch::toggled, this, &BladeRF2MIMOGui::on_biasTee_toggled); + QObject::connect(ui->transverter, &TransverterButton::clicked, this, &BladeRF2MIMOGui::on_transverter_clicked); + +} diff --git a/plugins/samplemimo/bladerf2mimo/bladerf2mimogui.h b/plugins/samplemimo/bladerf2mimo/bladerf2mimogui.h index 23101e88d..471d36a79 100644 --- a/plugins/samplemimo/bladerf2mimo/bladerf2mimogui.h +++ b/plugins/samplemimo/bladerf2mimo/bladerf2mimogui.h @@ -44,6 +44,10 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } + virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; } + virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; } + virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; } + virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; } private: Ui::BladeRF2MIMOGui* ui; @@ -97,6 +101,7 @@ private: int getGainValue(float gainDB, int gainMin, int gainMax, int gainStep, float gainScale); float setGainFromValue(int value); bool handleMessage(const Message& message); + void makeUIConnections(); private slots: void handleInputMessages(); diff --git a/plugins/samplemimo/bladerf2mimo/bladerf2mimosettings.cpp b/plugins/samplemimo/bladerf2mimo/bladerf2mimosettings.cpp index 650dd62f1..0a2058d59 100644 --- a/plugins/samplemimo/bladerf2mimo/bladerf2mimosettings.cpp +++ b/plugins/samplemimo/bladerf2mimo/bladerf2mimosettings.cpp @@ -58,6 +58,8 @@ void BladeRF2MIMOSettings::resetToDefaults() m_reverseAPIAddress = "127.0.0.1"; m_reverseAPIPort = 8888; m_reverseAPIDeviceIndex = 0; + + m_workspaceIndex = 0; } QByteArray BladeRF2MIMOSettings::serialize() const @@ -97,6 +99,9 @@ QByteArray BladeRF2MIMOSettings::serialize() const s.writeU32(53, m_reverseAPIPort); s.writeU32(54, m_reverseAPIDeviceIndex); + s.writeS32(55, m_workspaceIndex); + s.writeBlob(56, m_geometryBytes); + return s.final(); } @@ -158,6 +163,9 @@ bool BladeRF2MIMOSettings::deserialize(const QByteArray& data) d.readU32(54, &uintval, 0); m_reverseAPIDeviceIndex = uintval > 99 ? 99 : uintval; + d.readS32(55,&m_workspaceIndex, 0); + d.readBlob(56, &m_geometryBytes); + return true; } else diff --git a/plugins/samplemimo/bladerf2mimo/bladerf2mimosettings.h b/plugins/samplemimo/bladerf2mimo/bladerf2mimosettings.h index 2f2104630..98e69d66d 100644 --- a/plugins/samplemimo/bladerf2mimo/bladerf2mimosettings.h +++ b/plugins/samplemimo/bladerf2mimo/bladerf2mimosettings.h @@ -60,6 +60,8 @@ struct BladeRF2MIMOSettings { QString m_reverseAPIAddress; uint16_t m_reverseAPIPort; uint16_t m_reverseAPIDeviceIndex; + int m_workspaceIndex; + QByteArray m_geometryBytes; BladeRF2MIMOSettings(); void resetToDefaults(); diff --git a/plugins/samplemimo/limesdrmimo/limesdrmimogui.cpp b/plugins/samplemimo/limesdrmimo/limesdrmimogui.cpp index 6a0b4c649..2022ce44e 100644 --- a/plugins/samplemimo/limesdrmimo/limesdrmimogui.cpp +++ b/plugins/samplemimo/limesdrmimo/limesdrmimogui.cpp @@ -68,7 +68,10 @@ LimeSDRMIMOGUI::LimeSDRMIMOGUI(DeviceUISet *deviceUISet, QWidget* parent) : m_sampleRateMode(true) { qDebug("LimeSDRMIMOGUI::LimeSDRMIMOGUI"); - ui->setupUi(this); + setAttribute(Qt::WA_DeleteOnClose, true); + ui->setupUi(getContents()); + getContents()->setStyleSheet("#LimeSDRMIMOGUI { border: 1px solid #C06900 }"); + m_helpURL = "plugins/samplemimo/limesdrmimo/readme.md"; m_limeSDRMIMO = (LimeSDRMIMO*) m_deviceUISet->m_deviceAPI->getSampleMIMO(); m_limeSDRMIMO->getRxFrequencyRange(m_fMinRx, m_fMaxRx, m_fStepRx); @@ -100,6 +103,7 @@ LimeSDRMIMOGUI::LimeSDRMIMOGUI(DeviceUISet *deviceUISet, QWidget* parent) : connect(startStopRightClickEnabler, SIGNAL(rightClick(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &))); sendSettings(); + makeUIConnections(); } LimeSDRMIMOGUI::~LimeSDRMIMOGUI() @@ -1137,3 +1141,33 @@ void LimeSDRMIMOGUI::openDeviceSettingsDialog(const QPoint& p) sendSettings(); } + +void LimeSDRMIMOGUI::makeUIConnections() +{ + QObject::connect(ui->streamSide, QOverload::of(&QComboBox::currentIndexChanged), this, &LimeSDRMIMOGUI::on_streamSide_currentIndexChanged); + QObject::connect(ui->streamIndex, QOverload::of(&QComboBox::currentIndexChanged), this, &LimeSDRMIMOGUI::on_streamIndex_currentIndexChanged); + QObject::connect(ui->spectrumSide, QOverload::of(&QComboBox::currentIndexChanged), this, &LimeSDRMIMOGUI::on_spectrumSide_currentIndexChanged); + QObject::connect(ui->spectrumIndex, QOverload::of(&QComboBox::currentIndexChanged), this, &LimeSDRMIMOGUI::on_spectrumIndex_currentIndexChanged); + QObject::connect(ui->startStopRx, &ButtonSwitch::toggled, this, &LimeSDRMIMOGUI::on_startStopRx_toggled); + QObject::connect(ui->startStopTx, &ButtonSwitch::toggled, this, &LimeSDRMIMOGUI::on_startStopTx_toggled); + QObject::connect(ui->centerFrequency, &ValueDial::changed, this, &LimeSDRMIMOGUI::on_centerFrequency_changed); + QObject::connect(ui->ncoEnable, &ButtonSwitch::toggled, this, &LimeSDRMIMOGUI::on_ncoEnable_toggled); + QObject::connect(ui->ncoFrequency, &ValueDialZ::changed, this, &LimeSDRMIMOGUI::on_ncoFrequency_changed); + QObject::connect(ui->dcOffset, &ButtonSwitch::toggled, this, &LimeSDRMIMOGUI::on_dcOffset_toggled); + QObject::connect(ui->iqImbalance, &ButtonSwitch::toggled, this, &LimeSDRMIMOGUI::on_iqImbalance_toggled); + QObject::connect(ui->extClock, &ExternalClockButton::clicked, this, &LimeSDRMIMOGUI::on_extClock_clicked); + QObject::connect(ui->hwDecim, QOverload::of(&QComboBox::currentIndexChanged), this, &LimeSDRMIMOGUI::on_hwDecim_currentIndexChanged); + QObject::connect(ui->swDecim, QOverload::of(&QComboBox::currentIndexChanged), this, &LimeSDRMIMOGUI::on_swDecim_currentIndexChanged); + QObject::connect(ui->sampleRateMode, &QToolButton::toggled, this, &LimeSDRMIMOGUI::on_sampleRateMode_toggled); + QObject::connect(ui->sampleRate, &ValueDial::changed, this, &LimeSDRMIMOGUI::on_sampleRate_changed); + QObject::connect(ui->lpf, &ValueDial::changed, this, &LimeSDRMIMOGUI::on_lpf_changed); + QObject::connect(ui->lpFIREnable, &ButtonSwitch::toggled, this, &LimeSDRMIMOGUI::on_lpFIREnable_toggled); + QObject::connect(ui->lpFIR, &ValueDial::changed, this, &LimeSDRMIMOGUI::on_lpFIR_changed); + QObject::connect(ui->transverter, &TransverterButton::clicked, this, &LimeSDRMIMOGUI::on_transverter_clicked); + QObject::connect(ui->gainMode, QOverload::of(&QComboBox::currentIndexChanged), this, &LimeSDRMIMOGUI::on_gainMode_currentIndexChanged); + QObject::connect(ui->gain, &QDial::valueChanged, this, &LimeSDRMIMOGUI::on_gain_valueChanged); + QObject::connect(ui->lnaGain, &QDial::valueChanged, this, &LimeSDRMIMOGUI::on_lnaGain_valueChanged); + QObject::connect(ui->tiaGain, QOverload::of(&QComboBox::currentIndexChanged), this, &LimeSDRMIMOGUI::on_tiaGain_currentIndexChanged); + QObject::connect(ui->pgaGain, &QDial::valueChanged, this, &LimeSDRMIMOGUI::on_pgaGain_valueChanged); + QObject::connect(ui->antenna, QOverload::of(&QComboBox::currentIndexChanged), this, &LimeSDRMIMOGUI::on_antenna_currentIndexChanged); +} diff --git a/plugins/samplemimo/limesdrmimo/limesdrmimogui.h b/plugins/samplemimo/limesdrmimo/limesdrmimogui.h index e42720cdd..666f8d3a6 100644 --- a/plugins/samplemimo/limesdrmimo/limesdrmimogui.h +++ b/plugins/samplemimo/limesdrmimo/limesdrmimogui.h @@ -44,6 +44,10 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } + virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; } + virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; } + virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; } + virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; } private: Ui::LimeSDRMIMOGUI* ui; @@ -96,6 +100,7 @@ private: void updateSampleRateAndFrequency(); void sendSettings(); bool handleMessage(const Message& message); + void makeUIConnections(); private slots: void handleInputMessages(); diff --git a/plugins/samplemimo/limesdrmimo/limesdrmimosettings.cpp b/plugins/samplemimo/limesdrmimo/limesdrmimosettings.cpp index 7deaecb71..c25f25965 100644 --- a/plugins/samplemimo/limesdrmimo/limesdrmimosettings.cpp +++ b/plugins/samplemimo/limesdrmimo/limesdrmimosettings.cpp @@ -86,6 +86,8 @@ void LimeSDRMIMOSettings::resetToDefaults() m_lpfFIRBWTx1 = 2.5e6f; m_gainTx1 = 4; m_antennaPathTx1 = PATH_RFE_TX_NONE; + + m_workspaceIndex = 0; } QByteArray LimeSDRMIMOSettings::serialize() const @@ -153,6 +155,9 @@ QByteArray LimeSDRMIMOSettings::serialize() const s.writeU32(93, m_gainTx1); s.writeS32(94, (int) m_antennaPathTx1); + s.writeS32(95, m_workspaceIndex); + s.writeBlob(96, m_geometryBytes); + return s.final(); } @@ -248,6 +253,9 @@ bool LimeSDRMIMOSettings::deserialize(const QByteArray& data) d.readS32(94, &intval, 0); m_antennaPathTx1 = (PathTxRFE) intval; + d.readS32(95, &m_workspaceIndex, 0); + d.readBlob(96, &m_geometryBytes); + return true; } else @@ -255,4 +263,4 @@ bool LimeSDRMIMOSettings::deserialize(const QByteArray& data) resetToDefaults(); return false; } -} \ No newline at end of file +} diff --git a/plugins/samplemimo/limesdrmimo/limesdrmimosettings.h b/plugins/samplemimo/limesdrmimo/limesdrmimosettings.h index 6bb8617a5..ae189e162 100644 --- a/plugins/samplemimo/limesdrmimo/limesdrmimosettings.h +++ b/plugins/samplemimo/limesdrmimo/limesdrmimosettings.h @@ -119,10 +119,13 @@ struct LimeSDRMIMOSettings uint32_t m_gainTx1; //!< Tx[1] Optimally distributed gain (dB) PathTxRFE m_antennaPathTx1; //!< Tx[1] Antenna connection + int m_workspaceIndex; + QByteArray m_geometryBytes; + LimeSDRMIMOSettings(); void resetToDefaults(); QByteArray serialize() const; bool deserialize(const QByteArray& data); }; -#endif // PLUGINS_SAMPLEMIMO_LIMESDRMIMO_LIMESDRMIMOSETTINGS_H_ \ No newline at end of file +#endif // PLUGINS_SAMPLEMIMO_LIMESDRMIMO_LIMESDRMIMOSETTINGS_H_ diff --git a/plugins/samplemimo/metismiso/metismisogui.cpp b/plugins/samplemimo/metismiso/metismisogui.cpp index 3e720162c..98e91cd69 100644 --- a/plugins/samplemimo/metismiso/metismisogui.cpp +++ b/plugins/samplemimo/metismiso/metismisogui.cpp @@ -52,11 +52,14 @@ MetisMISOGui::MetisMISOGui(DeviceUISet *deviceUISet, QWidget* parent) : m_lastEngineState(DeviceAPI::StNotStarted) { qDebug("MetisMISOGui::MetisMISOGui"); + setAttribute(Qt::WA_DeleteOnClose, true); m_sampleMIMO = m_deviceUISet->m_deviceAPI->getSampleMIMO(); m_rxSampleRate = 48000; m_txSampleRate = 48000; - ui->setupUi(this); + ui->setupUi(getContents()); + getContents()->setStyleSheet("#MetisMISOGui { border: 1px solid #C06900 }"); + m_helpURL = "plugins/samplemimo/metismiso/readme.md"; ui->centerFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold)); ui->centerFrequency->setValueRange(7, 0, m_absMaxFreq); @@ -71,6 +74,8 @@ MetisMISOGui::MetisMISOGui(DeviceUISet *deviceUISet, QWidget* parent) : CRightClickEnabler *startStopRightClickEnabler = new CRightClickEnabler(ui->startStop); connect(startStopRightClickEnabler, SIGNAL(rightClick(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &))); + + makeUIConnections(); } MetisMISOGui::~MetisMISOGui() @@ -574,3 +579,26 @@ void MetisMISOGui::openDeviceSettingsDialog(const QPoint& p) sendSettings(); } + +void MetisMISOGui::makeUIConnections() +{ + QObject::connect(ui->streamIndex, QOverload::of(&QComboBox::currentIndexChanged), this, &MetisMISOGui::on_streamIndex_currentIndexChanged); + QObject::connect(ui->spectrumSource, QOverload::of(&QComboBox::currentIndexChanged), this, &MetisMISOGui::on_spectrumSource_currentIndexChanged); + QObject::connect(ui->streamLock, &QToolButton::toggled, this, &MetisMISOGui::on_streamLock_toggled); + QObject::connect(ui->LOppm, &QSlider::valueChanged, this, &MetisMISOGui::on_LOppm_valueChanged); + QObject::connect(ui->startStop, &ButtonSwitch::toggled, this, &MetisMISOGui::on_startStop_toggled); + QObject::connect(ui->centerFrequency, &ValueDial::changed, this, &MetisMISOGui::on_centerFrequency_changed); + QObject::connect(ui->samplerateIndex, QOverload::of(&QComboBox::currentIndexChanged), this, &MetisMISOGui::on_samplerateIndex_currentIndexChanged); + QObject::connect(ui->log2Decim, QOverload::of(&QComboBox::currentIndexChanged), this, &MetisMISOGui::on_log2Decim_currentIndexChanged); + QObject::connect(ui->subsamplingIndex, QOverload::of(&QComboBox::currentIndexChanged), this, &MetisMISOGui::on_subsamplingIndex_currentIndexChanged); + QObject::connect(ui->dcBlock, &ButtonSwitch::toggled, this, &MetisMISOGui::on_dcBlock_toggled); + QObject::connect(ui->iqCorrection, &ButtonSwitch::toggled, this, &MetisMISOGui::on_iqCorrection_toggled); + QObject::connect(ui->transverter, &TransverterButton::clicked, this, &MetisMISOGui::on_transverter_clicked); + QObject::connect(ui->preamp, &ButtonSwitch::toggled, this, &MetisMISOGui::on_preamp_toggled); + QObject::connect(ui->random, &ButtonSwitch::toggled, this, &MetisMISOGui::on_random_toggled); + QObject::connect(ui->dither, &ButtonSwitch::toggled, this, &MetisMISOGui::on_dither_toggled); + QObject::connect(ui->duplex, &ButtonSwitch::toggled, this, &MetisMISOGui::on_duplex_toggled); + QObject::connect(ui->nbRxIndex, QOverload::of(&QComboBox::currentIndexChanged), this, &MetisMISOGui::on_nbRxIndex_currentIndexChanged); + QObject::connect(ui->txEnable, &ButtonSwitch::toggled, this, &MetisMISOGui::on_txEnable_toggled); + QObject::connect(ui->txDrive, &QDial::valueChanged, this, &MetisMISOGui::on_txDrive_valueChanged); +} diff --git a/plugins/samplemimo/metismiso/metismisogui.h b/plugins/samplemimo/metismiso/metismisogui.h index 48cad3f44..b54780ef7 100644 --- a/plugins/samplemimo/metismiso/metismisogui.h +++ b/plugins/samplemimo/metismiso/metismisogui.h @@ -45,6 +45,10 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } + virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; } + virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; } + virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; } + virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; } private: Ui::MetisMISOGui* ui; @@ -74,6 +78,7 @@ private: void sendSettings(); void setCenterFrequency(qint64 centerFrequency); bool handleMessage(const Message& message); + void makeUIConnections(); private slots: void handleInputMessages(); diff --git a/plugins/samplemimo/metismiso/metismisosettings.cpp b/plugins/samplemimo/metismiso/metismisosettings.cpp index 2e3a0e45a..e636bbfd6 100644 --- a/plugins/samplemimo/metismiso/metismisosettings.cpp +++ b/plugins/samplemimo/metismiso/metismisosettings.cpp @@ -82,6 +82,7 @@ void MetisMISOSettings::resetToDefaults() m_reverseAPIAddress = "127.0.0.1"; m_reverseAPIPort = 8888; m_reverseAPIDeviceIndex = 0; + m_workspaceIndex = 0; } QByteArray MetisMISOSettings::serialize() const @@ -112,6 +113,8 @@ QByteArray MetisMISOSettings::serialize() const s.writeU32(22, m_reverseAPIDeviceIndex); s.writeS32(23, m_streamIndex); s.writeS32(24, m_spectrumStreamIndex); + s.writeS32(25, m_workspaceIndex); + s.writeBlob(26, m_geometryBytes); for (int i = 0; i < m_maxReceivers; i++) { @@ -175,6 +178,8 @@ bool MetisMISOSettings::deserialize(const QByteArray& data) d.readS32(23, &m_streamIndex, 0); d.readS32(24, &m_spectrumStreamIndex, 0); + d.readS32(25, &m_workspaceIndex, 0); + d.readBlob(26, &m_geometryBytes); return true; } diff --git a/plugins/samplemimo/metismiso/metismisosettings.h b/plugins/samplemimo/metismiso/metismisosettings.h index 3fd3ee396..26257d225 100644 --- a/plugins/samplemimo/metismiso/metismisosettings.h +++ b/plugins/samplemimo/metismiso/metismisosettings.h @@ -48,6 +48,8 @@ struct MetisMISOSettings { QString m_reverseAPIAddress; uint16_t m_reverseAPIPort; uint16_t m_reverseAPIDeviceIndex; + int m_workspaceIndex; + QByteArray m_geometryBytes; MetisMISOSettings(); MetisMISOSettings(const MetisMISOSettings& other); diff --git a/plugins/samplemimo/plutosdrmimo/plutosdrmimogui.cpp b/plugins/samplemimo/plutosdrmimo/plutosdrmimogui.cpp index 95f750f94..6b741d057 100644 --- a/plugins/samplemimo/plutosdrmimo/plutosdrmimogui.cpp +++ b/plugins/samplemimo/plutosdrmimo/plutosdrmimogui.cpp @@ -68,7 +68,10 @@ PlutoSDRMIMOGUI::PlutoSDRMIMOGUI(DeviceUISet *deviceUISet, QWidget* parent) : m_sampleRateMode(true) { qDebug("PlutoSDRMIMOGui::PlutoSDRMIMOGui"); - ui->setupUi(this); + setAttribute(Qt::WA_DeleteOnClose, true); + ui->setupUi(getContents()); + getContents()->setStyleSheet("#PlutoSDRMIMOGUI { border: 1px solid #C06900 }"); + m_helpURL = "plugins/samplemimo/plutosdrmimo/readme.md"; m_sampleMIMO = (PlutoSDRMIMO*) m_deviceUISet->m_deviceAPI->getSampleMIMO(); ui->centerFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold)); @@ -102,6 +105,8 @@ PlutoSDRMIMOGUI::PlutoSDRMIMOGUI(DeviceUISet *deviceUISet, QWidget* parent) : connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()), Qt::QueuedConnection); m_sampleMIMO->setMessageQueueToGUI(&m_inputMessageQueue); + + makeUIConnections(); } PlutoSDRMIMOGUI::~PlutoSDRMIMOGUI() @@ -891,3 +896,35 @@ void PlutoSDRMIMOGUI::openDeviceSettingsDialog(const QPoint& p) sendSettings(); } + +void PlutoSDRMIMOGUI::makeUIConnections() +{ + QObject::connect(ui->streamSide, QOverload::of(&QComboBox::currentIndexChanged), this, &PlutoSDRMIMOGUI::on_streamSide_currentIndexChanged); + QObject::connect(ui->streamIndex, QOverload::of(&QComboBox::currentIndexChanged), this, &PlutoSDRMIMOGUI::on_streamIndex_currentIndexChanged); + QObject::connect(ui->spectrumSide, QOverload::of(&QComboBox::currentIndexChanged), this, &PlutoSDRMIMOGUI::on_spectrumSide_currentIndexChanged); + QObject::connect(ui->spectrumIndex, QOverload::of(&QComboBox::currentIndexChanged), this, &PlutoSDRMIMOGUI::on_spectrumIndex_currentIndexChanged); + QObject::connect(ui->startStopRx, &ButtonSwitch::toggled, this, &PlutoSDRMIMOGUI::on_startStopRx_toggled); + QObject::connect(ui->startStopTx, &ButtonSwitch::toggled, this, &PlutoSDRMIMOGUI::on_startStopTx_toggled); + QObject::connect(ui->centerFrequency, &ValueDial::changed, this, &PlutoSDRMIMOGUI::on_centerFrequency_changed); + QObject::connect(ui->loPPM, &QSlider::valueChanged, this, &PlutoSDRMIMOGUI::on_loPPM_valueChanged); + QObject::connect(ui->dcOffset, &ButtonSwitch::toggled, this, &PlutoSDRMIMOGUI::on_dcOffset_toggled); + QObject::connect(ui->iqImbalance, &ButtonSwitch::toggled, this, &PlutoSDRMIMOGUI::on_iqImbalance_toggled); + QObject::connect(ui->sampleRate, &ValueDial::changed, this, &PlutoSDRMIMOGUI::on_sampleRate_changed); + QObject::connect(ui->sampleRateMode, &QToolButton::toggled, this, &PlutoSDRMIMOGUI::on_sampleRateMode_toggled); + QObject::connect(ui->fcPos, QOverload::of(&QComboBox::currentIndexChanged),this, &PlutoSDRMIMOGUI::on_fcPos_currentIndexChanged); + QObject::connect(ui->swDecim, QOverload::of(&QComboBox::currentIndexChanged), this, &PlutoSDRMIMOGUI::on_swDecim_currentIndexChanged); + QObject::connect(ui->gainLock, &QToolButton::toggled, this, &PlutoSDRMIMOGUI::on_gainLock_toggled); + QObject::connect(ui->gainMode, QOverload::of(&QComboBox::currentIndexChanged), this, &PlutoSDRMIMOGUI::on_gainMode_currentIndexChanged); + QObject::connect(ui->gain, &QDial::valueChanged, this, &PlutoSDRMIMOGUI::on_gain_valueChanged); + QObject::connect(ui->att, &QDial::valueChanged, this, &PlutoSDRMIMOGUI::on_att_valueChanged); + QObject::connect(ui->transverter, &TransverterButton::clicked, this, &PlutoSDRMIMOGUI::on_transverter_clicked); + QObject::connect(ui->rfDCOffset, &ButtonSwitch::toggled, this, &PlutoSDRMIMOGUI::on_rfDCOffset_toggled); + QObject::connect(ui->bbDCOffset, &ButtonSwitch::toggled, this, &PlutoSDRMIMOGUI::on_bbDCOffset_toggled); + QObject::connect(ui->hwIQImbalance, &ButtonSwitch::toggled, this, &PlutoSDRMIMOGUI::on_hwIQImbalance_toggled); + QObject::connect(ui->lpf, &ValueDial::changed, this, &PlutoSDRMIMOGUI::on_lpf_changed); + QObject::connect(ui->lpFIREnable, &ButtonSwitch::toggled, this, &PlutoSDRMIMOGUI::on_lpFIREnable_toggled); + QObject::connect(ui->lpFIR, &ValueDial::changed, this, &PlutoSDRMIMOGUI::on_lpFIR_changed); + QObject::connect(ui->lpFIRDecimation, QOverload::of(&QComboBox::currentIndexChanged), this, &PlutoSDRMIMOGUI::on_lpFIRDecimation_currentIndexChanged); + QObject::connect(ui->lpFIRGain, QOverload::of(&QComboBox::currentIndexChanged), this, &PlutoSDRMIMOGUI::on_lpFIRGain_currentIndexChanged); + QObject::connect(ui->antenna, QOverload::of(&QComboBox::currentIndexChanged), this, &PlutoSDRMIMOGUI::on_antenna_currentIndexChanged); +} diff --git a/plugins/samplemimo/plutosdrmimo/plutosdrmimogui.h b/plugins/samplemimo/plutosdrmimo/plutosdrmimogui.h index 0f58ffd8d..cbd816025 100644 --- a/plugins/samplemimo/plutosdrmimo/plutosdrmimogui.h +++ b/plugins/samplemimo/plutosdrmimo/plutosdrmimogui.h @@ -44,6 +44,10 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } + virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; } + virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; } + virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; } + virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; } private: Ui::PlutoSDRMIMOGUI* ui; @@ -81,6 +85,7 @@ private: void setSampleRateLimits(); void updateFrequencyLimits(); bool handleMessage(const Message& message); + void makeUIConnections(); private slots: void handleInputMessages(); diff --git a/plugins/samplemimo/plutosdrmimo/plutosdrmimosettings.cpp b/plugins/samplemimo/plutosdrmimo/plutosdrmimosettings.cpp index ceb4441d3..887c6d6d7 100644 --- a/plugins/samplemimo/plutosdrmimo/plutosdrmimosettings.cpp +++ b/plugins/samplemimo/plutosdrmimo/plutosdrmimosettings.cpp @@ -77,6 +77,7 @@ void PlutoSDRMIMOSettings::resetToDefaults() m_reverseAPIAddress = "127.0.0.1"; m_reverseAPIPort = 8888; m_reverseAPIDeviceIndex = 0; + m_workspaceIndex = 0; } QByteArray PlutoSDRMIMOSettings::serialize() const @@ -141,6 +142,9 @@ QByteArray PlutoSDRMIMOSettings::serialize() const s.writeU32(102, m_reverseAPIPort); s.writeU32(103, m_reverseAPIDeviceIndex); + s.writeS32(104, m_workspaceIndex); + s.writeBlob(105, m_geometryBytes); + return s.final(); } @@ -275,6 +279,9 @@ bool PlutoSDRMIMOSettings::deserialize(const QByteArray& data) d.readU32(103, &uintval, 0); m_reverseAPIDeviceIndex = uintval > 99 ? 99 : uintval; + d.readS32(104, &m_workspaceIndex, 0); + d.readBlob(105, &m_geometryBytes); + return true; } else diff --git a/plugins/samplemimo/plutosdrmimo/plutosdrmimosettings.h b/plugins/samplemimo/plutosdrmimo/plutosdrmimosettings.h index d99efd8f2..3fda801be 100644 --- a/plugins/samplemimo/plutosdrmimo/plutosdrmimosettings.h +++ b/plugins/samplemimo/plutosdrmimo/plutosdrmimosettings.h @@ -121,6 +121,8 @@ struct PlutoSDRMIMOSettings { QString m_reverseAPIAddress; uint16_t m_reverseAPIPort; uint16_t m_reverseAPIDeviceIndex; + int m_workspaceIndex; + QByteArray m_geometryBytes; static const int m_plutoSDRBlockSizeSamples = 64*256; //complex samples per buffer (must be multiple of 64) diff --git a/plugins/samplemimo/testmi/testmigui.cpp b/plugins/samplemimo/testmi/testmigui.cpp index e39e93c76..49751eabf 100644 --- a/plugins/samplemimo/testmi/testmigui.cpp +++ b/plugins/samplemimo/testmi/testmigui.cpp @@ -52,6 +52,7 @@ TestMIGui::TestMIGui(DeviceUISet *deviceUISet, QWidget* parent) : m_lastEngineState(DeviceAPI::StNotStarted) { qDebug("TestMIGui::TestMIGui"); + setAttribute(Qt::WA_DeleteOnClose, true); m_sampleMIMO = m_deviceUISet->m_deviceAPI->getSampleMIMO(); m_streamIndex = 0; m_deviceCenterFrequencies.push_back(m_settings.m_streams[0].m_centerFrequency); @@ -59,7 +60,9 @@ TestMIGui::TestMIGui(DeviceUISet *deviceUISet, QWidget* parent) : m_deviceSampleRates.push_back(m_settings.m_streams[0].m_sampleRate / (1<setupUi(this); + ui->setupUi(getContents()); + getContents()->setStyleSheet("#TestMIGui { border: 1px solid #C06900 }"); + m_helpURL = "plugins/samplemimo/testmi/readme.md"; ui->spectrumSource->addItem("0"); ui->spectrumSource->addItem("1"); ui->centerFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold)); @@ -81,6 +84,8 @@ TestMIGui::TestMIGui(DeviceUISet *deviceUISet, QWidget* parent) : CRightClickEnabler *startStopRightClickEnabler = new CRightClickEnabler(ui->startStop); connect(startStopRightClickEnabler, SIGNAL(rightClick(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &))); + + makeUIConnections(); } TestMIGui::~TestMIGui() @@ -559,3 +564,28 @@ void TestMIGui::openDeviceSettingsDialog(const QPoint& p) sendSettings(); } + +void TestMIGui::makeUIConnections() +{ + QObject::connect(ui->startStop, &ButtonSwitch::toggled, this, &TestMIGui::on_startStop_toggled); + QObject::connect(ui->streamIndex, QOverload::of(&QComboBox::currentIndexChanged), this, &TestMIGui::on_streamIndex_currentIndexChanged); + QObject::connect(ui->spectrumSource, QOverload::of(&QComboBox::currentIndexChanged), this, &TestMIGui::on_spectrumSource_currentIndexChanged); + QObject::connect(ui->streamLock, &QToolButton::toggled, this, &TestMIGui::on_streamLock_toggled); + QObject::connect(ui->centerFrequency, &ValueDial::changed, this, &TestMIGui::on_centerFrequency_changed); + QObject::connect(ui->autoCorr, QOverload::of(&QComboBox::currentIndexChanged), this, &TestMIGui::on_autoCorr_currentIndexChanged); + QObject::connect(ui->frequencyShift, &ValueDialZ::changed, this, &TestMIGui::on_frequencyShift_changed); + QObject::connect(ui->decimation, QOverload::of(&QComboBox::currentIndexChanged), this, &TestMIGui::on_decimation_currentIndexChanged); + QObject::connect(ui->fcPos, QOverload::of(&QComboBox::currentIndexChanged), this, &TestMIGui::on_fcPos_currentIndexChanged); + QObject::connect(ui->sampleRate, &ValueDial::changed, this, &TestMIGui::on_sampleRate_changed); + QObject::connect(ui->sampleSize, QOverload::of(&QComboBox::currentIndexChanged), this, &TestMIGui::on_sampleSize_currentIndexChanged); + QObject::connect(ui->amplitudeCoarse, &QSlider::valueChanged, this, &TestMIGui::on_amplitudeCoarse_valueChanged); + QObject::connect(ui->amplitudeFine, &QSlider::valueChanged, this, &TestMIGui::on_amplitudeFine_valueChanged); + QObject::connect(ui->modulation, QOverload::of(&QComboBox::currentIndexChanged), this, &TestMIGui::on_modulation_currentIndexChanged); + QObject::connect(ui->modulationFrequency, &QDial::valueChanged, this, &TestMIGui::on_modulationFrequency_valueChanged); + QObject::connect(ui->amModulation, &QDial::valueChanged, this, &TestMIGui::on_amModulation_valueChanged); + QObject::connect(ui->fmDeviation, &QDial::valueChanged, this, &TestMIGui::on_fmDeviation_valueChanged); + QObject::connect(ui->dcBias, &QSlider::valueChanged, this, &TestMIGui::on_dcBias_valueChanged); + QObject::connect(ui->iBias, &QSlider::valueChanged, this, &TestMIGui::on_iBias_valueChanged); + QObject::connect(ui->qBias, &QSlider::valueChanged, this, &TestMIGui::on_qBias_valueChanged); + QObject::connect(ui->phaseImbalance, &QSlider::valueChanged, this, &TestMIGui::on_phaseImbalance_valueChanged); +} diff --git a/plugins/samplemimo/testmi/testmigui.h b/plugins/samplemimo/testmi/testmigui.h index a3b9ecd6f..ec27cbaae 100644 --- a/plugins/samplemimo/testmi/testmigui.h +++ b/plugins/samplemimo/testmi/testmigui.h @@ -45,6 +45,10 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } + virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; } + virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; } + virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; } + virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; } private: Ui::TestMIGui* ui; @@ -73,6 +77,7 @@ private: void updateAmpFineLimit(); void updateFrequencyShiftLimit(); bool handleMessage(const Message& message); + void makeUIConnections(); private slots: void handleInputMessages(); diff --git a/plugins/samplemimo/testmi/testmigui.ui b/plugins/samplemimo/testmi/testmigui.ui index 45b8e95a5..078537460 100644 --- a/plugins/samplemimo/testmi/testmigui.ui +++ b/plugins/samplemimo/testmi/testmigui.ui @@ -7,7 +7,7 @@ 0 0 360 - 300 + 368 @@ -19,7 +19,7 @@ 360 - 300 + 0 @@ -224,6 +224,9 @@ Liberation Mono 20 + 50 + false + false @@ -488,6 +491,9 @@ Liberation Mono 12 + 50 + false + false @@ -644,6 +650,9 @@ Liberation Mono 12 + 50 + false + false @@ -764,13 +773,6 @@ - - - - Qt::Horizontal - - - @@ -1052,9 +1054,6 @@ - - -
diff --git a/plugins/samplemimo/testmi/testmisettings.cpp b/plugins/samplemimo/testmi/testmisettings.cpp index 7461e52b4..66d43f192 100644 --- a/plugins/samplemimo/testmi/testmisettings.cpp +++ b/plugins/samplemimo/testmi/testmisettings.cpp @@ -50,6 +50,7 @@ TestMISettings::TestMISettings() m_reverseAPIAddress = "127.0.0.1"; m_reverseAPIPort = 8888; m_reverseAPIDeviceIndex = 0; + m_workspaceIndex = 0; m_streams.push_back(TestMIStreamSettings()); m_streams.push_back(TestMIStreamSettings()); } @@ -61,6 +62,7 @@ TestMISettings::TestMISettings(const TestMISettings& other) : m_reverseAPIAddress = other.m_reverseAPIAddress; m_reverseAPIPort = other.m_reverseAPIPort; m_reverseAPIDeviceIndex = other.m_reverseAPIDeviceIndex; + m_workspaceIndex = other.m_workspaceIndex; } void TestMISettings::resetToDefaults() @@ -78,6 +80,8 @@ QByteArray TestMISettings::serialize() const s.writeString(2, m_reverseAPIAddress); s.writeU32(3, m_reverseAPIPort); s.writeU32(4, m_reverseAPIDeviceIndex); + s.writeS32(5, m_workspaceIndex); + s.writeBlob(6, m_geometryBytes); for (unsigned int i = 0; i < m_streams.size(); i++) { @@ -128,6 +132,8 @@ bool TestMISettings::deserialize(const QByteArray& data) d.readU32(4, &utmp, 0); m_reverseAPIDeviceIndex = utmp > 99 ? 99 : utmp; + d.readS32(5, &m_workspaceIndex, 0); + d.readBlob(6, &m_geometryBytes); for (unsigned int i = 0; i < m_streams.size(); i++) { diff --git a/plugins/samplemimo/testmi/testmisettings.h b/plugins/samplemimo/testmi/testmisettings.h index 62c328b97..95898b1ac 100644 --- a/plugins/samplemimo/testmi/testmisettings.h +++ b/plugins/samplemimo/testmi/testmisettings.h @@ -70,6 +70,8 @@ struct TestMISettings { QString m_reverseAPIAddress; uint16_t m_reverseAPIPort; uint16_t m_reverseAPIDeviceIndex; + int m_workspaceIndex; + QByteArray m_geometryBytes; std::vector m_streams; TestMISettings(); diff --git a/plugins/samplemimo/testmosync/testmosyncgui.cpp b/plugins/samplemimo/testmosync/testmosyncgui.cpp index cbcd64670..b9c3b8404 100644 --- a/plugins/samplemimo/testmosync/testmosyncgui.cpp +++ b/plugins/samplemimo/testmosync/testmosyncgui.cpp @@ -49,7 +49,10 @@ TestMOSyncGui::TestMOSyncGui(DeviceUISet *deviceUISet, QWidget* parent) : m_tickCount(0), m_lastEngineState(DeviceAPI::StNotStarted) { - ui->setupUi(this); + setAttribute(Qt::WA_DeleteOnClose, true); + ui->setupUi(getContents()); + getContents()->setStyleSheet("#TestMOSyncGui { border: 1px solid #C06900 }"); + m_helpURL = "plugins/samplemimo/testmosync/readme.md"; m_sampleMIMO = (TestMOSync*) m_deviceUISet->m_deviceAPI->getSampleMIMO(); ui->centerFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold)); @@ -70,6 +73,7 @@ TestMOSyncGui::TestMOSyncGui(DeviceUISet *deviceUISet, QWidget* parent) : m_statusTimer.start(500); displaySettings(); + makeUIConnections(); m_sampleMIMO->setMessageQueueToGUI(&m_inputMessageQueue); connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()), Qt::QueuedConnection); @@ -77,6 +81,7 @@ TestMOSyncGui::TestMOSyncGui(DeviceUISet *deviceUISet, QWidget* parent) : m_deviceUISet->m_spectrum->setDisplayedStream(false, 0); m_deviceUISet->m_deviceAPI->setSpectrumSinkInput(false, 0); m_deviceUISet->setSpectrumScalingFactor(SDR_TX_SCALEF); + } TestMOSyncGui::~TestMOSyncGui() @@ -282,3 +287,12 @@ void TestMOSyncGui::on_spectrumIndex_currentIndexChanged(int index) void TestMOSyncGui::tick() { } + +void TestMOSyncGui::makeUIConnections() +{ + QObject::connect(ui->centerFrequency, &ValueDial::changed, this, &TestMOSyncGui::on_centerFrequency_changed); + QObject::connect(ui->sampleRate, &ValueDial::changed, this, &TestMOSyncGui::on_sampleRate_changed); + QObject::connect(ui->startStop, &ButtonSwitch::toggled, this, &TestMOSyncGui::on_startStop_toggled); + QObject::connect(ui->interp, QOverload::of(&QComboBox::currentIndexChanged), this, &TestMOSyncGui::on_interp_currentIndexChanged); + QObject::connect(ui->spectrumIndex, QOverload::of(&QComboBox::currentIndexChanged), this, &TestMOSyncGui::on_spectrumIndex_currentIndexChanged); +} diff --git a/plugins/samplemimo/testmosync/testmosyncgui.h b/plugins/samplemimo/testmosync/testmosyncgui.h index e6b066d0b..13a509d67 100644 --- a/plugins/samplemimo/testmosync/testmosyncgui.h +++ b/plugins/samplemimo/testmosync/testmosyncgui.h @@ -47,6 +47,10 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } + virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; } + virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; } + virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; } + virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; } private: Ui::TestMOSyncGui* ui; @@ -72,6 +76,7 @@ private: void sendSettings(); void updateSampleRateAndFrequency(); bool handleMessage(const Message& message); + void makeUIConnections(); private slots: void handleInputMessages(); diff --git a/plugins/samplemimo/testmosync/testmosyncsettings.cpp b/plugins/samplemimo/testmosync/testmosyncsettings.cpp index 596f90432..ca026deec 100644 --- a/plugins/samplemimo/testmosync/testmosyncsettings.cpp +++ b/plugins/samplemimo/testmosync/testmosyncsettings.cpp @@ -31,6 +31,7 @@ void TestMOSyncSettings::resetToDefaults() m_sampleRate = 48000; m_log2Interp = 0; m_fcPosTx = FC_POS_CENTER; + m_workspaceIndex = 0; } QByteArray TestMOSyncSettings::serialize() const @@ -40,6 +41,8 @@ QByteArray TestMOSyncSettings::serialize() const s.writeU64(1, m_sampleRate); s.writeU32(2, m_log2Interp); s.writeS32(3, (int) m_fcPosTx); + s.writeS32(4, m_workspaceIndex); + s.writeBlob(5, m_geometryBytes); return s.final(); } @@ -60,8 +63,10 @@ bool TestMOSyncSettings::deserialize(const QByteArray& data) d.readU64(1, &m_sampleRate, 48000); d.readU32(2, &m_log2Interp, 0); - d.readS32(38, &intval, 2); + d.readS32(3, &intval, 2); m_fcPosTx = (fcPos_t) intval; + d.readS32(4, &m_workspaceIndex, 0); + d.readBlob(5, &m_geometryBytes); return true; } diff --git a/plugins/samplemimo/testmosync/testmosyncsettings.h b/plugins/samplemimo/testmosync/testmosyncsettings.h index dc40e9c5a..23976fb85 100644 --- a/plugins/samplemimo/testmosync/testmosyncsettings.h +++ b/plugins/samplemimo/testmosync/testmosyncsettings.h @@ -31,6 +31,8 @@ struct TestMOSyncSettings { quint64 m_sampleRate; quint32 m_log2Interp; fcPos_t m_fcPosTx; + int m_workspaceIndex; + QByteArray m_geometryBytes; static const unsigned int m_msThrottle; diff --git a/plugins/samplemimo/xtrxmimo/xtrxmimogui.cpp b/plugins/samplemimo/xtrxmimo/xtrxmimogui.cpp index cace01d2f..98fc27e4f 100644 --- a/plugins/samplemimo/xtrxmimo/xtrxmimogui.cpp +++ b/plugins/samplemimo/xtrxmimo/xtrxmimogui.cpp @@ -65,7 +65,10 @@ XTRXMIMOGUI::XTRXMIMOGUI(DeviceUISet *deviceUISet, QWidget* parent) : m_sampleRateMode(true) { qDebug("XTRXMIMOGUI::XTRXMIMOGUI"); - ui->setupUi(this); + setAttribute(Qt::WA_DeleteOnClose, true); + ui->setupUi(getContents()); + getContents()->setStyleSheet("#XTRXMIMOGUI { border: 1px solid #C06900 }"); + m_helpURL = "plugins/samplemimo/xtrxmimo/readme.md"; m_xtrxMIMO = (XTRXMIMO*) m_deviceUISet->m_deviceAPI->getSampleMIMO(); float minF, maxF, stepF; @@ -97,6 +100,7 @@ XTRXMIMOGUI::XTRXMIMOGUI(DeviceUISet *deviceUISet, QWidget* parent) : connect(startStopRightClickEnabler, SIGNAL(rightClick(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &))); sendSettings(); + makeUIConnections(); } XTRXMIMOGUI::~XTRXMIMOGUI() @@ -1013,3 +1017,31 @@ void XTRXMIMOGUI::openDeviceSettingsDialog(const QPoint& p) sendSettings(); } + +void XTRXMIMOGUI::makeUIConnections() +{ + QObject::connect(ui->streamSide, QOverload::of(&QComboBox::currentIndexChanged), this, &XTRXMIMOGUI::on_streamSide_currentIndexChanged); + QObject::connect(ui->streamIndex, QOverload::of(&QComboBox::currentIndexChanged), this, &XTRXMIMOGUI::on_streamIndex_currentIndexChanged); + QObject::connect(ui->spectrumSide, QOverload::of(&QComboBox::currentIndexChanged), this, &XTRXMIMOGUI::on_spectrumSide_currentIndexChanged); + QObject::connect(ui->spectrumIndex, QOverload::of(&QComboBox::currentIndexChanged), this, &XTRXMIMOGUI::on_spectrumIndex_currentIndexChanged); + QObject::connect(ui->startStopRx, &ButtonSwitch::toggled, this, &XTRXMIMOGUI::on_startStopRx_toggled); + QObject::connect(ui->startStopTx, &ButtonSwitch::toggled, this, &XTRXMIMOGUI::on_startStopTx_toggled); + QObject::connect(ui->centerFrequency, &ValueDial::changed, this, &XTRXMIMOGUI::on_centerFrequency_changed); + QObject::connect(ui->ncoEnable, &ButtonSwitch::toggled, this, &XTRXMIMOGUI::on_ncoEnable_toggled); + QObject::connect(ui->ncoFrequency, &ValueDialZ::changed, this, &XTRXMIMOGUI::on_ncoFrequency_changed); + QObject::connect(ui->dcOffset, &ButtonSwitch::toggled, this, &XTRXMIMOGUI::on_dcOffset_toggled); + QObject::connect(ui->iqImbalance, &ButtonSwitch::toggled, this, &XTRXMIMOGUI::on_iqImbalance_toggled); + QObject::connect(ui->extClock, &ExternalClockButton::clicked, this, &XTRXMIMOGUI::on_extClock_clicked); + QObject::connect(ui->hwDecim, QOverload::of(&QComboBox::currentIndexChanged), this, &XTRXMIMOGUI::on_hwDecim_currentIndexChanged); + QObject::connect(ui->swDecim, QOverload::of(&QComboBox::currentIndexChanged), this, &XTRXMIMOGUI::on_swDecim_currentIndexChanged); + QObject::connect(ui->sampleRateMode, &QToolButton::toggled, this, &XTRXMIMOGUI::on_sampleRateMode_toggled); + QObject::connect(ui->sampleRate, &ValueDial::changed, this, &XTRXMIMOGUI::on_sampleRate_changed); + QObject::connect(ui->lpf, &ValueDial::changed, this, &XTRXMIMOGUI::on_lpf_changed); + QObject::connect(ui->pwrmode, QOverload::of(&QComboBox::currentIndexChanged), this, &XTRXMIMOGUI::on_pwrmode_currentIndexChanged); + QObject::connect(ui->gainMode, QOverload::of(&QComboBox::currentIndexChanged), this, &XTRXMIMOGUI::on_gainMode_currentIndexChanged); + QObject::connect(ui->gain, &QDial::valueChanged, this, &XTRXMIMOGUI::on_gain_valueChanged); + QObject::connect(ui->lnaGain, &QDial::valueChanged, this, &XTRXMIMOGUI::on_lnaGain_valueChanged); + QObject::connect(ui->tiaGain, QOverload::of(&QComboBox::currentIndexChanged), this, &XTRXMIMOGUI::on_tiaGain_currentIndexChanged); + QObject::connect(ui->pgaGain, &QDial::valueChanged, this, &XTRXMIMOGUI::on_pgaGain_valueChanged); + QObject::connect(ui->antenna, QOverload::of(&QComboBox::currentIndexChanged), this, &XTRXMIMOGUI::on_antenna_currentIndexChanged); +} diff --git a/plugins/samplemimo/xtrxmimo/xtrxmimogui.h b/plugins/samplemimo/xtrxmimo/xtrxmimogui.h index 4699300fb..2f40493d7 100644 --- a/plugins/samplemimo/xtrxmimo/xtrxmimogui.h +++ b/plugins/samplemimo/xtrxmimo/xtrxmimogui.h @@ -44,6 +44,10 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } + virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; } + virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; } + virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; } + virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; } private: Ui::XTRXMIMOGUI* ui; @@ -85,6 +89,7 @@ private: void updateADCRate(); void updateDACRate(); bool handleMessage(const Message& message); + void makeUIConnections(); private slots: void handleInputMessages(); diff --git a/plugins/samplemimo/xtrxmimo/xtrxmimosettings.cpp b/plugins/samplemimo/xtrxmimo/xtrxmimosettings.cpp index fcde1096d..2d6cb75f1 100644 --- a/plugins/samplemimo/xtrxmimo/xtrxmimosettings.cpp +++ b/plugins/samplemimo/xtrxmimo/xtrxmimosettings.cpp @@ -75,6 +75,8 @@ void XTRXMIMOSettings::resetToDefaults() m_lpfBWTx1 = 4.5e6f; m_gainTx1 = 20; m_pwrmodeTx1 = 4; + // GUI + m_workspaceIndex = 0; } QByteArray XTRXMIMOSettings::serialize() const @@ -129,6 +131,9 @@ QByteArray XTRXMIMOSettings::serialize() const s.writeFloat(90, m_lpfBWTx1); s.writeU32(91, m_gainTx1); s.writeU32(92, m_pwrmodeTx1); + // GUI + s.writeS32(93, m_workspaceIndex); + s.writeBlob(94, m_geometryBytes); return s.final(); } diff --git a/plugins/samplemimo/xtrxmimo/xtrxmimosettings.h b/plugins/samplemimo/xtrxmimo/xtrxmimosettings.h index 8dc3a7a19..f86fdb5ce 100644 --- a/plugins/samplemimo/xtrxmimo/xtrxmimosettings.h +++ b/plugins/samplemimo/xtrxmimo/xtrxmimosettings.h @@ -93,6 +93,9 @@ struct XTRXMIMOSettings float m_lpfBWTx1; //!< LMS analog lowpass filter bandwidth (Hz) uint32_t m_gainTx1; //!< Optimally distributed gain (dB) uint32_t m_pwrmodeTx1; + // GUI + int m_workspaceIndex; + QByteArray m_geometryBytes; XTRXMIMOSettings(); void resetToDefaults(); diff --git a/plugins/samplesink/audiooutput/audiooutputgui.cpp b/plugins/samplesink/audiooutput/audiooutputgui.cpp index be4df2bb1..8aeddf0c8 100644 --- a/plugins/samplesink/audiooutput/audiooutputgui.cpp +++ b/plugins/samplesink/audiooutput/audiooutputgui.cpp @@ -40,9 +40,12 @@ AudioOutputGui::AudioOutputGui(DeviceUISet *deviceUISet, QWidget* parent) : m_settings(), m_centerFrequency(0) { + setAttribute(Qt::WA_DeleteOnClose, true); m_audioOutput = (AudioOutput*) m_deviceUISet->m_deviceAPI->getSampleSink(); - ui->setupUi(this); + ui->setupUi(getContents()); + getContents()->setStyleSheet("#AudioOutputGui { border: 1px solid #C06900 }"); + m_helpURL = "plugins/samplesink/audiooutput/readme.md"; connect(&m_updateTimer, SIGNAL(timeout()), this, SLOT(updateHardware())); @@ -57,6 +60,8 @@ AudioOutputGui::AudioOutputGui(DeviceUISet *deviceUISet, QWidget* parent) : connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()), Qt::QueuedConnection); m_audioOutput->setMessageQueueToGUI(&m_inputMessageQueue); + + makeUIConnections(); } AudioOutputGui::~AudioOutputGui() @@ -237,3 +242,11 @@ void AudioOutputGui::openDeviceSettingsDialog(const QPoint& p) sendSettings(); } + +void AudioOutputGui::makeUIConnections() +{ + QObject::connect(ui->deviceSelect, &QPushButton::clicked, this, &AudioOutputGui::on_deviceSelect_clicked); + QObject::connect(ui->volume, &QDial::valueChanged, this, &AudioOutputGui::on_volume_valueChanged); + QObject::connect(ui->channels, QOverload::of(&QComboBox::currentIndexChanged), this, &AudioOutputGui::on_channels_currentIndexChanged); + QObject::connect(ui->startStop, &ButtonSwitch::toggled, this, &AudioOutputGui::on_startStop_toggled); +} diff --git a/plugins/samplesink/audiooutput/audiooutputgui.h b/plugins/samplesink/audiooutput/audiooutputgui.h index 84e3e92a9..d53748dfc 100644 --- a/plugins/samplesink/audiooutput/audiooutputgui.h +++ b/plugins/samplesink/audiooutput/audiooutputgui.h @@ -45,6 +45,10 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } + virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; } + virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; } + virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; } + virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; } private: Ui::AudioOutputGui* ui; @@ -65,6 +69,7 @@ private: void sendSettings(); void updateSampleRateAndFrequency(); bool handleMessage(const Message& message); + void makeUIConnections(); private slots: void handleInputMessages(); diff --git a/plugins/samplesink/audiooutput/audiooutputsettings.cpp b/plugins/samplesink/audiooutput/audiooutputsettings.cpp index e6d39f949..b11772506 100644 --- a/plugins/samplesink/audiooutput/audiooutputsettings.cpp +++ b/plugins/samplesink/audiooutput/audiooutputsettings.cpp @@ -34,6 +34,7 @@ void AudioOutputSettings::resetToDefaults() m_reverseAPIAddress = "127.0.0.1"; m_reverseAPIPort = 8888; m_reverseAPIDeviceIndex = 0; + m_workspaceIndex = 0; } QByteArray AudioOutputSettings::serialize() const @@ -43,6 +44,8 @@ QByteArray AudioOutputSettings::serialize() const s.writeString(1, m_deviceName); s.writeFloat(3, m_volume); s.writeS32(5, (int)m_iqMapping); + s.writeS32(6, m_workspaceIndex); + s.writeBlob(7, m_geometryBytes); s.writeBool(24, m_useReverseAPI); s.writeString(25, m_reverseAPIAddress); @@ -69,6 +72,8 @@ bool AudioOutputSettings::deserialize(const QByteArray& data) d.readString(1, &m_deviceName, ""); d.readFloat(3, &m_volume, 1.0f); d.readS32(5, (int *)&m_iqMapping, IQMapping::LR); + d.readS32(6, &m_workspaceIndex, 0); + d.readBlob(7, &m_geometryBytes); d.readBool(24, &m_useReverseAPI, false); d.readString(25, &m_reverseAPIAddress, "127.0.0.1"); diff --git a/plugins/samplesink/audiooutput/audiooutputsettings.h b/plugins/samplesink/audiooutput/audiooutputsettings.h index d2f9e7634..66335e21d 100644 --- a/plugins/samplesink/audiooutput/audiooutputsettings.h +++ b/plugins/samplesink/audiooutput/audiooutputsettings.h @@ -34,6 +34,8 @@ struct AudioOutputSettings { QString m_reverseAPIAddress; uint16_t m_reverseAPIPort; uint16_t m_reverseAPIDeviceIndex; + int m_workspaceIndex; + QByteArray m_geometryBytes; AudioOutputSettings(); void resetToDefaults(); diff --git a/plugins/samplesink/bladerf1output/bladerf1outputgui.cpp b/plugins/samplesink/bladerf1output/bladerf1outputgui.cpp index 56c2168a8..4e96bb307 100644 --- a/plugins/samplesink/bladerf1output/bladerf1outputgui.cpp +++ b/plugins/samplesink/bladerf1output/bladerf1outputgui.cpp @@ -43,10 +43,12 @@ Bladerf1OutputGui::Bladerf1OutputGui(DeviceUISet *deviceUISet, QWidget* parent) m_sampleRate(0), m_lastEngineState(DeviceAPI::StNotStarted) { + setAttribute(Qt::WA_DeleteOnClose, true); m_deviceSampleSink = (Bladerf1Output*) m_deviceUISet->m_deviceAPI->getSampleSink(); - ui->setupUi(this); - ui->centerFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold)); + ui->setupUi(getContents()); + getContents()->setStyleSheet("#Bladerf1OutputGui { border: 1px solid #C06900 }"); + m_helpURL = "/plugins/samplesink/bladerf1output/readme.md"; ui->centerFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold)); ui->centerFrequency->setValueRange(7, BLADERF_FREQUENCY_MIN_XB200/1000, BLADERF_FREQUENCY_MAX/1000); ui->sampleRate->setColorMapper(ColorMapper(ColorMapper::GrayGreenYellow)); @@ -67,6 +69,7 @@ Bladerf1OutputGui::Bladerf1OutputGui(DeviceUISet *deviceUISet, QWidget* parent) connect(startStopRightClickEnabler, SIGNAL(rightClick(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &))); displaySettings(); + makeUIConnections(); connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()), Qt::QueuedConnection); } @@ -460,3 +463,13 @@ void Bladerf1OutputGui::openDeviceSettingsDialog(const QPoint& p) sendSettings(); } + +void Bladerf1OutputGui::makeUIConnections() +{ + QObject::connect(ui->centerFrequency, &ValueDial::changed, this, &Bladerf1OutputGui::on_centerFrequency_changed); + QObject::connect(ui->sampleRate, &ValueDial::changed, this, &Bladerf1OutputGui::on_sampleRate_changed); + QObject::connect(ui->bandwidth, QOverload::of(&QComboBox::currentIndexChanged), this, &Bladerf1OutputGui::on_bandwidth_currentIndexChanged); + QObject::connect(ui->interp, QOverload::of(&QComboBox::currentIndexChanged), this, &Bladerf1OutputGui::on_interp_currentIndexChanged); + QObject::connect(ui->startStop, &ButtonSwitch::toggled, this, &Bladerf1OutputGui::on_startStop_toggled); + QObject::connect(ui->sampleRateMode, &QToolButton::toggled, this, &Bladerf1OutputGui::on_sampleRateMode_toggled); +} diff --git a/plugins/samplesink/bladerf1output/bladerf1outputgui.h b/plugins/samplesink/bladerf1output/bladerf1outputgui.h index 36d8312aa..af5cc768e 100644 --- a/plugins/samplesink/bladerf1output/bladerf1outputgui.h +++ b/plugins/samplesink/bladerf1output/bladerf1outputgui.h @@ -45,6 +45,10 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } + virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; } + virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; } + virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; } + virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; } private: Ui::Bladerf1OutputGui* ui; @@ -69,6 +73,7 @@ private: unsigned int getXb200Index(bool xb_200, bladerf_xb200_path xb200Path, bladerf_xb200_filter xb200Filter); void updateSampleRateAndFrequency(); bool handleMessage(const Message& message); + void makeUIConnections(); private slots: void handleInputMessages(); diff --git a/plugins/samplesink/bladerf1output/bladerf1outputsettings.cpp b/plugins/samplesink/bladerf1output/bladerf1outputsettings.cpp index 42e33f262..4e29762e4 100644 --- a/plugins/samplesink/bladerf1output/bladerf1outputsettings.cpp +++ b/plugins/samplesink/bladerf1output/bladerf1outputsettings.cpp @@ -41,6 +41,7 @@ void BladeRF1OutputSettings::resetToDefaults() m_reverseAPIAddress = "127.0.0.1"; m_reverseAPIPort = 8888; m_reverseAPIDeviceIndex = 0; + m_workspaceIndex = 0; } QByteArray BladeRF1OutputSettings::serialize() const @@ -59,6 +60,8 @@ QByteArray BladeRF1OutputSettings::serialize() const s.writeString(10, m_reverseAPIAddress); s.writeU32(11, m_reverseAPIPort); s.writeU32(12, m_reverseAPIDeviceIndex); + s.writeS32(13, m_workspaceIndex); + s.writeBlob(14, m_geometryBytes); return s.final(); } @@ -100,6 +103,8 @@ bool BladeRF1OutputSettings::deserialize(const QByteArray& data) d.readU32(12, &uintval, 0); m_reverseAPIDeviceIndex = uintval > 99 ? 99 : uintval; + d.readS32(13, &m_workspaceIndex, 0); + d.readBlob(14, &m_geometryBytes); return true; } diff --git a/plugins/samplesink/bladerf1output/bladerf1outputsettings.h b/plugins/samplesink/bladerf1output/bladerf1outputsettings.h index 71d15376c..44c0b5d0b 100644 --- a/plugins/samplesink/bladerf1output/bladerf1outputsettings.h +++ b/plugins/samplesink/bladerf1output/bladerf1outputsettings.h @@ -37,6 +37,8 @@ struct BladeRF1OutputSettings { QString m_reverseAPIAddress; uint16_t m_reverseAPIPort; uint16_t m_reverseAPIDeviceIndex; + int m_workspaceIndex; + QByteArray m_geometryBytes; BladeRF1OutputSettings(); void resetToDefaults(); diff --git a/plugins/samplesink/bladerf2output/bladerf2outputgui.cpp b/plugins/samplesink/bladerf2output/bladerf2outputgui.cpp index eac2bb5c7..7fa2bd767 100644 --- a/plugins/samplesink/bladerf2output/bladerf2outputgui.cpp +++ b/plugins/samplesink/bladerf2output/bladerf2outputgui.cpp @@ -43,12 +43,15 @@ BladeRF2OutputGui::BladeRF2OutputGui(DeviceUISet *deviceUISet, QWidget* parent) m_sampleRate(0), m_lastEngineState(DeviceAPI::StNotStarted) { + setAttribute(Qt::WA_DeleteOnClose, true); m_sampleSink = (BladeRF2Output*) m_deviceUISet->m_deviceAPI->getSampleSink(); int max, min, step; float scale; uint64_t f_min, f_max; - ui->setupUi(this); + ui->setupUi(getContents()); + getContents()->setStyleSheet("#BladeRF2OutputGui { border: 1px solid #C06900 }"); + m_helpURL = "plugins/samplesink/bladerf2output/readme.md"; ui->centerFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold)); m_sampleSink->getFrequencyRange(f_min, f_max, step, scale); qDebug("BladeRF2OutputGui::BladeRF2OutputGui: getFrequencyRange: [%lu,%lu] step: %d", f_min, f_max, step); @@ -80,6 +83,7 @@ BladeRF2OutputGui::BladeRF2OutputGui(DeviceUISet *deviceUISet, QWidget* parent) connect(startStopRightClickEnabler, SIGNAL(rightClick(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &))); displaySettings(); + makeUIConnections(); connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()), Qt::QueuedConnection); m_sampleSink->setMessageQueueToGUI(&m_inputMessageQueue); @@ -453,3 +457,17 @@ int BladeRF2OutputGui::getGainValue(float gainDB) // gainDB, m_gainMin, m_gainMax, m_gainStep, gain); return gain; } + +void BladeRF2OutputGui::makeUIConnections() +{ + QObject::connect(ui->centerFrequency, &ValueDial::changed, this, &BladeRF2OutputGui::on_centerFrequency_changed); + QObject::connect(ui->LOppm, &QSlider::valueChanged, this, &BladeRF2OutputGui::on_LOppm_valueChanged); + QObject::connect(ui->biasTee, &ButtonSwitch::toggled, this, &BladeRF2OutputGui::on_biasTee_toggled); + QObject::connect(ui->sampleRate, &ValueDial::changed, this, &BladeRF2OutputGui::on_sampleRate_changed); + QObject::connect(ui->bandwidth, &ValueDial::changed, this, &BladeRF2OutputGui::on_bandwidth_changed); + QObject::connect(ui->interp, QOverload::of(&QComboBox::currentIndexChanged), this, &BladeRF2OutputGui::on_interp_currentIndexChanged); + QObject::connect(ui->gain, &QSlider::valueChanged, this, &BladeRF2OutputGui::on_gain_valueChanged); + QObject::connect(ui->startStop, &ButtonSwitch::toggled, this, &BladeRF2OutputGui::on_startStop_toggled); + QObject::connect(ui->transverter, &TransverterButton::clicked, this, &BladeRF2OutputGui::on_transverter_clicked); + QObject::connect(ui->sampleRateMode, &QToolButton::toggled, this, &BladeRF2OutputGui::on_sampleRateMode_toggled); +} diff --git a/plugins/samplesink/bladerf2output/bladerf2outputgui.h b/plugins/samplesink/bladerf2output/bladerf2outputgui.h index 19e382f34..c4930bbce 100644 --- a/plugins/samplesink/bladerf2output/bladerf2outputgui.h +++ b/plugins/samplesink/bladerf2output/bladerf2outputgui.h @@ -46,6 +46,10 @@ public: virtual bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } virtual bool handleMessage(const Message& message); + virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; } + virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; } + virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; } + virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; } private: Ui::BladeRF2OutputGui* ui; @@ -76,6 +80,7 @@ private: void setCenterFrequencySetting(uint64_t kHzValue); float getGainDB(int gainValue); int getGainValue(float gainDB); + void makeUIConnections(); private slots: void handleInputMessages(); diff --git a/plugins/samplesink/bladerf2output/bladerf2outputsettings.cpp b/plugins/samplesink/bladerf2output/bladerf2outputsettings.cpp index a50ee005f..1aa48b762 100644 --- a/plugins/samplesink/bladerf2output/bladerf2outputsettings.cpp +++ b/plugins/samplesink/bladerf2output/bladerf2outputsettings.cpp @@ -41,6 +41,7 @@ void BladeRF2OutputSettings::resetToDefaults() m_reverseAPIAddress = "127.0.0.1"; m_reverseAPIPort = 8888; m_reverseAPIDeviceIndex = 0; + m_workspaceIndex = 0; } QByteArray BladeRF2OutputSettings::serialize() const @@ -59,6 +60,8 @@ QByteArray BladeRF2OutputSettings::serialize() const s.writeString(10, m_reverseAPIAddress); s.writeU32(11, m_reverseAPIPort); s.writeU32(12, m_reverseAPIDeviceIndex); + s.writeS32(13, m_workspaceIndex); + s.writeBlob(14, m_geometryBytes); return s.final(); } @@ -97,6 +100,8 @@ bool BladeRF2OutputSettings::deserialize(const QByteArray& data) d.readU32(12, &uintval, 0); m_reverseAPIDeviceIndex = uintval > 99 ? 99 : uintval; + d.readS32(13, &m_workspaceIndex, 0); + d.readBlob(14, &m_geometryBytes); return true; } diff --git a/plugins/samplesink/bladerf2output/bladerf2outputsettings.h b/plugins/samplesink/bladerf2output/bladerf2outputsettings.h index 69a4dd3b3..21e5e762a 100644 --- a/plugins/samplesink/bladerf2output/bladerf2outputsettings.h +++ b/plugins/samplesink/bladerf2output/bladerf2outputsettings.h @@ -35,6 +35,8 @@ struct BladeRF2OutputSettings { QString m_reverseAPIAddress; uint16_t m_reverseAPIPort; uint16_t m_reverseAPIDeviceIndex; + int m_workspaceIndex; + QByteArray m_geometryBytes; BladeRF2OutputSettings(); void resetToDefaults(); diff --git a/plugins/samplesink/fileoutput/fileoutputgui.cpp b/plugins/samplesink/fileoutput/fileoutputgui.cpp index 3a06d16e0..60c61e310 100644 --- a/plugins/samplesink/fileoutput/fileoutputgui.cpp +++ b/plugins/samplesink/fileoutput/fileoutputgui.cpp @@ -51,7 +51,11 @@ FileOutputGui::FileOutputGui(DeviceUISet *deviceUISet, QWidget* parent) : m_tickCount(0), m_lastEngineState(DeviceAPI::StNotStarted) { - ui->setupUi(this); + setAttribute(Qt::WA_DeleteOnClose, true); + ui->setupUi(getContents()); + getContents()->setStyleSheet("#FileOutputGui { border: 1px solid #C06900 }"); + m_helpURL = "plugins/samplesink/fileoutput/readme.md"; + ui->centerFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold)); ui->centerFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold)); ui->centerFrequency->setValueRange(7, 0, pow(10,7)); @@ -67,6 +71,7 @@ FileOutputGui::FileOutputGui(DeviceUISet *deviceUISet, QWidget* parent) : m_statusTimer.start(500); displaySettings(); + makeUIConnections(); m_deviceSampleSink = (FileOutput*) m_deviceUISet->m_deviceAPI->getSampleSink(); connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()), Qt::QueuedConnection); @@ -316,3 +321,12 @@ void FileOutputGui::tick() m_deviceSampleSink->getInputMessageQueue()->push(message); } } + +void FileOutputGui::makeUIConnections() +{ + QObject::connect(ui->centerFrequency, &ValueDial::changed, this, &FileOutputGui::on_centerFrequency_changed); + QObject::connect(ui->sampleRate, &ValueDial::changed, this, &FileOutputGui::on_sampleRate_changed); + QObject::connect(ui->startStop, &ButtonSwitch::toggled, this, &FileOutputGui::on_startStop_toggled); + QObject::connect(ui->showFileDialog, &QPushButton::clicked, this, &FileOutputGui::on_showFileDialog_clicked); + QObject::connect(ui->interp, QOverload::of(&QComboBox::currentIndexChanged), this, &FileOutputGui::on_interp_currentIndexChanged); +} diff --git a/plugins/samplesink/fileoutput/fileoutputgui.h b/plugins/samplesink/fileoutput/fileoutputgui.h index 0b8061bbb..ef3445595 100644 --- a/plugins/samplesink/fileoutput/fileoutputgui.h +++ b/plugins/samplesink/fileoutput/fileoutputgui.h @@ -47,6 +47,10 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } + virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; } + virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; } + virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; } + virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; } private: Ui::FileOutputGui* ui; @@ -76,6 +80,7 @@ private: void updateWithStreamTime(); void updateSampleRateAndFrequency(); bool handleMessage(const Message& message); + void makeUIConnections(); private slots: void handleInputMessages(); diff --git a/plugins/samplesink/fileoutput/fileoutputgui.ui b/plugins/samplesink/fileoutput/fileoutputgui.ui index 6deafee04..82c4c8094 100644 --- a/plugins/samplesink/fileoutput/fileoutputgui.ui +++ b/plugins/samplesink/fileoutput/fileoutputgui.ui @@ -6,8 +6,8 @@ 0 0 - 350 - 190 + 360 + 144 @@ -18,8 +18,8 @@ - 350 - 190 + 360 + 0 @@ -364,23 +364,6 @@ - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - diff --git a/plugins/samplesink/fileoutput/fileoutputsettings.cpp b/plugins/samplesink/fileoutput/fileoutputsettings.cpp index a4affa826..e07abde6a 100644 --- a/plugins/samplesink/fileoutput/fileoutputsettings.cpp +++ b/plugins/samplesink/fileoutput/fileoutputsettings.cpp @@ -33,6 +33,7 @@ void FileOutputSettings::resetToDefaults() m_reverseAPIAddress = "127.0.0.1"; m_reverseAPIPort = 8888; m_reverseAPIDeviceIndex = 0; + m_workspaceIndex = 0; } QByteArray FileOutputSettings::serialize() const @@ -46,6 +47,8 @@ QByteArray FileOutputSettings::serialize() const s.writeString(5, m_reverseAPIAddress); s.writeU32(6, m_reverseAPIPort); s.writeU32(7, m_reverseAPIDeviceIndex); + s.writeS32(8, m_workspaceIndex); + s.writeBlob(9, m_geometryBytes); return s.final(); } @@ -79,6 +82,8 @@ bool FileOutputSettings::deserialize(const QByteArray& data) d.readU32(7, &uintval, 0); m_reverseAPIDeviceIndex = uintval > 99 ? 99 : uintval; + d.readS32(8, &m_workspaceIndex, 0); + d.readBlob(9, &m_geometryBytes); return true; } diff --git a/plugins/samplesink/fileoutput/fileoutputsettings.h b/plugins/samplesink/fileoutput/fileoutputsettings.h index 3b6b666c4..a75d9af88 100644 --- a/plugins/samplesink/fileoutput/fileoutputsettings.h +++ b/plugins/samplesink/fileoutput/fileoutputsettings.h @@ -29,6 +29,8 @@ struct FileOutputSettings { QString m_reverseAPIAddress; uint16_t m_reverseAPIPort; uint16_t m_reverseAPIDeviceIndex; + int m_workspaceIndex; + QByteArray m_geometryBytes; FileOutputSettings(); void resetToDefaults(); diff --git a/plugins/samplesink/hackrfoutput/hackrfoutputgui.cpp b/plugins/samplesink/hackrfoutput/hackrfoutputgui.cpp index a7453bea3..1bfa6105e 100644 --- a/plugins/samplesink/hackrfoutput/hackrfoutputgui.cpp +++ b/plugins/samplesink/hackrfoutput/hackrfoutputgui.cpp @@ -45,9 +45,12 @@ HackRFOutputGui::HackRFOutputGui(DeviceUISet *deviceUISet, QWidget* parent) : m_lastEngineState(DeviceAPI::StNotStarted), m_doApplySettings(true) { + setAttribute(Qt::WA_DeleteOnClose, true); m_deviceSampleSink = (HackRFOutput*) m_deviceUISet->m_deviceAPI->getSampleSink(); - ui->setupUi(this); + ui->setupUi(getContents()); + getContents()->setStyleSheet("#HackRFOutputGui { border: 1px solid #C06900 }"); + m_helpURL = "plugins/samplesink/hackrfoutput/readme.md"; ui->centerFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold)); ui->centerFrequency->setValueRange(7, 0U, 7250000U); @@ -64,6 +67,7 @@ HackRFOutputGui::HackRFOutputGui(DeviceUISet *deviceUISet, QWidget* parent) : displaySettings(); displayBandwidths(); sendSettings(); + makeUIConnections(); connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()), Qt::QueuedConnection); } @@ -454,3 +458,19 @@ void HackRFOutputGui::openDeviceSettingsDialog(const QPoint& p) sendSettings(); } + +void HackRFOutputGui::makeUIConnections() +{ + QObject::connect(ui->centerFrequency, &ValueDial::changed, this, &HackRFOutputGui::on_centerFrequency_changed); + QObject::connect(ui->sampleRate, &ValueDial::changed, this, &HackRFOutputGui::on_sampleRate_changed); + QObject::connect(ui->LOppm, &QSlider::valueChanged, this, &HackRFOutputGui::on_LOppm_valueChanged); + QObject::connect(ui->biasT, &QCheckBox::stateChanged, this, &HackRFOutputGui::on_biasT_stateChanged); + QObject::connect(ui->interp, QOverload::of(&QComboBox::currentIndexChanged), this, &HackRFOutputGui::on_interp_currentIndexChanged); + QObject::connect(ui->fcPos, QOverload::of(&QComboBox::currentIndexChanged), this, &HackRFOutputGui::on_fcPos_currentIndexChanged); + QObject::connect(ui->lnaExt, &QCheckBox::stateChanged, this, &HackRFOutputGui::on_lnaExt_stateChanged); + QObject::connect(ui->bbFilter, QOverload::of(&QComboBox::currentIndexChanged), this, &HackRFOutputGui::on_bbFilter_currentIndexChanged); + QObject::connect(ui->txvga, &QSlider::valueChanged, this, &HackRFOutputGui::on_txvga_valueChanged); + QObject::connect(ui->startStop, &ButtonSwitch::toggled, this, &HackRFOutputGui::on_startStop_toggled); + QObject::connect(ui->sampleRateMode, &QToolButton::toggled, this, &HackRFOutputGui::on_sampleRateMode_toggled); + QObject::connect(ui->transverter, &TransverterButton::clicked, this, &HackRFOutputGui::on_transverter_clicked); +} diff --git a/plugins/samplesink/hackrfoutput/hackrfoutputgui.h b/plugins/samplesink/hackrfoutput/hackrfoutputgui.h index 0774a134a..44ac8ab02 100644 --- a/plugins/samplesink/hackrfoutput/hackrfoutputgui.h +++ b/plugins/samplesink/hackrfoutput/hackrfoutputgui.h @@ -55,6 +55,10 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } + virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; } + virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; } + virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; } + virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; } private: Ui::HackRFOutputGui* ui; @@ -81,6 +85,7 @@ private: void updateFrequencyLimits(); void blockApplySettings(bool block); bool handleMessage(const Message& message); + void makeUIConnections(); private slots: void handleInputMessages(); diff --git a/plugins/samplesink/hackrfoutput/hackrfoutputsettings.cpp b/plugins/samplesink/hackrfoutput/hackrfoutputsettings.cpp index e876d4c66..498b18951 100644 --- a/plugins/samplesink/hackrfoutput/hackrfoutputsettings.cpp +++ b/plugins/samplesink/hackrfoutput/hackrfoutputsettings.cpp @@ -43,6 +43,7 @@ void HackRFOutputSettings::resetToDefaults() m_reverseAPIAddress = "127.0.0.1"; m_reverseAPIPort = 8888; m_reverseAPIDeviceIndex = 0; + m_workspaceIndex = 0; } QByteArray HackRFOutputSettings::serialize() const @@ -63,6 +64,8 @@ QByteArray HackRFOutputSettings::serialize() const s.writeU32(12, m_reverseAPIDeviceIndex); s.writeBool(13, m_transverterMode); s.writeS64(14, m_transverterDeltaFrequency); + s.writeS32(15, m_workspaceIndex); + s.writeBlob(16, m_geometryBytes); return s.final(); } @@ -105,6 +108,8 @@ bool HackRFOutputSettings::deserialize(const QByteArray& data) m_reverseAPIDeviceIndex = uintval > 99 ? 99 : uintval; d.readBool(13, &m_transverterMode, false); d.readS64(14, &m_transverterDeltaFrequency, 0); + d.readS32(15, &m_workspaceIndex, 0); + d.readBlob(16, &m_geometryBytes); return true; } diff --git a/plugins/samplesink/hackrfoutput/hackrfoutputsettings.h b/plugins/samplesink/hackrfoutput/hackrfoutputsettings.h index 25ffe4c54..e8b4ee158 100644 --- a/plugins/samplesink/hackrfoutput/hackrfoutputsettings.h +++ b/plugins/samplesink/hackrfoutput/hackrfoutputsettings.h @@ -43,6 +43,8 @@ struct HackRFOutputSettings { QString m_reverseAPIAddress; uint16_t m_reverseAPIPort; uint16_t m_reverseAPIDeviceIndex; + int m_workspaceIndex; + QByteArray m_geometryBytes; HackRFOutputSettings(); void resetToDefaults(); diff --git a/plugins/samplesink/limesdroutput/limesdroutputgui.cpp b/plugins/samplesink/limesdroutput/limesdroutputgui.cpp index 7bca77a1b..7e4ebe7dc 100644 --- a/plugins/samplesink/limesdroutput/limesdroutputgui.cpp +++ b/plugins/samplesink/limesdroutput/limesdroutputgui.cpp @@ -42,9 +42,12 @@ LimeSDROutputGUI::LimeSDROutputGUI(DeviceUISet *deviceUISet, QWidget* parent) : m_statusCounter(0), m_deviceStatusCounter(0) { + setAttribute(Qt::WA_DeleteOnClose, true); m_limeSDROutput = (LimeSDROutput*) m_deviceUISet->m_deviceAPI->getSampleSink(); - ui->setupUi(this); + ui->setupUi(getContents()); + getContents()->setStyleSheet("#LimeSDROutputGUI { border: 1px solid #C06900 }"); + m_helpURL = "plugins/samplesink/limesdroutput/readme.md"; float minF, maxF; @@ -94,6 +97,7 @@ LimeSDROutputGUI::LimeSDROutputGUI(DeviceUISet *deviceUISet, QWidget* parent) : connect(startStopRightClickEnabler, SIGNAL(rightClick(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &))); sendSettings(); + makeUIConnections(); } LimeSDROutputGUI::~LimeSDROutputGUI() @@ -630,3 +634,22 @@ void LimeSDROutputGUI::openDeviceSettingsDialog(const QPoint& p) sendSettings(); } + +void LimeSDROutputGUI::makeUIConnections() +{ + QObject::connect(ui->startStop, &ButtonSwitch::toggled, this, &LimeSDROutputGUI::on_startStop_toggled); + QObject::connect(ui->centerFrequency, &ValueDial::changed, this, &LimeSDROutputGUI::on_centerFrequency_changed); + QObject::connect(ui->ncoFrequency, &ValueDialZ::changed, this, &LimeSDROutputGUI::on_ncoFrequency_changed); + QObject::connect(ui->ncoEnable, &ButtonSwitch::toggled, this, &LimeSDROutputGUI::on_ncoEnable_toggled); + QObject::connect(ui->sampleRate, &ValueDial::changed, this, &LimeSDROutputGUI::on_sampleRate_changed); + QObject::connect(ui->hwInterp, QOverload::of(&QComboBox::currentIndexChanged), this, &LimeSDROutputGUI::on_hwInterp_currentIndexChanged); + QObject::connect(ui->swInterp, QOverload::of(&QComboBox::currentIndexChanged), this, &LimeSDROutputGUI::on_swInterp_currentIndexChanged); + QObject::connect(ui->lpf, &ValueDial::changed, this, &LimeSDROutputGUI::on_lpf_changed); + QObject::connect(ui->lpFIREnable, &ButtonSwitch::toggled, this, &LimeSDROutputGUI::on_lpFIREnable_toggled); + QObject::connect(ui->lpFIR, &ValueDial::changed, this, &LimeSDROutputGUI::on_lpFIR_changed); + QObject::connect(ui->gain, &QSlider::valueChanged, this, &LimeSDROutputGUI::on_gain_valueChanged); + QObject::connect(ui->antenna, QOverload::of(&QComboBox::currentIndexChanged), this, &LimeSDROutputGUI::on_antenna_currentIndexChanged); + QObject::connect(ui->extClock, &ExternalClockButton::clicked, this, &LimeSDROutputGUI::on_extClock_clicked); + QObject::connect(ui->transverter, &TransverterButton::clicked, this, &LimeSDROutputGUI::on_transverter_clicked); + QObject::connect(ui->sampleRateMode, &QToolButton::toggled, this, &LimeSDROutputGUI::on_sampleRateMode_toggled); +} diff --git a/plugins/samplesink/limesdroutput/limesdroutputgui.h b/plugins/samplesink/limesdroutput/limesdroutputgui.h index 5093b7e80..8ba79e4e3 100644 --- a/plugins/samplesink/limesdroutput/limesdroutputgui.h +++ b/plugins/samplesink/limesdroutput/limesdroutputgui.h @@ -45,6 +45,10 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } + virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; } + virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; } + virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; } + virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; } private: Ui::LimeSDROutputGUI* ui; @@ -75,6 +79,7 @@ private: void updateFrequencyLimits(); void blockApplySettings(bool block); bool handleMessage(const Message& message); + void makeUIConnections(); private slots: void handleInputMessages(); diff --git a/plugins/samplesink/limesdroutput/limesdroutputsettings.cpp b/plugins/samplesink/limesdroutput/limesdroutputsettings.cpp index bd54bb58b..6164e616b 100644 --- a/plugins/samplesink/limesdroutput/limesdroutputsettings.cpp +++ b/plugins/samplesink/limesdroutput/limesdroutputsettings.cpp @@ -47,6 +47,7 @@ void LimeSDROutputSettings::resetToDefaults() m_reverseAPIAddress = "127.0.0.1"; m_reverseAPIPort = 8888; m_reverseAPIDeviceIndex = 0; + m_workspaceIndex = 0; } QByteArray LimeSDROutputSettings::serialize() const @@ -73,6 +74,8 @@ QByteArray LimeSDROutputSettings::serialize() const s.writeString(21, m_reverseAPIAddress); s.writeU32(22, m_reverseAPIPort); s.writeU32(23, m_reverseAPIDeviceIndex); + s.writeS32(24, m_workspaceIndex); + s.writeBlob(25, m_geometryBytes); return s.final(); } @@ -123,6 +126,8 @@ bool LimeSDROutputSettings::deserialize(const QByteArray& data) d.readU32(23, &uintval, 0); m_reverseAPIDeviceIndex = uintval > 99 ? 99 : uintval; + d.readS32(24, &m_workspaceIndex, 0); + d.readBlob(25, &m_geometryBytes); return true; } diff --git a/plugins/samplesink/limesdroutput/limesdroutputsettings.h b/plugins/samplesink/limesdroutput/limesdroutputsettings.h index 6bed846c4..148493670 100644 --- a/plugins/samplesink/limesdroutput/limesdroutputsettings.h +++ b/plugins/samplesink/limesdroutput/limesdroutputsettings.h @@ -65,6 +65,8 @@ struct LimeSDROutputSettings QString m_reverseAPIAddress; uint16_t m_reverseAPIPort; uint16_t m_reverseAPIDeviceIndex; + int m_workspaceIndex; + QByteArray m_geometryBytes; LimeSDROutputSettings(); void resetToDefaults(); diff --git a/plugins/samplesink/localoutput/localoutputgui.cpp b/plugins/samplesink/localoutput/localoutputgui.cpp index 70619e7c4..7fc23d47b 100644 --- a/plugins/samplesink/localoutput/localoutputgui.cpp +++ b/plugins/samplesink/localoutput/localoutputgui.cpp @@ -58,10 +58,13 @@ LocalOutputGui::LocalOutputGui(DeviceUISet *deviceUISet, QWidget* parent) : m_doApplySettings(true), m_forceSettings(true) { + setAttribute(Qt::WA_DeleteOnClose, true); m_paletteGreenText.setColor(QPalette::WindowText, Qt::green); m_paletteWhiteText.setColor(QPalette::WindowText, Qt::white); - ui->setupUi(this); + ui->setupUi(getContents()); + getContents()->setStyleSheet("#LocalOutputGui { border: 1px solid #C06900 }"); + m_helpURL = "plugins/samplesink/localoutput/readme.md"; CRightClickEnabler *startStopRightClickEnabler = new CRightClickEnabler(ui->startStop); connect(startStopRightClickEnabler, SIGNAL(rightClick(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &))); @@ -79,6 +82,7 @@ LocalOutputGui::LocalOutputGui(DeviceUISet *deviceUISet, QWidget* parent) : m_forceSettings = true; sendSettings(); + makeUIConnections(); } LocalOutputGui::~LocalOutputGui() @@ -293,3 +297,8 @@ void LocalOutputGui::openDeviceSettingsDialog(const QPoint& p) sendSettings(); } + +void LocalOutputGui::makeUIConnections() +{ + QObject::connect(ui->startStop, &ButtonSwitch::toggled, this, &LocalOutputGui::on_startStop_toggled); +} diff --git a/plugins/samplesink/localoutput/localoutputgui.h b/plugins/samplesink/localoutput/localoutputgui.h index f9d27e8f4..50a9ecead 100644 --- a/plugins/samplesink/localoutput/localoutputgui.h +++ b/plugins/samplesink/localoutput/localoutputgui.h @@ -46,6 +46,10 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } + virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; } + virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; } + virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; } + virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; } private: Ui::LocalOutputGui* ui; @@ -75,6 +79,7 @@ private: void sendSettings(); void updateSampleRateAndFrequency(); bool handleMessage(const Message& message); + void makeUIConnections(); private slots: void handleInputMessages(); diff --git a/plugins/samplesink/localoutput/localoutputsettings.cpp b/plugins/samplesink/localoutput/localoutputsettings.cpp index 226bfd807..8165dd454 100644 --- a/plugins/samplesink/localoutput/localoutputsettings.cpp +++ b/plugins/samplesink/localoutput/localoutputsettings.cpp @@ -29,6 +29,7 @@ void LocalOutputSettings::resetToDefaults() m_reverseAPIAddress = "127.0.0.1"; m_reverseAPIPort = 8888; m_reverseAPIDeviceIndex = 0; + m_workspaceIndex = 0; } QByteArray LocalOutputSettings::serialize() const @@ -38,6 +39,8 @@ QByteArray LocalOutputSettings::serialize() const s.writeString(4, m_reverseAPIAddress); s.writeU32(5, m_reverseAPIPort); s.writeU32(6, m_reverseAPIDeviceIndex); + s.writeS32(7, m_workspaceIndex); + s.writeBlob(8, m_geometryBytes); return s.final(); } @@ -67,6 +70,9 @@ bool LocalOutputSettings::deserialize(const QByteArray& data) d.readU32(6, &uintval, 0); m_reverseAPIDeviceIndex = uintval > 99 ? 99 : uintval; + d.readS32(7, &m_workspaceIndex, 0); + d.readBlob(8, &m_geometryBytes); + return true; } else diff --git a/plugins/samplesink/localoutput/localoutputsettings.h b/plugins/samplesink/localoutput/localoutputsettings.h index 6c7569f7f..5b42fe082 100644 --- a/plugins/samplesink/localoutput/localoutputsettings.h +++ b/plugins/samplesink/localoutput/localoutputsettings.h @@ -26,6 +26,8 @@ struct LocalOutputSettings { QString m_reverseAPIAddress; uint16_t m_reverseAPIPort; uint16_t m_reverseAPIDeviceIndex; + int m_workspaceIndex; + QByteArray m_geometryBytes; LocalOutputSettings(); void resetToDefaults(); diff --git a/plugins/samplesink/plutosdroutput/plutosdroutputgui.cpp b/plugins/samplesink/plutosdroutput/plutosdroutputgui.cpp index be0b3e59d..006c31900 100644 --- a/plugins/samplesink/plutosdroutput/plutosdroutputgui.cpp +++ b/plugins/samplesink/plutosdroutput/plutosdroutputgui.cpp @@ -45,9 +45,12 @@ PlutoSDROutputGUI::PlutoSDROutputGUI(DeviceUISet *deviceUISet, QWidget* parent) m_doApplySettings(true), m_statusCounter(0) { + setAttribute(Qt::WA_DeleteOnClose, true); m_sampleSink = (PlutoSDROutput*) m_deviceUISet->m_deviceAPI->getSampleSink(); - ui->setupUi(this); + ui->setupUi(getContents()); + getContents()->setStyleSheet("#PlutoSDROutputGUI { border: 1px solid #C06900 }"); + m_helpURL = "plugins/samplesink/plutosdroutput/readme.md"; ui->centerFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold)); updateFrequencyLimits(); @@ -71,6 +74,7 @@ PlutoSDROutputGUI::PlutoSDROutputGUI(DeviceUISet *deviceUISet, QWidget* parent) blockApplySettings(true); displaySettings(); + makeUIConnections(); blockApplySettings(false); connect(&m_updateTimer, SIGNAL(timeout()), this, SLOT(updateHardware())); @@ -493,3 +497,21 @@ void PlutoSDROutputGUI::openDeviceSettingsDialog(const QPoint& p) sendSettings(); } + +void PlutoSDROutputGUI::makeUIConnections() +{ + QObject::connect(ui->startStop, &ButtonSwitch::toggled, this, &PlutoSDROutputGUI::on_startStop_toggled); + QObject::connect(ui->centerFrequency, &ValueDial::changed, this, &PlutoSDROutputGUI::on_centerFrequency_changed); + QObject::connect(ui->loPPM, &QSlider::valueChanged, this, &PlutoSDROutputGUI::on_loPPM_valueChanged); + QObject::connect(ui->swInterp, QOverload::of(&QComboBox::currentIndexChanged), this, &PlutoSDROutputGUI::on_swInterp_currentIndexChanged); + QObject::connect(ui->sampleRate, &ValueDial::changed, this, &PlutoSDROutputGUI::on_sampleRate_changed); + QObject::connect(ui->lpf, &ValueDial::changed, this, &PlutoSDROutputGUI::on_lpf_changed); + QObject::connect(ui->lpFIREnable, &ButtonSwitch::toggled, this, &PlutoSDROutputGUI::on_lpFIREnable_toggled); + QObject::connect(ui->lpFIR, &ValueDial::changed, this, &PlutoSDROutputGUI::on_lpFIR_changed); + QObject::connect(ui->lpFIRInterpolation, QOverload::of(&QComboBox::currentIndexChanged), this, &PlutoSDROutputGUI::on_lpFIRInterpolation_currentIndexChanged); + QObject::connect(ui->lpFIRGain, QOverload::of(&QComboBox::currentIndexChanged), this, &PlutoSDROutputGUI::on_lpFIRGain_currentIndexChanged); + QObject::connect(ui->att, &QDial::valueChanged, this, &PlutoSDROutputGUI::on_att_valueChanged); + QObject::connect(ui->antenna, QOverload::of(&QComboBox::currentIndexChanged), this, &PlutoSDROutputGUI::on_antenna_currentIndexChanged); + QObject::connect(ui->transverter, &TransverterButton::clicked, this, &PlutoSDROutputGUI::on_transverter_clicked); + QObject::connect(ui->sampleRateMode, &QToolButton::toggled, this, &PlutoSDROutputGUI::on_sampleRateMode_toggled); +} diff --git a/plugins/samplesink/plutosdroutput/plutosdroutputgui.h b/plugins/samplesink/plutosdroutput/plutosdroutputgui.h index 61db78122..9851da659 100644 --- a/plugins/samplesink/plutosdroutput/plutosdroutputgui.h +++ b/plugins/samplesink/plutosdroutput/plutosdroutputgui.h @@ -47,6 +47,10 @@ public: virtual QByteArray serialize() const; virtual bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } + virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; } + virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; } + virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; } + virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; } private: Ui::PlutoSDROutputGUI* ui; @@ -73,6 +77,7 @@ private: void setSampleRateLimits(); void updateFrequencyLimits(); bool handleMessage(const Message& message); + void makeUIConnections(); private slots: void on_startStop_toggled(bool checked); diff --git a/plugins/samplesink/plutosdroutput/plutosdroutputsettings.cpp b/plugins/samplesink/plutosdroutput/plutosdroutputsettings.cpp index d42615dac..6885defcc 100644 --- a/plugins/samplesink/plutosdroutput/plutosdroutputsettings.cpp +++ b/plugins/samplesink/plutosdroutput/plutosdroutputsettings.cpp @@ -44,6 +44,7 @@ void PlutoSDROutputSettings::resetToDefaults() m_reverseAPIAddress = "127.0.0.1"; m_reverseAPIPort = 8888; m_reverseAPIDeviceIndex = 0; + m_workspaceIndex = 0; } QByteArray PlutoSDROutputSettings::serialize() const @@ -66,6 +67,8 @@ QByteArray PlutoSDROutputSettings::serialize() const s.writeString(18, m_reverseAPIAddress); s.writeU32(19, m_reverseAPIPort); s.writeU32(20, m_reverseAPIDeviceIndex); + s.writeS32(21, m_workspaceIndex); + s.writeBlob(22, m_geometryBytes); return s.final(); } @@ -119,6 +122,8 @@ bool PlutoSDROutputSettings::deserialize(const QByteArray& data) d.readU32(20, &uintval, 0); m_reverseAPIDeviceIndex = uintval > 99 ? 99 : uintval; + d.readS32(21, &m_workspaceIndex, 0); + d.readBlob(22, &m_geometryBytes); return true; } diff --git a/plugins/samplesink/plutosdroutput/plutosdroutputsettings.h b/plugins/samplesink/plutosdroutput/plutosdroutputsettings.h index 02f917885..db4f4d5ad 100644 --- a/plugins/samplesink/plutosdroutput/plutosdroutputsettings.h +++ b/plugins/samplesink/plutosdroutput/plutosdroutputsettings.h @@ -51,6 +51,8 @@ struct PlutoSDROutputSettings { QString m_reverseAPIAddress; uint16_t m_reverseAPIPort; uint16_t m_reverseAPIDeviceIndex; + int m_workspaceIndex; + QByteArray m_geometryBytes; PlutoSDROutputSettings(); void resetToDefaults(); diff --git a/plugins/samplesink/remoteoutput/remoteoutputgui.cpp b/plugins/samplesink/remoteoutput/remoteoutputgui.cpp index 393dead1e..447d0d232 100644 --- a/plugins/samplesink/remoteoutput/remoteoutputgui.cpp +++ b/plugins/samplesink/remoteoutput/remoteoutputgui.cpp @@ -56,6 +56,7 @@ RemoteOutputSinkGui::RemoteOutputSinkGui(DeviceUISet *deviceUISet, QWidget* pare m_forceSettings(true), m_remoteAPIConnected(false) { + setAttribute(Qt::WA_DeleteOnClose, true); m_countUnrecoverable = 0; m_countRecovered = 0; m_lastCountUnrecoverable = 0; @@ -66,7 +67,9 @@ RemoteOutputSinkGui::RemoteOutputSinkGui(DeviceUISet *deviceUISet, QWidget* pare m_paletteRedText.setColor(QPalette::WindowText, Qt::red); m_paletteWhiteText.setColor(QPalette::WindowText, Qt::white); - ui->setupUi(this); + ui->setupUi(getContents()); + getContents()->setStyleSheet("#RemoteOutputGui { border: 1px solid #C06900 }"); + m_helpURL = "plugins/samplesink/remoteoutput/readme.md"; connect(&(m_deviceUISet->m_deviceAPI->getMasterTimer()), SIGNAL(timeout()), this, SLOT(tick())); connect(&m_updateTimer, SIGNAL(timeout()), this, SLOT(updateHardware())); @@ -88,6 +91,7 @@ RemoteOutputSinkGui::RemoteOutputSinkGui(DeviceUISet *deviceUISet, QWidget* pare displaySettings(); sendSettings(); + makeUIConnections(); } RemoteOutputSinkGui::~RemoteOutputSinkGui() @@ -540,3 +544,19 @@ void RemoteOutputSinkGui::openDeviceSettingsDialog(const QPoint& p) sendSettings(); } + +void RemoteOutputSinkGui::makeUIConnections() +{ + QObject::connect(ui->nbFECBlocks, &QDial::valueChanged, this, &RemoteOutputSinkGui::on_nbFECBlocks_valueChanged); + QObject::connect(ui->deviceIndex, &QLineEdit::returnPressed, this, &RemoteOutputSinkGui::on_deviceIndex_returnPressed); + QObject::connect(ui->channelIndex, &QLineEdit::returnPressed, this, &RemoteOutputSinkGui::on_channelIndex_returnPressed); + QObject::connect(ui->nbTxBytes, QOverload::of(&QComboBox::currentIndexChanged), this, &RemoteOutputSinkGui::on_nbTxBytes_currentIndexChanged); + QObject::connect(ui->apiAddress, &QLineEdit::returnPressed, this, &RemoteOutputSinkGui::on_apiAddress_returnPressed); + QObject::connect(ui->apiPort, &QLineEdit::returnPressed, this, &RemoteOutputSinkGui::on_apiPort_returnPressed); + QObject::connect(ui->dataAddress, &QLineEdit::returnPressed, this, &RemoteOutputSinkGui::on_dataAddress_returnPressed); + QObject::connect(ui->dataPort, &QLineEdit::returnPressed, this, &RemoteOutputSinkGui::on_dataPort_returnPressed); + QObject::connect(ui->apiApplyButton, &QPushButton::clicked, this, &RemoteOutputSinkGui::on_apiApplyButton_clicked); + QObject::connect(ui->dataApplyButton, &QPushButton::clicked, this, &RemoteOutputSinkGui::on_dataApplyButton_clicked); + QObject::connect(ui->startStop, &ButtonSwitch::toggled, this, &RemoteOutputSinkGui::on_startStop_toggled); + QObject::connect(ui->eventCountsReset, &QPushButton::clicked, this, &RemoteOutputSinkGui::on_eventCountsReset_clicked); +} diff --git a/plugins/samplesink/remoteoutput/remoteoutputgui.h b/plugins/samplesink/remoteoutput/remoteoutputgui.h index 7b1964440..f2f440d74 100644 --- a/plugins/samplesink/remoteoutput/remoteoutputgui.h +++ b/plugins/samplesink/remoteoutput/remoteoutputgui.h @@ -77,6 +77,10 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } + virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; } + virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; } + virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; } + virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; } private: Ui::RemoteOutputGui* ui; @@ -123,6 +127,7 @@ private: void displayEventStatus(int recoverableCount, int unrecoverableCount); void displayEventTimer(); bool handleMessage(const Message& message); + void makeUIConnections(); private slots: void handleInputMessages(); diff --git a/plugins/samplesink/remoteoutput/remoteoutputsettings.cpp b/plugins/samplesink/remoteoutput/remoteoutputsettings.cpp index cbff6e6f0..65a222484 100644 --- a/plugins/samplesink/remoteoutput/remoteoutputsettings.cpp +++ b/plugins/samplesink/remoteoutput/remoteoutputsettings.cpp @@ -37,6 +37,7 @@ void RemoteOutputSettings::resetToDefaults() m_reverseAPIAddress = "127.0.0.1"; m_reverseAPIPort = 8888; m_reverseAPIDeviceIndex = 0; + m_workspaceIndex = 0; } QByteArray RemoteOutputSettings::serialize() const @@ -55,6 +56,8 @@ QByteArray RemoteOutputSettings::serialize() const s.writeString(13, m_reverseAPIAddress); s.writeU32(14, m_reverseAPIPort); s.writeU32(15, m_reverseAPIDeviceIndex); + s.writeS32(16, m_workspaceIndex); + s.writeBlob(17, m_geometryBytes); return s.final(); } @@ -95,6 +98,8 @@ bool RemoteOutputSettings::deserialize(const QByteArray& data) d.readU32(15, &uintval, 0); m_reverseAPIDeviceIndex = uintval > 99 ? 99 : uintval; + d.readS32(16, &m_workspaceIndex, 0); + d.readBlob(17, &m_geometryBytes); return true; } diff --git a/plugins/samplesink/remoteoutput/remoteoutputsettings.h b/plugins/samplesink/remoteoutput/remoteoutputsettings.h index 830e056dd..814577e3a 100644 --- a/plugins/samplesink/remoteoutput/remoteoutputsettings.h +++ b/plugins/samplesink/remoteoutput/remoteoutputsettings.h @@ -34,6 +34,8 @@ struct RemoteOutputSettings { QString m_reverseAPIAddress; uint16_t m_reverseAPIPort; uint16_t m_reverseAPIDeviceIndex; + int m_workspaceIndex; + QByteArray m_geometryBytes; RemoteOutputSettings(); void resetToDefaults(); diff --git a/plugins/samplesink/soapysdroutput/soapysdroutputgui.cpp b/plugins/samplesink/soapysdroutput/soapysdroutputgui.cpp index 677de3e3f..133925b01 100644 --- a/plugins/samplesink/soapysdroutput/soapysdroutputgui.cpp +++ b/plugins/samplesink/soapysdroutput/soapysdroutputgui.cpp @@ -57,8 +57,11 @@ SoapySDROutputGui::SoapySDROutputGui(DeviceUISet *deviceUISet, QWidget* parent) m_autoDCCorrection(0), m_autoIQCorrection(0) { + setAttribute(Qt::WA_DeleteOnClose, true); m_sampleSink = (SoapySDROutput*) m_deviceUISet->m_deviceAPI->getSampleSink(); - ui->setupUi(this); + ui->setupUi(getContents()); + getContents()->setStyleSheet("#SoapySDROutputGui { border: 1px solid #C06900 }"); + m_helpURL = "plugins/samplesink/soapysdroutput/readme.md"; ui->centerFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold)); uint64_t f_min, f_max; @@ -99,6 +102,7 @@ SoapySDROutputGui::SoapySDROutputGui(DeviceUISet *deviceUISet, QWidget* parent) m_sampleSink->setMessageQueueToGUI(&m_inputMessageQueue); sendSettings(); + makeUIConnections(); } SoapySDROutputGui::~SoapySDROutputGui() @@ -883,3 +887,12 @@ void SoapySDROutputGui::openDeviceSettingsDialog(const QPoint& p) sendSettings(); } + +void SoapySDROutputGui::makeUIConnections() +{ + QObject::connect(ui->centerFrequency, &ValueDial::changed, this, &SoapySDROutputGui::on_centerFrequency_changed); + QObject::connect(ui->LOppm, &QSlider::valueChanged, this, &SoapySDROutputGui::on_LOppm_valueChanged); + QObject::connect(ui->interp, QOverload::of(&QComboBox::currentIndexChanged), this, &SoapySDROutputGui::on_interp_currentIndexChanged); + QObject::connect(ui->transverter, &TransverterButton::clicked, this, &SoapySDROutputGui::on_transverter_clicked); + QObject::connect(ui->startStop, &ButtonSwitch::toggled, this, &SoapySDROutputGui::on_startStop_toggled); +} diff --git a/plugins/samplesink/soapysdroutput/soapysdroutputgui.h b/plugins/samplesink/soapysdroutput/soapysdroutputgui.h index a16e2b779..8f3b53e7f 100644 --- a/plugins/samplesink/soapysdroutput/soapysdroutputgui.h +++ b/plugins/samplesink/soapysdroutput/soapysdroutputgui.h @@ -53,6 +53,10 @@ public: virtual QByteArray serialize() const; virtual bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } + virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; } + virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; } + virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; } + virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; } private: void createRangesControl( @@ -107,6 +111,7 @@ private: void updateSampleRateAndFrequency(); void updateFrequencyLimits(); void setCenterFrequencySetting(uint64_t kHzValue); + void makeUIConnections(); private slots: void handleInputMessages(); diff --git a/plugins/samplesink/soapysdroutput/soapysdroutputsettings.cpp b/plugins/samplesink/soapysdroutput/soapysdroutputsettings.cpp index 7d7a431d0..2cc77d0fe 100644 --- a/plugins/samplesink/soapysdroutput/soapysdroutputsettings.cpp +++ b/plugins/samplesink/soapysdroutput/soapysdroutputsettings.cpp @@ -47,6 +47,7 @@ void SoapySDROutputSettings::resetToDefaults() m_reverseAPIAddress = "127.0.0.1"; m_reverseAPIPort = 8888; m_reverseAPIDeviceIndex = 0; + m_workspaceIndex = 0; } QByteArray SoapySDROutputSettings::serialize() const @@ -76,6 +77,8 @@ QByteArray SoapySDROutputSettings::serialize() const s.writeString(24, m_reverseAPIAddress); s.writeU32(25, m_reverseAPIPort); s.writeU32(26, m_reverseAPIDeviceIndex); + s.writeS32(27, m_workspaceIndex); + s.writeBlob(28, m_geometryBytes); return s.final(); } @@ -133,6 +136,8 @@ bool SoapySDROutputSettings::deserialize(const QByteArray& data) d.readU32(26, &uintval, 0); m_reverseAPIDeviceIndex = uintval > 99 ? 99 : uintval; + d.readS32(27, &m_workspaceIndex, 0); + d.readBlob(28, &m_geometryBytes); return true; } diff --git a/plugins/samplesink/soapysdroutput/soapysdroutputsettings.h b/plugins/samplesink/soapysdroutput/soapysdroutputsettings.h index 9589a65f1..7216385aa 100644 --- a/plugins/samplesink/soapysdroutput/soapysdroutputsettings.h +++ b/plugins/samplesink/soapysdroutput/soapysdroutputsettings.h @@ -45,6 +45,8 @@ struct SoapySDROutputSettings { QString m_reverseAPIAddress; uint16_t m_reverseAPIPort; uint16_t m_reverseAPIDeviceIndex; + int m_workspaceIndex; + QByteArray m_geometryBytes; SoapySDROutputSettings(); void resetToDefaults(); diff --git a/plugins/samplesink/testsink/testsinkgui.cpp b/plugins/samplesink/testsink/testsinkgui.cpp index 5afe0f4ab..9b22a7bec 100644 --- a/plugins/samplesink/testsink/testsinkgui.cpp +++ b/plugins/samplesink/testsink/testsinkgui.cpp @@ -48,7 +48,10 @@ TestSinkGui::TestSinkGui(DeviceUISet *deviceUISet, QWidget* parent) : m_tickCount(0), m_lastEngineState(DeviceAPI::StNotStarted) { - ui->setupUi(this); + setAttribute(Qt::WA_DeleteOnClose, true); + ui->setupUi(getContents()); + getContents()->setStyleSheet("#TestSinkGui { border: 1px solid #C06900 }"); + m_helpURL = "plugins/samplesink/testsink/readme.md"; m_sampleSink = (TestSinkOutput*) m_deviceUISet->m_deviceAPI->getSampleSink(); ui->centerFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold)); @@ -69,6 +72,7 @@ TestSinkGui::TestSinkGui(DeviceUISet *deviceUISet, QWidget* parent) : m_statusTimer.start(500); displaySettings(); + makeUIConnections(); connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()), Qt::QueuedConnection); } @@ -257,3 +261,11 @@ void TestSinkGui::on_startStop_toggled(bool checked) void TestSinkGui::tick() { } + +void TestSinkGui::makeUIConnections() +{ + QObject::connect(ui->centerFrequency, &ValueDial::changed, this, &TestSinkGui::on_centerFrequency_changed); + QObject::connect(ui->sampleRate, &ValueDial::changed, this, &TestSinkGui::on_sampleRate_changed); + QObject::connect(ui->startStop, &ButtonSwitch::toggled, this, &TestSinkGui::on_startStop_toggled); + QObject::connect(ui->interp, QOverload::of(&QComboBox::currentIndexChanged), this, &TestSinkGui::on_interp_currentIndexChanged); +} diff --git a/plugins/samplesink/testsink/testsinkgui.h b/plugins/samplesink/testsink/testsinkgui.h index 257c57cbd..255d7b1de 100644 --- a/plugins/samplesink/testsink/testsinkgui.h +++ b/plugins/samplesink/testsink/testsinkgui.h @@ -48,6 +48,10 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } + virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; } + virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; } + virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; } + virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; } private: Ui::TestSinkGui* ui; @@ -73,6 +77,7 @@ private: void sendSettings(); void updateSampleRateAndFrequency(); bool handleMessage(const Message& message); + void makeUIConnections(); private slots: void handleInputMessages(); diff --git a/plugins/samplesink/testsink/testsinksettings.cpp b/plugins/samplesink/testsink/testsinksettings.cpp index 89ad8b87c..ea14da772 100644 --- a/plugins/samplesink/testsink/testsinksettings.cpp +++ b/plugins/samplesink/testsink/testsinksettings.cpp @@ -30,6 +30,7 @@ void TestSinkSettings::resetToDefaults() m_sampleRate = 48000; m_log2Interp = 0; m_spectrumGUI = nullptr; + m_workspaceIndex = 0; } QByteArray TestSinkSettings::serialize() const @@ -44,6 +45,9 @@ QByteArray TestSinkSettings::serialize() const s.writeBlob(4, m_spectrumGUI->serialize()); } + s.writeS32(5, m_workspaceIndex); + s.writeBlob(6, m_geometryBytes); + return s.final(); } @@ -71,6 +75,9 @@ bool TestSinkSettings::deserialize(const QByteArray& data) m_spectrumGUI->deserialize(bytetmp); } + d.readS32(5, &m_workspaceIndex, 0); + d.readBlob(6, &m_geometryBytes); + return true; } else diff --git a/plugins/samplesink/testsink/testsinksettings.h b/plugins/samplesink/testsink/testsinksettings.h index 355537569..79e082488 100644 --- a/plugins/samplesink/testsink/testsinksettings.h +++ b/plugins/samplesink/testsink/testsinksettings.h @@ -27,6 +27,8 @@ struct TestSinkSettings { quint64 m_sampleRate; quint32 m_log2Interp; Serializable *m_spectrumGUI; + int m_workspaceIndex; + QByteArray m_geometryBytes; TestSinkSettings(); void resetToDefaults(); diff --git a/plugins/samplesink/usrpoutput/usrpoutputgui.cpp b/plugins/samplesink/usrpoutput/usrpoutputgui.cpp index 7e8c52e24..c9848c106 100644 --- a/plugins/samplesink/usrpoutput/usrpoutputgui.cpp +++ b/plugins/samplesink/usrpoutput/usrpoutputgui.cpp @@ -43,9 +43,12 @@ USRPOutputGUI::USRPOutputGUI(DeviceUISet *deviceUISet, QWidget* parent) : m_statusCounter(0), m_deviceStatusCounter(0) { + setAttribute(Qt::WA_DeleteOnClose, true); m_usrpOutput = (USRPOutput*) m_deviceUISet->m_deviceAPI->getSampleSink(); - ui->setupUi(this); + ui->setupUi(getContents()); + getContents()->setStyleSheet("#USRPOutputGUI { border: 1px solid #C06900 }"); + m_helpURL = "plugins/samplesink/usrpoutput/readme.md"; float minF, maxF; @@ -87,6 +90,7 @@ USRPOutputGUI::USRPOutputGUI(DeviceUISet *deviceUISet, QWidget* parent) : connect(startStopRightClickEnabler, SIGNAL(rightClick(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &))); sendSettings(); + makeUIConnections(); } USRPOutputGUI::~USRPOutputGUI() @@ -574,3 +578,18 @@ void USRPOutputGUI::openDeviceSettingsDialog(const QPoint& p) sendSettings(); } + +void USRPOutputGUI::makeUIConnections() +{ + QObject::connect(ui->startStop, &ButtonSwitch::toggled, this, &USRPOutputGUI::on_startStop_toggled); + QObject::connect(ui->centerFrequency, &ValueDial::changed, this, &USRPOutputGUI::on_centerFrequency_changed); + QObject::connect(ui->sampleRate, &ValueDial::changed, this, &USRPOutputGUI::on_sampleRate_changed); + QObject::connect(ui->swInterp, QOverload::of(&QComboBox::currentIndexChanged), this, &USRPOutputGUI::on_swInterp_currentIndexChanged); + QObject::connect(ui->lpf, &ValueDial::changed, this, &USRPOutputGUI::on_lpf_changed); + QObject::connect(ui->loOffset, &ValueDialZ::changed, this, &USRPOutputGUI::on_loOffset_changed); + QObject::connect(ui->gain, &QSlider::valueChanged, this, &USRPOutputGUI::on_gain_valueChanged); + QObject::connect(ui->antenna, QOverload::of(&QComboBox::currentIndexChanged), this, &USRPOutputGUI::on_antenna_currentIndexChanged); + QObject::connect(ui->clockSource, QOverload::of(&QComboBox::currentIndexChanged), this, &USRPOutputGUI::on_clockSource_currentIndexChanged); + QObject::connect(ui->transverter, &TransverterButton::clicked, this, &USRPOutputGUI::on_transverter_clicked); + QObject::connect(ui->sampleRateMode, &QToolButton::toggled, this, &USRPOutputGUI::on_sampleRateMode_toggled); +} diff --git a/plugins/samplesink/usrpoutput/usrpoutputgui.h b/plugins/samplesink/usrpoutput/usrpoutputgui.h index 1f337e40f..4d602bc92 100644 --- a/plugins/samplesink/usrpoutput/usrpoutputgui.h +++ b/plugins/samplesink/usrpoutput/usrpoutputgui.h @@ -52,6 +52,10 @@ public: bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } virtual bool handleMessage(const Message& message); + virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; } + virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; } + virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; } + virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; } private: Ui::USRPOutputGUI* ui; @@ -80,6 +84,7 @@ private: void updateSampleRate(); void updateFrequencyLimits(); void blockApplySettings(bool block); + void makeUIConnections(); private slots: void handleInputMessages(); diff --git a/plugins/samplesink/usrpoutput/usrpoutputsettings.cpp b/plugins/samplesink/usrpoutput/usrpoutputsettings.cpp index ef7c067c2..3483bd7fd 100644 --- a/plugins/samplesink/usrpoutput/usrpoutputsettings.cpp +++ b/plugins/samplesink/usrpoutput/usrpoutputsettings.cpp @@ -42,6 +42,7 @@ void USRPOutputSettings::resetToDefaults() m_reverseAPIAddress = "127.0.0.1"; m_reverseAPIPort = 8888; m_reverseAPIDeviceIndex = 0; + m_workspaceIndex = 0; } QByteArray USRPOutputSettings::serialize() const @@ -61,6 +62,8 @@ QByteArray USRPOutputSettings::serialize() const s.writeU32(11, m_reverseAPIPort); s.writeU32(12, m_reverseAPIDeviceIndex); s.writeS32(13, m_loOffset); + s.writeS32(14, m_workspaceIndex); + s.writeBlob(15, m_geometryBytes); return s.final(); } @@ -100,6 +103,8 @@ bool USRPOutputSettings::deserialize(const QByteArray& data) d.readU32(12, &uintval, 0); m_reverseAPIDeviceIndex = uintval > 99 ? 99 : uintval; d.readS32(13, &m_loOffset, 0); + d.readS32(14, &m_workspaceIndex, 0); + d.readBlob(15, &m_geometryBytes); return true; } diff --git a/plugins/samplesink/usrpoutput/usrpoutputsettings.h b/plugins/samplesink/usrpoutput/usrpoutputsettings.h index 80e20d281..968e9de84 100644 --- a/plugins/samplesink/usrpoutput/usrpoutputsettings.h +++ b/plugins/samplesink/usrpoutput/usrpoutputsettings.h @@ -47,6 +47,8 @@ struct USRPOutputSettings QString m_reverseAPIAddress; uint16_t m_reverseAPIPort; uint16_t m_reverseAPIDeviceIndex; + int m_workspaceIndex; + QByteArray m_geometryBytes; USRPOutputSettings(); void resetToDefaults(); diff --git a/plugins/samplesink/xtrxoutput/xtrxoutputgui.cpp b/plugins/samplesink/xtrxoutput/xtrxoutputgui.cpp index e1e9454d4..9bdc65f45 100644 --- a/plugins/samplesink/xtrxoutput/xtrxoutputgui.cpp +++ b/plugins/samplesink/xtrxoutput/xtrxoutputgui.cpp @@ -44,9 +44,12 @@ XTRXOutputGUI::XTRXOutputGUI(DeviceUISet *deviceUISet, QWidget* parent) : m_statusCounter(0), m_deviceStatusCounter(0) { + setAttribute(Qt::WA_DeleteOnClose, true); m_XTRXOutput = (XTRXOutput*) m_deviceUISet->m_deviceAPI->getSampleSink(); - ui->setupUi(this); + ui->setupUi(getContents()); + getContents()->setStyleSheet("#XTRXOutputGUI { border: 1px solid #C06900 }"); + m_helpURL = "plugins/samplesink/xtrxoutput/readme.md"; float minF, maxF, stepF; @@ -74,6 +77,7 @@ XTRXOutputGUI::XTRXOutputGUI(DeviceUISet *deviceUISet, QWidget* parent) : connect(startStopRightClickEnabler, SIGNAL(rightClick(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &))); displaySettings(); + makeUIConnections(); connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()), Qt::QueuedConnection); } @@ -565,3 +569,20 @@ void XTRXOutputGUI::openDeviceSettingsDialog(const QPoint& p) sendSettings(); } + +void XTRXOutputGUI::makeUIConnections() +{ + QObject::connect(ui->startStop, &ButtonSwitch::toggled, this, &XTRXOutputGUI::on_startStop_toggled); + QObject::connect(ui->centerFrequency, &ValueDial::changed, this, &XTRXOutputGUI::on_centerFrequency_changed); + QObject::connect(ui->ncoFrequency, &ValueDialZ::changed, this, &XTRXOutputGUI::on_ncoFrequency_changed); + QObject::connect(ui->ncoEnable, &ButtonSwitch::toggled, this, &XTRXOutputGUI::on_ncoEnable_toggled); + QObject::connect(ui->sampleRate, &ValueDial::changed, this, &XTRXOutputGUI::on_sampleRate_changed); + QObject::connect(ui->hwInterp, QOverload::of(&QComboBox::currentIndexChanged), this, &XTRXOutputGUI::on_hwInterp_currentIndexChanged); + QObject::connect(ui->swInterp, QOverload::of(&QComboBox::currentIndexChanged), this, &XTRXOutputGUI::on_swInterp_currentIndexChanged); + QObject::connect(ui->lpf, &ValueDial::changed, this, &XTRXOutputGUI::on_lpf_changed); + QObject::connect(ui->gain, &QSlider::valueChanged, this, &XTRXOutputGUI::on_gain_valueChanged); + QObject::connect(ui->antenna, QOverload::of(&QComboBox::currentIndexChanged), this, &XTRXOutputGUI::on_antenna_currentIndexChanged); + QObject::connect(ui->extClock, &ExternalClockButton::clicked, this, &XTRXOutputGUI::on_extClock_clicked); + QObject::connect(ui->pwrmode, QOverload::of(&QComboBox::currentIndexChanged), this, &XTRXOutputGUI::on_pwrmode_currentIndexChanged); + QObject::connect(ui->sampleRateMode, &QToolButton::toggled, this, &XTRXOutputGUI::on_sampleRateMode_toggled); +} diff --git a/plugins/samplesink/xtrxoutput/xtrxoutputgui.h b/plugins/samplesink/xtrxoutput/xtrxoutputgui.h index b53d1c0f4..c355e7524 100644 --- a/plugins/samplesink/xtrxoutput/xtrxoutputgui.h +++ b/plugins/samplesink/xtrxoutput/xtrxoutputgui.h @@ -44,6 +44,10 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } + virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; } + virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; } + virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; } + virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; } private: Ui::XTRXOutputGUI* ui; @@ -73,6 +77,7 @@ private: void updateDACRate(); void blockApplySettings(bool block); bool handleMessage(const Message& message); + void makeUIConnections(); private slots: void handleInputMessages(); diff --git a/plugins/samplesink/xtrxoutput/xtrxoutputsettings.cpp b/plugins/samplesink/xtrxoutput/xtrxoutputsettings.cpp index f5dfe188c..42ed29e20 100644 --- a/plugins/samplesink/xtrxoutput/xtrxoutputsettings.cpp +++ b/plugins/samplesink/xtrxoutput/xtrxoutputsettings.cpp @@ -42,6 +42,7 @@ void XTRXOutputSettings::resetToDefaults() m_reverseAPIAddress = "127.0.0.1"; m_reverseAPIPort = 8888; m_reverseAPIDeviceIndex = 0; + m_workspaceIndex = 0; } QByteArray XTRXOutputSettings::serialize() const @@ -63,6 +64,8 @@ QByteArray XTRXOutputSettings::serialize() const s.writeString(13, m_reverseAPIAddress); s.writeU32(14, m_reverseAPIPort); s.writeU32(15, m_reverseAPIDeviceIndex); + s.writeS32(16, m_workspaceIndex); + s.writeBlob(17, m_geometryBytes); return s.final(); } @@ -106,6 +109,8 @@ bool XTRXOutputSettings::deserialize(const QByteArray& data) d.readU32(15, &uintval, 0); m_reverseAPIDeviceIndex = uintval > 99 ? 99 : uintval; + d.readS32(16, &m_workspaceIndex, 0); + d.readBlob(17, &m_geometryBytes); return true; } diff --git a/plugins/samplesink/xtrxoutput/xtrxoutputsettings.h b/plugins/samplesink/xtrxoutput/xtrxoutputsettings.h index 71954f555..a7a9909e8 100644 --- a/plugins/samplesink/xtrxoutput/xtrxoutputsettings.h +++ b/plugins/samplesink/xtrxoutput/xtrxoutputsettings.h @@ -49,6 +49,8 @@ struct XTRXOutputSettings QString m_reverseAPIAddress; uint16_t m_reverseAPIPort; uint16_t m_reverseAPIDeviceIndex; + int m_workspaceIndex; + QByteArray m_geometryBytes; XTRXOutputSettings(); void resetToDefaults(); diff --git a/plugins/samplesource/airspy/airspygui.cpp b/plugins/samplesource/airspy/airspygui.cpp index 06ab05bd1..9cc584157 100644 --- a/plugins/samplesource/airspy/airspygui.cpp +++ b/plugins/samplesource/airspy/airspygui.cpp @@ -44,9 +44,11 @@ AirspyGui::AirspyGui(DeviceUISet *deviceUISet, QWidget* parent) : m_sampleSource(0), m_lastEngineState(DeviceAPI::StNotStarted) { + setAttribute(Qt::WA_DeleteOnClose, true); m_sampleSource = (AirspyInput*) m_deviceUISet->m_deviceAPI->getSampleSource(); - ui->setupUi(this); + ui->setupUi(getContents()); + getContents()->setStyleSheet("#AirspyGui { border: 1px solid #C06900 }"); ui->centerFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold)); updateFrequencyLimits(); @@ -65,6 +67,7 @@ AirspyGui::AirspyGui(DeviceUISet *deviceUISet, QWidget* parent) : m_sampleSource->setMessageQueueToGUI(&m_inputMessageQueue); sendSettings(); + makeUIConnections(); } AirspyGui::~AirspyGui() @@ -447,3 +450,22 @@ void AirspyGui::openDeviceSettingsDialog(const QPoint& p) sendSettings(); } + +void AirspyGui::makeUIConnections() +{ + QObject::connect(ui->centerFrequency, &ValueDial::changed, this, &AirspyGui::on_centerFrequency_changed); + QObject::connect(ui->LOppm, &QSlider::valueChanged, this, &AirspyGui::on_LOppm_valueChanged); + QObject::connect(ui->dcOffset, &ButtonSwitch::toggled, this, &AirspyGui::on_dcOffset_toggled); + QObject::connect(ui->iqImbalance, &ButtonSwitch::toggled, this, &AirspyGui::on_iqImbalance_toggled); + QObject::connect(ui->sampleRate, QOverload::of(&QComboBox::currentIndexChanged), this, &AirspyGui::on_sampleRate_currentIndexChanged); + QObject::connect(ui->biasT, &QCheckBox::stateChanged, this, &AirspyGui::on_biasT_stateChanged); + QObject::connect(ui->decim, QOverload::of(&QComboBox::currentIndexChanged), this, &AirspyGui::on_decim_currentIndexChanged); + QObject::connect(ui->fcPos, QOverload::of(&QComboBox::currentIndexChanged), this, &AirspyGui::on_fcPos_currentIndexChanged); + QObject::connect(ui->lna, &QSlider::valueChanged, this, &AirspyGui::on_lna_valueChanged); + QObject::connect(ui->mix, &QSlider::valueChanged, this, &AirspyGui::on_mix_valueChanged); + QObject::connect(ui->vga, &QSlider::valueChanged, this, &AirspyGui::on_vga_valueChanged); + QObject::connect(ui->lnaAGC, &QCheckBox::stateChanged, this, &AirspyGui::on_lnaAGC_stateChanged); + QObject::connect(ui->mixAGC, &QCheckBox::stateChanged, this, &AirspyGui::on_mixAGC_stateChanged); + QObject::connect(ui->startStop, &ButtonSwitch::toggled, this, &AirspyGui::on_startStop_toggled); + QObject::connect(ui->transverter, &TransverterButton::clicked, this, &AirspyGui::on_transverter_clicked); +} diff --git a/plugins/samplesource/airspy/airspygui.h b/plugins/samplesource/airspy/airspygui.h index 3f52caef6..2f0a9c03c 100644 --- a/plugins/samplesource/airspy/airspygui.h +++ b/plugins/samplesource/airspy/airspygui.h @@ -44,6 +44,11 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue* getInputMessageQueue() { return &m_inputMessageQueue; } + virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; } + virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; } + virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; } + virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; } + uint32_t getDevSampleRate(unsigned int index); int getDevSampleRateIndex(uint32_t sampleRate); @@ -70,6 +75,7 @@ private: void updateSampleRateAndFrequency(); void updateFrequencyLimits(); bool handleMessage(const Message& message); + void makeUIConnections(); private slots: void on_centerFrequency_changed(quint64 value); diff --git a/plugins/samplesource/airspy/airspysettings.cpp b/plugins/samplesource/airspy/airspysettings.cpp index 5be260a5a..1026a1213 100644 --- a/plugins/samplesource/airspy/airspysettings.cpp +++ b/plugins/samplesource/airspy/airspysettings.cpp @@ -46,6 +46,7 @@ void AirspySettings::resetToDefaults() m_reverseAPIAddress = "127.0.0.1"; m_reverseAPIPort = 8888; m_reverseAPIDeviceIndex = 0; + m_workspaceIndex = 0; } QByteArray AirspySettings::serialize() const @@ -71,6 +72,8 @@ QByteArray AirspySettings::serialize() const s.writeU32(17, m_reverseAPIPort); s.writeU32(18, m_reverseAPIDeviceIndex); s.writeBool(19, m_iqOrder); + s.writeS32(20, m_workspaceIndex); + s.writeBlob(21, m_geometryBytes); return s.final(); } @@ -118,6 +121,8 @@ bool AirspySettings::deserialize(const QByteArray& data) d.readU32(18, &uintval, 0); m_reverseAPIDeviceIndex = uintval > 99 ? 99 : uintval; d.readBool(19, &m_iqOrder, true); + d.readS32(20, &m_workspaceIndex, 0); + d.readBlob(21, &m_geometryBytes); return true; } diff --git a/plugins/samplesource/airspy/airspysettings.h b/plugins/samplesource/airspy/airspysettings.h index 5b2850fcd..9b6275aa4 100644 --- a/plugins/samplesource/airspy/airspysettings.h +++ b/plugins/samplesource/airspy/airspysettings.h @@ -47,6 +47,8 @@ struct AirspySettings { QString m_reverseAPIAddress; uint16_t m_reverseAPIPort; uint16_t m_reverseAPIDeviceIndex; + int m_workspaceIndex; + QByteArray m_geometryBytes; AirspySettings(); void resetToDefaults(); diff --git a/plugins/samplesource/airspyhf/airspyhfgui.cpp b/plugins/samplesource/airspyhf/airspyhfgui.cpp index 93da0660d..a2e3e2c54 100644 --- a/plugins/samplesource/airspyhf/airspyhfgui.cpp +++ b/plugins/samplesource/airspyhf/airspyhfgui.cpp @@ -43,9 +43,12 @@ AirspyHFGui::AirspyHFGui(DeviceUISet *deviceUISet, QWidget* parent) : m_sampleSource(0), m_lastEngineState(DeviceAPI::StNotStarted) { + setAttribute(Qt::WA_DeleteOnClose, true); m_sampleSource = (AirspyHFInput*) m_deviceUISet->m_deviceAPI->getSampleSource(); - ui->setupUi(this); + ui->setupUi(getContents()); + getContents()->setStyleSheet("#AirspyHFGui { border: 1px solid #C06900 }"); + m_helpURL = "plugins/samplesource/airspyhf/readme.md"; ui->centerFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold)); updateFrequencyLimits(); @@ -64,6 +67,7 @@ AirspyHFGui::AirspyHFGui(DeviceUISet *deviceUISet, QWidget* parent) : m_sampleSource->setMessageQueueToGUI(&m_inputMessageQueue); sendSettings(); + makeUIConnections(); } AirspyHFGui::~AirspyHFGui() @@ -466,3 +470,21 @@ void AirspyHFGui::openDeviceSettingsDialog(const QPoint& p) sendSettings(); } + +void AirspyHFGui::makeUIConnections() +{ + QObject::connect(ui->centerFrequency, &ValueDial::changed, this, &AirspyHFGui::on_centerFrequency_changed); + QObject::connect(ui->LOppm, &QSlider::valueChanged, this, &AirspyHFGui::on_LOppm_valueChanged); + QObject::connect(ui->resetLOppm, &QPushButton::clicked, this, &AirspyHFGui::on_resetLOppm_clicked); + QObject::connect(ui->dcOffset, &ButtonSwitch::toggled, this, &AirspyHFGui::on_dcOffset_toggled); + QObject::connect(ui->iqImbalance, &ButtonSwitch::toggled, this, &AirspyHFGui::on_iqImbalance_toggled); + QObject::connect(ui->sampleRate, QOverload::of(&QComboBox::currentIndexChanged), this, &AirspyHFGui::on_sampleRate_currentIndexChanged); + QObject::connect(ui->decim, QOverload::of(&QComboBox::currentIndexChanged), this, &AirspyHFGui::on_decim_currentIndexChanged); + QObject::connect(ui->startStop, &ButtonSwitch::toggled, this, &AirspyHFGui::on_startStop_toggled); + QObject::connect(ui->transverter, &TransverterButton::clicked, this, &AirspyHFGui::on_transverter_clicked); + QObject::connect(ui->band, QOverload::of(&QComboBox::currentIndexChanged), this, &AirspyHFGui::on_band_currentIndexChanged); + QObject::connect(ui->dsp, &ButtonSwitch::toggled, this, &AirspyHFGui::on_dsp_toggled); + QObject::connect(ui->lna, &ButtonSwitch::toggled, this, &AirspyHFGui::on_lna_toggled); + QObject::connect(ui->agc, QOverload::of(&QComboBox::currentIndexChanged), this, &AirspyHFGui::on_agc_currentIndexChanged); + QObject::connect(ui->att, QOverload::of(&QComboBox::currentIndexChanged), this, &AirspyHFGui::on_att_currentIndexChanged); +} diff --git a/plugins/samplesource/airspyhf/airspyhfgui.h b/plugins/samplesource/airspyhf/airspyhfgui.h index 6c92f3bc2..58e2ce8fa 100644 --- a/plugins/samplesource/airspyhf/airspyhfgui.h +++ b/plugins/samplesource/airspyhf/airspyhfgui.h @@ -45,6 +45,11 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue* getInputMessageQueue() { return &m_inputMessageQueue; } + virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; } + virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; } + virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; } + virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; } + uint32_t getDevSampleRate(unsigned int index); int getDevSampleRateIndex(uint32_t sampleRate); @@ -72,6 +77,7 @@ private: void updateSampleRateAndFrequency(); void updateFrequencyLimits(); bool handleMessage(const Message& message); + void makeUIConnections(); private slots: void on_centerFrequency_changed(quint64 value); diff --git a/plugins/samplesource/airspyhf/airspyhfsettings.cpp b/plugins/samplesource/airspyhf/airspyhfsettings.cpp index 4f7ae4b57..ead7a0f1b 100644 --- a/plugins/samplesource/airspyhf/airspyhfsettings.cpp +++ b/plugins/samplesource/airspyhf/airspyhfsettings.cpp @@ -46,6 +46,7 @@ void AirspyHFSettings::resetToDefaults() m_attenuatorSteps = 0; m_dcBlock = false; m_iqCorrection = false; + m_workspaceIndex = 0; } QByteArray AirspyHFSettings::serialize() const @@ -70,6 +71,8 @@ QByteArray AirspyHFSettings::serialize() const s.writeBool(19, m_dcBlock); s.writeBool(20, m_iqCorrection); s.writeBool(21, m_iqOrder); + s.writeS32(22, m_workspaceIndex); + s.writeBlob(23, m_geometryBytes); return s.final(); } @@ -117,6 +120,8 @@ bool AirspyHFSettings::deserialize(const QByteArray& data) d.readBool(19, &m_dcBlock, false); d.readBool(20, &m_iqCorrection, false); d.readBool(21, &m_iqOrder, true); + d.readS32(22, &m_workspaceIndex, 0); + d.readBlob(23, &m_geometryBytes); return true; } diff --git a/plugins/samplesource/airspyhf/airspyhfsettings.h b/plugins/samplesource/airspyhf/airspyhfsettings.h index 805a0a708..d093f747f 100644 --- a/plugins/samplesource/airspyhf/airspyhfsettings.h +++ b/plugins/samplesource/airspyhf/airspyhfsettings.h @@ -41,6 +41,8 @@ struct AirspyHFSettings quint32 m_attenuatorSteps; bool m_dcBlock; bool m_iqCorrection; + int m_workspaceIndex; + QByteArray m_geometryBytes; AirspyHFSettings(); void resetToDefaults(); diff --git a/plugins/samplesource/audioinput/audioinputgui.cpp b/plugins/samplesource/audioinput/audioinputgui.cpp index 10983f562..29b4fd49f 100644 --- a/plugins/samplesource/audioinput/audioinputgui.cpp +++ b/plugins/samplesource/audioinput/audioinputgui.cpp @@ -40,9 +40,12 @@ AudioInputGui::AudioInputGui(DeviceUISet *deviceUISet, QWidget* parent) : m_sampleSource(nullptr), m_centerFrequency(0) { + setAttribute(Qt::WA_DeleteOnClose, true); m_sampleSource = (AudioInput*) m_deviceUISet->m_deviceAPI->getSampleSource(); - ui->setupUi(this); + ui->setupUi(getContents()); + getContents()->setStyleSheet("#AudioInputGui { border: 1px solid #C06900 }"); + m_helpURL = "plugins/samplesource/audioinput/readme.md"; connect(&m_updateTimer, SIGNAL(timeout()), this, SLOT(updateHardware())); @@ -50,6 +53,7 @@ AudioInputGui::AudioInputGui(DeviceUISet *deviceUISet, QWidget* parent) : connect(startStopRightClickEnabler, SIGNAL(rightClick(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &))); displaySettings(); + makeUIConnections(); connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()), Qt::QueuedConnection); m_sampleSource->setMessageQueueToGUI(&m_inputMessageQueue); @@ -306,3 +310,13 @@ void AudioInputGui::openDeviceSettingsDialog(const QPoint& p) sendSettings(); } + +void AudioInputGui::makeUIConnections() +{ + QObject::connect(ui->device, QOverload::of(&QComboBox::currentIndexChanged), this, &AudioInputGui::on_device_currentIndexChanged); + QObject::connect(ui->sampleRate, QOverload::of(&QComboBox::currentIndexChanged), this, &AudioInputGui::on_sampleRate_currentIndexChanged); + QObject::connect(ui->decim, QOverload::of(&QComboBox::currentIndexChanged), this, &AudioInputGui::on_decim_currentIndexChanged); + QObject::connect(ui->volume, &QDial::valueChanged, this, &AudioInputGui::on_volume_valueChanged); + QObject::connect(ui->channels, QOverload::of(&QComboBox::currentIndexChanged), this, &AudioInputGui::on_channels_currentIndexChanged); + QObject::connect(ui->startStop, &ButtonSwitch::toggled, this, &AudioInputGui::on_startStop_toggled); +} diff --git a/plugins/samplesource/audioinput/audioinputgui.h b/plugins/samplesource/audioinput/audioinputgui.h index 1976091d4..46775ba21 100644 --- a/plugins/samplesource/audioinput/audioinputgui.h +++ b/plugins/samplesource/audioinput/audioinputgui.h @@ -46,6 +46,10 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } + virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; } + virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; } + virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; } + virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; } private: Ui::AudioInputGui* ui; @@ -68,6 +72,7 @@ private: void sendSettings(); void updateSampleRateAndFrequency(); bool handleMessage(const Message& message); + void makeUIConnections(); private slots: void handleInputMessages(); diff --git a/plugins/samplesource/audioinput/audioinputsettings.cpp b/plugins/samplesource/audioinput/audioinputsettings.cpp index 84fb1ae07..777ccfd02 100644 --- a/plugins/samplesource/audioinput/audioinputsettings.cpp +++ b/plugins/samplesource/audioinput/audioinputsettings.cpp @@ -36,6 +36,7 @@ void AudioInputSettings::resetToDefaults() m_reverseAPIAddress = "127.0.0.1"; m_reverseAPIPort = 8888; m_reverseAPIDeviceIndex = 0; + m_workspaceIndex = 0; } QByteArray AudioInputSettings::serialize() const @@ -47,6 +48,8 @@ QByteArray AudioInputSettings::serialize() const s.writeFloat(3, m_volume); s.writeU32(4, m_log2Decim); s.writeS32(5, (int)m_iqMapping); + s.writeS32(6, m_workspaceIndex); + s.writeBlob(7, m_geometryBytes); s.writeBool(24, m_useReverseAPI); s.writeString(25, m_reverseAPIAddress); @@ -75,6 +78,8 @@ bool AudioInputSettings::deserialize(const QByteArray& data) d.readFloat(3, &m_volume, 1.0f); d.readU32(4, &m_log2Decim, 0); d.readS32(5, (int *)&m_iqMapping, IQMapping::L); + d.readS32(6, &m_workspaceIndex, 0); + d.readBlob(7, &m_geometryBytes); d.readBool(24, &m_useReverseAPI, false); d.readString(25, &m_reverseAPIAddress, "127.0.0.1"); diff --git a/plugins/samplesource/audioinput/audioinputsettings.h b/plugins/samplesource/audioinput/audioinputsettings.h index 16f8eb4f1..f707fbead 100644 --- a/plugins/samplesource/audioinput/audioinputsettings.h +++ b/plugins/samplesource/audioinput/audioinputsettings.h @@ -39,6 +39,8 @@ struct AudioInputSettings { QString m_reverseAPIAddress; uint16_t m_reverseAPIPort; uint16_t m_reverseAPIDeviceIndex; + int m_workspaceIndex; + QByteArray m_geometryBytes; AudioInputSettings(); void resetToDefaults(); diff --git a/plugins/samplesource/bladerf1input/bladerf1inputgui.cpp b/plugins/samplesource/bladerf1input/bladerf1inputgui.cpp index 8745d2622..06bf09a2c 100644 --- a/plugins/samplesource/bladerf1input/bladerf1inputgui.cpp +++ b/plugins/samplesource/bladerf1input/bladerf1inputgui.cpp @@ -45,9 +45,12 @@ Bladerf1InputGui::Bladerf1InputGui(DeviceUISet *deviceUISet, QWidget* parent) : m_sampleRate(0), m_lastEngineState(DeviceAPI::StNotStarted) { + setAttribute(Qt::WA_DeleteOnClose, true); m_sampleSource = (Bladerf1Input*) m_deviceUISet->m_deviceAPI->getSampleSource(); - ui->setupUi(this); + ui->setupUi(getContents()); + getContents()->setStyleSheet("#Bladerf1InputGui { border: 1px solid #C06900 }"); + m_helpURL = "plugins/samplesource/bladerf1input/readme.md"; ui->centerFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold)); ui->centerFrequency->setValueRange(7, BLADERF_FREQUENCY_MIN_XB200/1000, BLADERF_FREQUENCY_MAX/1000); @@ -74,6 +77,7 @@ Bladerf1InputGui::Bladerf1InputGui(DeviceUISet *deviceUISet, QWidget* parent) : m_sampleSource->setMessageQueueToGUI(&m_inputMessageQueue); sendSettings(); + makeUIConnections(); } Bladerf1InputGui::~Bladerf1InputGui() @@ -525,3 +529,17 @@ void Bladerf1InputGui::openDeviceSettingsDialog(const QPoint& p) sendSettings(); } + +void Bladerf1InputGui::makeUIConnections() +{ + QObject::connect(ui->centerFrequency, &ValueDial::changed, this, &Bladerf1InputGui::on_centerFrequency_changed); + QObject::connect(ui->sampleRate, &ValueDial::changed, this, &Bladerf1InputGui::on_sampleRate_changed); + QObject::connect(ui->dcOffset, &ButtonSwitch::toggled, this, &Bladerf1InputGui::on_dcOffset_toggled); + QObject::connect(ui->iqImbalance, &ButtonSwitch::toggled, this, &Bladerf1InputGui::on_iqImbalance_toggled); + QObject::connect(ui->bandwidth, QOverload::of(&QComboBox::currentIndexChanged), this, &Bladerf1InputGui::on_bandwidth_currentIndexChanged); + QObject::connect(ui->decim, QOverload::of(&QComboBox::currentIndexChanged), this, &Bladerf1InputGui::on_decim_currentIndexChanged); + QObject::connect(ui->lna, QOverload::of(&QComboBox::currentIndexChanged), this, &Bladerf1InputGui::on_lna_currentIndexChanged); + QObject::connect(ui->fcPos, QOverload::of(&QComboBox::currentIndexChanged), this, &Bladerf1InputGui::on_fcPos_currentIndexChanged); + QObject::connect(ui->startStop, &ButtonSwitch::toggled, this, &Bladerf1InputGui::on_startStop_toggled); + QObject::connect(ui->sampleRateMode, &QToolButton::toggled, this, &Bladerf1InputGui::on_sampleRateMode_toggled); +} diff --git a/plugins/samplesource/bladerf1input/bladerf1inputgui.h b/plugins/samplesource/bladerf1input/bladerf1inputgui.h index 27ea2d695..a774bc4c1 100644 --- a/plugins/samplesource/bladerf1input/bladerf1inputgui.h +++ b/plugins/samplesource/bladerf1input/bladerf1inputgui.h @@ -44,6 +44,10 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } + virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; } + virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; } + virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; } + virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; } private: Ui::Bladerf1InputGui* ui; @@ -70,6 +74,7 @@ private: void updateSampleRateAndFrequency(); void blockApplySettings(bool block); bool handleMessage(const Message& message); + void makeUIConnections(); private slots: void handleInputMessages(); diff --git a/plugins/samplesource/bladerf1input/bladerf1inputsettings.cpp b/plugins/samplesource/bladerf1input/bladerf1inputsettings.cpp index 6745b693e..4975d5c85 100644 --- a/plugins/samplesource/bladerf1input/bladerf1inputsettings.cpp +++ b/plugins/samplesource/bladerf1input/bladerf1inputsettings.cpp @@ -47,6 +47,7 @@ void BladeRF1InputSettings::resetToDefaults() m_reverseAPIAddress = "127.0.0.1"; m_reverseAPIPort = 8888; m_reverseAPIDeviceIndex = 0; + m_workspaceIndex = 0; } QByteArray BladeRF1InputSettings::serialize() const @@ -70,6 +71,8 @@ QByteArray BladeRF1InputSettings::serialize() const s.writeU32(15, m_reverseAPIPort); s.writeU32(16, m_reverseAPIDeviceIndex); s.writeBool(17, m_iqOrder); + s.writeS32(18, m_workspaceIndex); + s.writeBlob(19, m_geometryBytes); return s.final(); } @@ -117,6 +120,8 @@ bool BladeRF1InputSettings::deserialize(const QByteArray& data) d.readU32(16, &uintval, 0); m_reverseAPIDeviceIndex = uintval > 99 ? 99 : uintval; d.readBool(17, &m_iqOrder); + d.readS32(18, &m_workspaceIndex, 0); + d.readBlob(19, &m_geometryBytes); return true; } diff --git a/plugins/samplesource/bladerf1input/bladerf1inputsettings.h b/plugins/samplesource/bladerf1input/bladerf1inputsettings.h index 32ab4ba33..333218ffd 100644 --- a/plugins/samplesource/bladerf1input/bladerf1inputsettings.h +++ b/plugins/samplesource/bladerf1input/bladerf1inputsettings.h @@ -48,6 +48,8 @@ struct BladeRF1InputSettings { QString m_reverseAPIAddress; uint16_t m_reverseAPIPort; uint16_t m_reverseAPIDeviceIndex; + int m_workspaceIndex; + QByteArray m_geometryBytes; BladeRF1InputSettings(); void resetToDefaults(); diff --git a/plugins/samplesource/bladerf2input/bladerf2inputgui.cpp b/plugins/samplesource/bladerf2input/bladerf2inputgui.cpp index 1705b0a8f..824c0e927 100644 --- a/plugins/samplesource/bladerf2input/bladerf2inputgui.cpp +++ b/plugins/samplesource/bladerf2input/bladerf2inputgui.cpp @@ -45,12 +45,15 @@ BladeRF2InputGui::BladeRF2InputGui(DeviceUISet *deviceUISet, QWidget* parent) : m_sampleRate(0), m_lastEngineState(DeviceAPI::StNotStarted) { + setAttribute(Qt::WA_DeleteOnClose, true); m_sampleSource = (BladeRF2Input*) m_deviceUISet->m_deviceAPI->getSampleSource(); int max, min, step; float scale; uint64_t f_min, f_max; - ui->setupUi(this); + ui->setupUi(getContents()); + getContents()->setStyleSheet("#Bladerf2InputGui { border: 1px solid #C06900 }"); + m_helpURL = "plugins/samplesource/bladerf2input/readme.md"; m_sampleSource->getFrequencyRange(f_min, f_max, step, scale); ui->centerFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold)); @@ -96,6 +99,7 @@ BladeRF2InputGui::BladeRF2InputGui(DeviceUISet *deviceUISet, QWidget* parent) : m_sampleSource->setMessageQueueToGUI(&m_inputMessageQueue); sendSettings(); + makeUIConnections(); } BladeRF2InputGui::~BladeRF2InputGui() @@ -542,3 +546,21 @@ int BladeRF2InputGui::getGainValue(float gainDB) // gainDB, m_gainMin, m_gainMax, m_gainStep, m_gainScale, gain); return gain; } + +void BladeRF2InputGui::makeUIConnections() +{ + QObject::connect(ui->centerFrequency, &ValueDial::changed, this, &BladeRF2InputGui::on_centerFrequency_changed); + QObject::connect(ui->LOppm, &QSlider::valueChanged, this, &BladeRF2InputGui::on_LOppm_valueChanged); + QObject::connect(ui->sampleRate, &ValueDial::changed, this, &BladeRF2InputGui::on_sampleRate_changed); + QObject::connect(ui->dcOffset, &ButtonSwitch::toggled, this, &BladeRF2InputGui::on_dcOffset_toggled); + QObject::connect(ui->iqImbalance, &ButtonSwitch::toggled, this, &BladeRF2InputGui::on_iqImbalance_toggled); + QObject::connect(ui->biasTee, &ButtonSwitch::toggled, this, &BladeRF2InputGui::on_biasTee_toggled); + QObject::connect(ui->bandwidth, &ValueDial::changed, this, &BladeRF2InputGui::on_bandwidth_changed); + QObject::connect(ui->decim, QOverload::of(&QComboBox::currentIndexChanged), this, &BladeRF2InputGui::on_decim_currentIndexChanged); + QObject::connect(ui->fcPos, QOverload::of(&QComboBox::currentIndexChanged), this, &BladeRF2InputGui::on_fcPos_currentIndexChanged); + QObject::connect(ui->gainMode, QOverload::of(&QComboBox::currentIndexChanged), this, &BladeRF2InputGui::on_gainMode_currentIndexChanged); + QObject::connect(ui->gain, &QSlider::valueChanged, this, &BladeRF2InputGui::on_gain_valueChanged); + QObject::connect(ui->transverter, &TransverterButton::clicked, this, &BladeRF2InputGui::on_transverter_clicked); + QObject::connect(ui->startStop, &ButtonSwitch::toggled, this, &BladeRF2InputGui::on_startStop_toggled); + QObject::connect(ui->sampleRateMode, &QToolButton::toggled, this, &BladeRF2InputGui::on_sampleRateMode_toggled); +} diff --git a/plugins/samplesource/bladerf2input/bladerf2inputgui.h b/plugins/samplesource/bladerf2input/bladerf2inputgui.h index 98b24b8ed..08cb773a2 100644 --- a/plugins/samplesource/bladerf2input/bladerf2inputgui.h +++ b/plugins/samplesource/bladerf2input/bladerf2inputgui.h @@ -44,6 +44,10 @@ public: virtual QByteArray serialize() const; virtual bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } + virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; } + virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; } + virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; } + virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; } private: Ui::Bladerf2InputGui* ui; @@ -77,6 +81,7 @@ private: int getGainValue(float gainDB); void blockApplySettings(bool block); bool handleMessage(const Message& message); + void makeUIConnections(); private slots: void handleInputMessages(); diff --git a/plugins/samplesource/bladerf2input/bladerf2inputsettings.cpp b/plugins/samplesource/bladerf2input/bladerf2inputsettings.cpp index 17c975f62..f0de33bae 100644 --- a/plugins/samplesource/bladerf2input/bladerf2inputsettings.cpp +++ b/plugins/samplesource/bladerf2input/bladerf2inputsettings.cpp @@ -44,6 +44,7 @@ void BladeRF2InputSettings::resetToDefaults() m_reverseAPIAddress = "127.0.0.1"; m_reverseAPIPort = 8888; m_reverseAPIDeviceIndex = 0; + m_workspaceIndex = 0; } QByteArray BladeRF2InputSettings::serialize() const @@ -67,6 +68,8 @@ QByteArray BladeRF2InputSettings::serialize() const s.writeU32(15, m_reverseAPIPort); s.writeU32(16, m_reverseAPIDeviceIndex); s.writeBool(17, m_iqOrder); + s.writeS32(18, m_workspaceIndex); + s.writeBlob(19, m_geometryBytes); return s.final(); } @@ -112,6 +115,8 @@ bool BladeRF2InputSettings::deserialize(const QByteArray& data) d.readU32(16, &uintval, 0); m_reverseAPIDeviceIndex = uintval > 99 ? 99 : uintval; d.readBool(17, &m_iqOrder, true); + d.readS32(18, &m_workspaceIndex, 0); + d.readBlob(19, &m_geometryBytes); return true; } diff --git a/plugins/samplesource/bladerf2input/bladerf2inputsettings.h b/plugins/samplesource/bladerf2input/bladerf2inputsettings.h index 6fb1a2952..7059be36a 100644 --- a/plugins/samplesource/bladerf2input/bladerf2inputsettings.h +++ b/plugins/samplesource/bladerf2input/bladerf2inputsettings.h @@ -46,6 +46,8 @@ struct BladeRF2InputSettings { QString m_reverseAPIAddress; uint16_t m_reverseAPIPort; uint16_t m_reverseAPIDeviceIndex; + int m_workspaceIndex; + QByteArray m_geometryBytes; BladeRF2InputSettings(); void resetToDefaults(); diff --git a/plugins/samplesource/fcdpro/fcdprogui.cpp b/plugins/samplesource/fcdpro/fcdprogui.cpp index 64c1e18a9..c3fbe5877 100644 --- a/plugins/samplesource/fcdpro/fcdprogui.cpp +++ b/plugins/samplesource/fcdpro/fcdprogui.cpp @@ -41,9 +41,12 @@ FCDProGui::FCDProGui(DeviceUISet *deviceUISet, QWidget* parent) : m_sampleSource(NULL), m_lastEngineState(DeviceAPI::StNotStarted) { + setAttribute(Qt::WA_DeleteOnClose, true); m_sampleSource = (FCDProInput*) m_deviceUISet->m_deviceAPI->getSampleSource(); - ui->setupUi(this); + ui->setupUi(getContents()); + getContents()->setStyleSheet("#FCDProGui { border: 1px solid #C06900 }"); + m_helpURL = "plugins/samplesource/fcdpro/readme.md"; ui->centerFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold)); updateFrequencyLimits(); @@ -151,6 +154,7 @@ FCDProGui::FCDProGui(DeviceUISet *deviceUISet, QWidget* parent) : connect(startStopRightClickEnabler, SIGNAL(rightClick(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &))); displaySettings(); + makeUIConnections(); connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()), Qt::QueuedConnection); m_sampleSource->setMessageQueueToGUI(&m_inputMessageQueue); @@ -550,3 +554,26 @@ void FCDProGui::openDeviceSettingsDialog(const QPoint& p) sendSettings(); } + +void FCDProGui::makeUIConnections() +{ + QObject::connect(ui->centerFrequency, &ValueDial::changed, this, &FCDProGui::on_centerFrequency_changed); + QObject::connect(ui->ppm, &QSlider::valueChanged, this, &FCDProGui::on_ppm_valueChanged); + QObject::connect(ui->dcOffset, &ButtonSwitch::toggled, this, &FCDProGui::on_dcOffset_toggled); + QObject::connect(ui->iqImbalance, &ButtonSwitch::toggled, this, &FCDProGui::on_iqImbalance_toggled); + QObject::connect(ui->lnaGain, QOverload::of(&QComboBox::currentIndexChanged), this, &FCDProGui::on_lnaGain_currentIndexChanged); + QObject::connect(ui->rfFilter, QOverload::of(&QComboBox::currentIndexChanged), this, &FCDProGui::on_rfFilter_currentIndexChanged); + QObject::connect(ui->lnaEnhance, QOverload::of(&QComboBox::currentIndexChanged), this, &FCDProGui::on_lnaEnhance_currentIndexChanged); + QObject::connect(ui->band, QOverload::of(&QComboBox::currentIndexChanged), this, &FCDProGui::on_band_currentIndexChanged); + QObject::connect(ui->mixGain, QOverload::of(&QComboBox::currentIndexChanged), this, &FCDProGui::on_mixGain_currentIndexChanged); + QObject::connect(ui->mixFilter, QOverload::of(&QComboBox::currentIndexChanged), this, &FCDProGui::on_mixFilter_currentIndexChanged); + QObject::connect(ui->bias, QOverload::of(&QComboBox::currentIndexChanged), this, &FCDProGui::on_bias_currentIndexChanged); + QObject::connect(ui->mode, QOverload::of(&QComboBox::currentIndexChanged), this, &FCDProGui::on_mode_currentIndexChanged); + QObject::connect(ui->rcFilter, QOverload::of(&QComboBox::currentIndexChanged), this, &FCDProGui::on_rcFilter_currentIndexChanged); + QObject::connect(ui->ifFilter, QOverload::of(&QComboBox::currentIndexChanged), this, &FCDProGui::on_ifFilter_currentIndexChanged); + QObject::connect(ui->decim, QOverload::of(&QComboBox::currentIndexChanged), this, &FCDProGui::on_decim_currentIndexChanged); + QObject::connect(ui->fcPos, QOverload::of(&QComboBox::currentIndexChanged), this, &FCDProGui::on_fcPos_currentIndexChanged); + QObject::connect(ui->setDefaults, &QPushButton::clicked, this, &FCDProGui::on_setDefaults_clicked); + QObject::connect(ui->startStop, &ButtonSwitch::toggled, this, &FCDProGui::on_startStop_toggled); + QObject::connect(ui->transverter, &TransverterButton::clicked, this, &FCDProGui::on_transverter_clicked); +} diff --git a/plugins/samplesource/fcdpro/fcdprogui.h b/plugins/samplesource/fcdpro/fcdprogui.h index 304dc6273..50c7594fb 100644 --- a/plugins/samplesource/fcdpro/fcdprogui.h +++ b/plugins/samplesource/fcdpro/fcdprogui.h @@ -45,6 +45,10 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } + virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; } + virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; } + virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; } + virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; } private: Ui::FCDProGui* ui; @@ -68,6 +72,7 @@ private: void updateSampleRateAndFrequency(); void updateFrequencyLimits(); bool handleMessage(const Message& message); + void makeUIConnections(); private slots: void handleInputMessages(); diff --git a/plugins/samplesource/fcdpro/fcdprosettings.cpp b/plugins/samplesource/fcdpro/fcdprosettings.cpp index 075419d6b..3765dc3fb 100644 --- a/plugins/samplesource/fcdpro/fcdprosettings.cpp +++ b/plugins/samplesource/fcdpro/fcdprosettings.cpp @@ -55,6 +55,7 @@ void FCDProSettings::resetToDefaults() m_reverseAPIAddress = "127.0.0.1"; m_reverseAPIPort = 8888; m_reverseAPIDeviceIndex = 0; + m_workspaceIndex = 0; } QByteArray FCDProSettings::serialize() const @@ -89,6 +90,8 @@ QByteArray FCDProSettings::serialize() const s.writeU32(26, m_reverseAPIPort); s.writeU32(27, m_reverseAPIDeviceIndex); s.writeBool(28, m_iqOrder); + s.writeS32(29, m_workspaceIndex); + s.writeBlob(30, m_geometryBytes); return s.final(); } @@ -145,6 +148,8 @@ bool FCDProSettings::deserialize(const QByteArray& data) d.readU32(27, &uintval, 0); m_reverseAPIDeviceIndex = uintval > 99 ? 99 : uintval; d.readBool(28, &m_iqOrder, true); + d.readS32(29, &m_workspaceIndex, 0); + d.readBlob(30, &m_geometryBytes); return true; } diff --git a/plugins/samplesource/fcdpro/fcdprosettings.h b/plugins/samplesource/fcdpro/fcdprosettings.h index 03d126c7e..e00a1292a 100644 --- a/plugins/samplesource/fcdpro/fcdprosettings.h +++ b/plugins/samplesource/fcdpro/fcdprosettings.h @@ -56,6 +56,8 @@ struct FCDProSettings { QString m_reverseAPIAddress; uint16_t m_reverseAPIPort; uint16_t m_reverseAPIDeviceIndex; + int m_workspaceIndex; + QByteArray m_geometryBytes; FCDProSettings(); void resetToDefaults(); diff --git a/plugins/samplesource/fcdproplus/fcdproplusgui.cpp b/plugins/samplesource/fcdproplus/fcdproplusgui.cpp index deac61bfd..075eb4a05 100644 --- a/plugins/samplesource/fcdproplus/fcdproplusgui.cpp +++ b/plugins/samplesource/fcdproplus/fcdproplusgui.cpp @@ -42,9 +42,12 @@ FCDProPlusGui::FCDProPlusGui(DeviceUISet *deviceUISet, QWidget* parent) : m_sampleSource(NULL), m_lastEngineState(DeviceAPI::StNotStarted) { + setAttribute(Qt::WA_DeleteOnClose, true); m_sampleSource = (FCDProPlusInput*) m_deviceUISet->m_deviceAPI->getSampleSource(); - ui->setupUi(this); + ui->setupUi(getContents()); + getContents()->setStyleSheet("#FCDProPlusGui { border: 1px solid #C06900 }"); + m_helpURL = "plugins/samplesource/fcdproplus/readme.md"; ui->centerFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold)); updateFrequencyLimits(); @@ -69,6 +72,7 @@ FCDProPlusGui::FCDProPlusGui(DeviceUISet *deviceUISet, QWidget* parent) : connect(startStopRightClickEnabler, SIGNAL(rightClick(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &))); displaySettings(); + makeUIConnections(); connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()), Qt::QueuedConnection); m_sampleSource->setMessageQueueToGUI(&m_inputMessageQueue); @@ -372,3 +376,21 @@ void FCDProPlusGui::openDeviceSettingsDialog(const QPoint& p) sendSettings(); } + +void FCDProPlusGui::makeUIConnections() +{ + QObject::connect(ui->centerFrequency, &ValueDial::changed, this, &FCDProPlusGui::on_centerFrequency_changed); + QObject::connect(ui->decim, QOverload::of(&QComboBox::currentIndexChanged), this, &FCDProPlusGui::on_decim_currentIndexChanged); + QObject::connect(ui->fcPos, QOverload::of(&QComboBox::currentIndexChanged), this, &FCDProPlusGui::on_fcPos_currentIndexChanged); + QObject::connect(ui->dcOffset, &ButtonSwitch::toggled, this, &FCDProPlusGui::on_dcOffset_toggled); + QObject::connect(ui->iqImbalance, &ButtonSwitch::toggled, this, &FCDProPlusGui::on_iqImbalance_toggled); + QObject::connect(ui->checkBoxG, &QCheckBox::stateChanged, this, &FCDProPlusGui::on_checkBoxG_stateChanged); + QObject::connect(ui->checkBoxB, &QCheckBox::stateChanged, this, &FCDProPlusGui::on_checkBoxB_stateChanged); + QObject::connect(ui->mixGain, &QCheckBox::stateChanged, this, &FCDProPlusGui::on_mixGain_stateChanged); + QObject::connect(ui->ifGain, &QSlider::valueChanged, this, &FCDProPlusGui::on_ifGain_valueChanged); + QObject::connect(ui->filterRF, QOverload::of(&QComboBox::currentIndexChanged), this, &FCDProPlusGui::on_filterRF_currentIndexChanged); + QObject::connect(ui->filterIF, QOverload::of(&QComboBox::currentIndexChanged), this, &FCDProPlusGui::on_filterIF_currentIndexChanged); + QObject::connect(ui->ppm, &QSlider::valueChanged, this, &FCDProPlusGui::on_ppm_valueChanged); + QObject::connect(ui->startStop, &ButtonSwitch::toggled, this, &FCDProPlusGui::on_startStop_toggled); + QObject::connect(ui->transverter, &TransverterButton::clicked, this, &FCDProPlusGui::on_transverter_clicked); +} diff --git a/plugins/samplesource/fcdproplus/fcdproplusgui.h b/plugins/samplesource/fcdproplus/fcdproplusgui.h index 5a628f218..11955c69c 100644 --- a/plugins/samplesource/fcdproplus/fcdproplusgui.h +++ b/plugins/samplesource/fcdproplus/fcdproplusgui.h @@ -44,6 +44,10 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } + virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; } + virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; } + virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; } + virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; } private: Ui::FCDProPlusGui* ui; @@ -67,6 +71,7 @@ private: void updateSampleRateAndFrequency(); void updateFrequencyLimits(); bool handleMessage(const Message& message); + void makeUIConnections(); private slots: void handleInputMessages(); diff --git a/plugins/samplesource/fcdproplus/fcdproplussettings.cpp b/plugins/samplesource/fcdproplus/fcdproplussettings.cpp index aaa302934..53fd2be89 100644 --- a/plugins/samplesource/fcdproplus/fcdproplussettings.cpp +++ b/plugins/samplesource/fcdproplus/fcdproplussettings.cpp @@ -46,6 +46,7 @@ void FCDProPlusSettings::resetToDefaults() m_reverseAPIAddress = "127.0.0.1"; m_reverseAPIPort = 8888; m_reverseAPIDeviceIndex = 0; + m_workspaceIndex = 0; } QByteArray FCDProPlusSettings::serialize() const @@ -70,6 +71,8 @@ QByteArray FCDProPlusSettings::serialize() const s.writeU32(16, m_reverseAPIPort); s.writeU32(17, m_reverseAPIDeviceIndex); s.writeBool(18, m_iqOrder); + s.writeS32(19, m_workspaceIndex); + s.writeBlob(20, m_geometryBytes); return s.final(); } @@ -116,6 +119,8 @@ bool FCDProPlusSettings::deserialize(const QByteArray& data) d.readU32(17, &uintval, 0); m_reverseAPIDeviceIndex = uintval > 99 ? 99 : uintval; d.readBool(18, &m_iqOrder, true); + d.readS32(19, &m_workspaceIndex, 0); + d.readBlob(20, &m_geometryBytes); return true; } diff --git a/plugins/samplesource/fcdproplus/fcdproplussettings.h b/plugins/samplesource/fcdproplus/fcdproplussettings.h index ed5bf7401..5b9397805 100644 --- a/plugins/samplesource/fcdproplus/fcdproplussettings.h +++ b/plugins/samplesource/fcdproplus/fcdproplussettings.h @@ -47,6 +47,8 @@ struct FCDProPlusSettings { QString m_reverseAPIAddress; uint16_t m_reverseAPIPort; uint16_t m_reverseAPIDeviceIndex; + int m_workspaceIndex; + QByteArray m_geometryBytes; FCDProPlusSettings(); void resetToDefaults(); diff --git a/plugins/samplesource/fileinput/fileinputgui.cpp b/plugins/samplesource/fileinput/fileinputgui.cpp index 15ee71c2b..d4f7aee5a 100644 --- a/plugins/samplesource/fileinput/fileinputgui.cpp +++ b/plugins/samplesource/fileinput/fileinputgui.cpp @@ -55,7 +55,10 @@ FileInputGUI::FileInputGUI(DeviceUISet *deviceUISet, QWidget* parent) : m_enableNavTime(false), m_lastEngineState(DeviceAPI::StNotStarted) { - ui->setupUi(this); + setAttribute(Qt::WA_DeleteOnClose, true); + ui->setupUi(getContents()); + getContents()->setStyleSheet("#FileInputGUI { border: 1px solid #C06900 }"); + m_helpURL = "plugins/samplesource/fileinput/readme.md"; ui->crcLabel->setStyleSheet("QLabel { background:rgb(79,79,79); }"); connect(&(m_deviceUISet->m_deviceAPI->getMasterTimer()), SIGNAL(timeout()), this, SLOT(tick())); @@ -75,11 +78,15 @@ FileInputGUI::FileInputGUI(DeviceUISet *deviceUISet, QWidget* parent) : connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()), Qt::QueuedConnection); m_sampleSource->setMessageQueueToGUI(&m_inputMessageQueue); + + makeUIConnections(); } FileInputGUI::~FileInputGUI() { + qDebug("FileInputGUI::~FileInputGUI"); delete ui; + qDebug("FileInputGUI::~FileInputGUI: end"); } void FileInputGUI::destroy() @@ -444,3 +451,13 @@ void FileInputGUI::openDeviceSettingsDialog(const QPoint& p) sendSettings(); } + +void FileInputGUI::makeUIConnections() +{ + QObject::connect(ui->startStop, &ButtonSwitch::toggled, this, &FileInputGUI::on_startStop_toggled); + QObject::connect(ui->playLoop, &ButtonSwitch::toggled, this, &FileInputGUI::on_playLoop_toggled); + QObject::connect(ui->play, &ButtonSwitch::toggled, this, &FileInputGUI::on_play_toggled); + QObject::connect(ui->navTimeSlider, &QSlider::valueChanged, this, &FileInputGUI::on_navTimeSlider_valueChanged); + QObject::connect(ui->showFileDialog, &QPushButton::clicked, this, &FileInputGUI::on_showFileDialog_clicked); + QObject::connect(ui->acceleration, QOverload::of(&QComboBox::currentIndexChanged), this, &FileInputGUI::on_acceleration_currentIndexChanged); +} diff --git a/plugins/samplesource/fileinput/fileinputgui.h b/plugins/samplesource/fileinput/fileinputgui.h index 2b5dae4b9..6f6e7b01e 100644 --- a/plugins/samplesource/fileinput/fileinputgui.h +++ b/plugins/samplesource/fileinput/fileinputgui.h @@ -37,7 +37,7 @@ class FileInputGUI : public DeviceGUI { Q_OBJECT public: - explicit FileInputGUI(DeviceUISet *deviceUISet, QWidget* parent = 0); + explicit FileInputGUI(DeviceUISet *deviceUISet, QWidget* parent = nullptr); virtual ~FileInputGUI(); virtual void destroy(); @@ -46,6 +46,10 @@ public: bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } virtual bool handleMessage(const Message& message); + virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; } + virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; } + virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; } + virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; } private: Ui::FileInputGUI* ui; @@ -81,6 +85,7 @@ private: void updateWithStreamTime(); void setAccelerationCombo(); void setNumberStr(int n, QString& s); + void makeUIConnections(); private slots: void handleInputMessages(); diff --git a/plugins/samplesource/fileinput/fileinputgui.ui b/plugins/samplesource/fileinput/fileinputgui.ui index 6d9c9bbde..512b97571 100644 --- a/plugins/samplesource/fileinput/fileinputgui.ui +++ b/plugins/samplesource/fileinput/fileinputgui.ui @@ -6,8 +6,8 @@ 0 0 - 377 - 190 + 360 + 202 @@ -18,8 +18,8 @@ - 246 - 190 + 360 + 0 @@ -592,23 +592,6 @@ - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - diff --git a/plugins/samplesource/fileinput/fileinputsettings.cpp b/plugins/samplesource/fileinput/fileinputsettings.cpp index c63128502..0bbf959cc 100644 --- a/plugins/samplesource/fileinput/fileinputsettings.cpp +++ b/plugins/samplesource/fileinput/fileinputsettings.cpp @@ -36,6 +36,7 @@ void FileInputSettings::resetToDefaults() m_reverseAPIAddress = "127.0.0.1"; m_reverseAPIPort = 8888; m_reverseAPIDeviceIndex = 0; + m_workspaceIndex = 0; } QByteArray FileInputSettings::serialize() const @@ -48,6 +49,8 @@ QByteArray FileInputSettings::serialize() const s.writeString(5, m_reverseAPIAddress); s.writeU32(6, m_reverseAPIPort); s.writeU32(7, m_reverseAPIDeviceIndex); + s.writeS32(8, m_workspaceIndex); + s.writeBlob(9, m_geometryBytes); return s.final(); } @@ -80,6 +83,8 @@ bool FileInputSettings::deserialize(const QByteArray& data) d.readU32(7, &uintval, 0); m_reverseAPIDeviceIndex = uintval > 99 ? 99 : uintval; + d.readS32(8, &m_workspaceIndex, 0); + d.readBlob(9, &m_geometryBytes); return true; } diff --git a/plugins/samplesource/fileinput/fileinputsettings.h b/plugins/samplesource/fileinput/fileinputsettings.h index d007000e5..1d9bdaf33 100644 --- a/plugins/samplesource/fileinput/fileinputsettings.h +++ b/plugins/samplesource/fileinput/fileinputsettings.h @@ -29,6 +29,8 @@ struct FileInputSettings { QString m_reverseAPIAddress; uint16_t m_reverseAPIPort; uint16_t m_reverseAPIDeviceIndex; + int m_workspaceIndex; + QByteArray m_geometryBytes; static const unsigned int m_accelerationMaxScale; //!< Max power of 10 multiplier to 2,5,10 base ex: 2 -> 2,5,10,20,50,100,200,500,1000 diff --git a/plugins/samplesource/hackrfinput/hackrfinputgui.cpp b/plugins/samplesource/hackrfinput/hackrfinputgui.cpp index dda1e7139..9bab88194 100644 --- a/plugins/samplesource/hackrfinput/hackrfinputgui.cpp +++ b/plugins/samplesource/hackrfinput/hackrfinputgui.cpp @@ -46,9 +46,12 @@ HackRFInputGui::HackRFInputGui(DeviceUISet *deviceUISet, QWidget* parent) : m_sampleSource(NULL), m_lastEngineState(DeviceAPI::StNotStarted) { + setAttribute(Qt::WA_DeleteOnClose, true); m_sampleSource = (HackRFInput*) m_deviceUISet->m_deviceAPI->getSampleSource(); - ui->setupUi(this); + ui->setupUi(getContents()); + getContents()->setStyleSheet("#HackRFInputGui { border: 1px solid #C06900 }"); + m_helpURL = "plugins/samplesource/hackrfinput/readme.md"; ui->centerFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold)); ui->centerFrequency->setValueRange(7, 0U, 7250000U); @@ -69,6 +72,7 @@ HackRFInputGui::HackRFInputGui(DeviceUISet *deviceUISet, QWidget* parent) : m_sampleSource->setMessageQueueToGUI(&m_inputMessageQueue); sendSettings(); + makeUIConnections(); } HackRFInputGui::~HackRFInputGui() @@ -506,3 +510,23 @@ void HackRFInputGui::openDeviceSettingsDialog(const QPoint& p) sendSettings(); } + +void HackRFInputGui::makeUIConnections() +{ + QObject::connect(ui->centerFrequency, &ValueDial::changed, this, &HackRFInputGui::on_centerFrequency_changed); + QObject::connect(ui->sampleRate, &ValueDial::changed, this, &HackRFInputGui::on_sampleRate_changed); + QObject::connect(ui->LOppm, &QSlider::valueChanged, this, &HackRFInputGui::on_LOppm_valueChanged); + QObject::connect(ui->dcOffset, &ButtonSwitch::toggled, this, &HackRFInputGui::on_dcOffset_toggled); + QObject::connect(ui->iqImbalance, &ButtonSwitch::toggled, this, &HackRFInputGui::on_iqImbalance_toggled); + QObject::connect(ui->autoBBF, &ButtonSwitch::toggled, this, &HackRFInputGui::on_autoBBF_toggled); + QObject::connect(ui->biasT, &QCheckBox::stateChanged, this, &HackRFInputGui::on_biasT_stateChanged); + QObject::connect(ui->decim, QOverload::of(&QComboBox::currentIndexChanged), this, &HackRFInputGui::on_decim_currentIndexChanged); + QObject::connect(ui->fcPos, QOverload::of(&QComboBox::currentIndexChanged), this, &HackRFInputGui::on_fcPos_currentIndexChanged); + QObject::connect(ui->lnaExt, &QCheckBox::stateChanged, this, &HackRFInputGui::on_lnaExt_stateChanged); + QObject::connect(ui->lna, &QSlider::valueChanged, this, &HackRFInputGui::on_lna_valueChanged); + QObject::connect(ui->bbFilter, QOverload::of(&QComboBox::currentIndexChanged), this, &HackRFInputGui::on_bbFilter_currentIndexChanged); + QObject::connect(ui->vga, &QSlider::valueChanged, this, &HackRFInputGui::on_vga_valueChanged); + QObject::connect(ui->startStop, &ButtonSwitch::toggled, this, &HackRFInputGui::on_startStop_toggled); + QObject::connect(ui->sampleRateMode, &QToolButton::toggled, this, &HackRFInputGui::on_sampleRateMode_toggled); + QObject::connect(ui->transverter, &TransverterButton::clicked, this, &HackRFInputGui::on_transverter_clicked); +} diff --git a/plugins/samplesource/hackrfinput/hackrfinputgui.h b/plugins/samplesource/hackrfinput/hackrfinputgui.h index 758837920..51cac6209 100644 --- a/plugins/samplesource/hackrfinput/hackrfinputgui.h +++ b/plugins/samplesource/hackrfinput/hackrfinputgui.h @@ -54,6 +54,10 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } + virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; } + virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; } + virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; } + virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; } private: Ui::HackRFInputGui* ui; @@ -80,6 +84,7 @@ private: void updateFrequencyLimits(); void blockApplySettings(bool block); bool handleMessage(const Message& message); + void makeUIConnections(); private slots: void handleInputMessages(); diff --git a/plugins/samplesource/hackrfinput/hackrfinputsettings.cpp b/plugins/samplesource/hackrfinput/hackrfinputsettings.cpp index e7b14f65c..7c20b4454 100644 --- a/plugins/samplesource/hackrfinput/hackrfinputsettings.cpp +++ b/plugins/samplesource/hackrfinput/hackrfinputsettings.cpp @@ -48,6 +48,7 @@ void HackRFInputSettings::resetToDefaults() m_reverseAPIAddress = "127.0.0.1"; m_reverseAPIPort = 8888; m_reverseAPIDeviceIndex = 0; + m_workspaceIndex = 0; } QByteArray HackRFInputSettings::serialize() const @@ -73,6 +74,8 @@ QByteArray HackRFInputSettings::serialize() const s.writeS64(19, m_transverterDeltaFrequency); s.writeBool(20, m_iqOrder); s.writeBool(21, m_autoBBF); + s.writeS32(22, m_workspaceIndex); + s.writeBlob(23, m_geometryBytes); return s.final(); } @@ -120,6 +123,8 @@ bool HackRFInputSettings::deserialize(const QByteArray& data) d.readS64(19, &m_transverterDeltaFrequency, 0); d.readBool(20, &m_iqOrder, true); d.readBool(21, &m_autoBBF, true); + d.readS32(22, &m_workspaceIndex, 0); + d.readBlob(23, &m_geometryBytes); return true; } diff --git a/plugins/samplesource/hackrfinput/hackrfinputsettings.h b/plugins/samplesource/hackrfinput/hackrfinputsettings.h index de756e03e..36bad5903 100644 --- a/plugins/samplesource/hackrfinput/hackrfinputsettings.h +++ b/plugins/samplesource/hackrfinput/hackrfinputsettings.h @@ -48,6 +48,8 @@ struct HackRFInputSettings { QString m_reverseAPIAddress; uint16_t m_reverseAPIPort; uint16_t m_reverseAPIDeviceIndex; + int m_workspaceIndex; + QByteArray m_geometryBytes; HackRFInputSettings(); void resetToDefaults(); diff --git a/plugins/samplesource/kiwisdr/kiwisdrgui.cpp b/plugins/samplesource/kiwisdr/kiwisdrgui.cpp index 886d08851..dc339541a 100644 --- a/plugins/samplesource/kiwisdr/kiwisdrgui.cpp +++ b/plugins/samplesource/kiwisdr/kiwisdrgui.cpp @@ -52,6 +52,7 @@ KiwiSDRGui::KiwiSDRGui(DeviceUISet *deviceUISet, QWidget* parent) : m_lastEngineState(DeviceAPI::StNotStarted) { qDebug("KiwiSDRGui::KiwiSDRGui"); + setAttribute(Qt::WA_DeleteOnClose, true); m_sampleSource = m_deviceUISet->m_deviceAPI->getSampleSource(); m_statusTooltips.push_back("Idle"); // 0 @@ -66,11 +67,14 @@ KiwiSDRGui::KiwiSDRGui(DeviceUISet *deviceUISet, QWidget* parent) : m_statusColors.push_back("rgb(232, 85, 85)"); // Error (red) m_statusColors.push_back("rgb(232, 85, 232)"); // Disconnected (magenta) - ui->setupUi(this); + ui->setupUi(getContents()); + getContents()->setStyleSheet("#KiwiSDRGui { border: 1px solid #C06900 }"); + m_helpURL = "plugins/samplesource/kiwisdr/readme.md"; ui->centerFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold)); ui->centerFrequency->setValueRange(7, 0, 9999999); displaySettings(); + makeUIConnections(); connect(&m_updateTimer, SIGNAL(timeout()), this, SLOT(updateHardware())); connect(&m_statusTimer, SIGNAL(timeout()), this, SLOT(updateStatus())); @@ -313,3 +317,14 @@ void KiwiSDRGui::openDeviceSettingsDialog(const QPoint& p) sendSettings(); } + +void KiwiSDRGui::makeUIConnections() +{ + QObject::connect(ui->startStop, &ButtonSwitch::toggled, this, &KiwiSDRGui::on_startStop_toggled); + QObject::connect(ui->centerFrequency, &ValueDial::changed, this, &KiwiSDRGui::on_centerFrequency_changed); + QObject::connect(ui->gain, &QSlider::valueChanged, this, &KiwiSDRGui::on_gain_valueChanged); + QObject::connect(ui->agc, &QToolButton::toggled, this, &KiwiSDRGui::on_agc_toggled); + QObject::connect(ui->serverAddress, &QLineEdit::returnPressed, this, &KiwiSDRGui::on_serverAddress_returnPressed); + QObject::connect(ui->serverAddressApplyButton, &QPushButton::clicked, this, &KiwiSDRGui::on_serverAddressApplyButton_clicked); + QObject::connect(ui->dcBlock, &ButtonSwitch::toggled, this, &KiwiSDRGui::on_dcBlock_toggled); +} diff --git a/plugins/samplesource/kiwisdr/kiwisdrgui.h b/plugins/samplesource/kiwisdr/kiwisdrgui.h index f0b08d9b2..125fd6111 100644 --- a/plugins/samplesource/kiwisdr/kiwisdrgui.h +++ b/plugins/samplesource/kiwisdr/kiwisdrgui.h @@ -46,6 +46,10 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } + virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; } + virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; } + virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; } + virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; } private: Ui::KiwiSDRGui* ui; @@ -70,6 +74,7 @@ private: void sendSettings(); void updateSampleRateAndFrequency(); bool handleMessage(const Message& message); + void makeUIConnections(); private slots: void handleInputMessages(); diff --git a/plugins/samplesource/kiwisdr/kiwisdrsettings.cpp b/plugins/samplesource/kiwisdr/kiwisdrsettings.cpp index f894288c7..8dfbecd14 100644 --- a/plugins/samplesource/kiwisdr/kiwisdrsettings.cpp +++ b/plugins/samplesource/kiwisdr/kiwisdrsettings.cpp @@ -38,6 +38,8 @@ void KiwiSDRSettings::resetToDefaults() m_reverseAPIAddress = "127.0.0.1"; m_reverseAPIPort = 8888; m_reverseAPIDeviceIndex = 0; + + m_workspaceIndex = 0; } QByteArray KiwiSDRSettings::serialize() const @@ -47,6 +49,8 @@ QByteArray KiwiSDRSettings::serialize() const s.writeString(2, m_serverAddress); s.writeU32(3, m_gain); s.writeBool(4, m_useAGC); + s.writeS32(5, m_workspaceIndex); + s.writeBlob(6, m_geometryBytes); s.writeBool(100, m_useReverseAPI); s.writeString(101, m_reverseAPIAddress); @@ -73,6 +77,8 @@ bool KiwiSDRSettings::deserialize(const QByteArray& data) d.readString(2, &m_serverAddress, "127.0.0.1:8073"); d.readU32(3, &m_gain, 20); d.readBool(4, &m_useAGC, true); + d.readS32(5, &m_workspaceIndex, 0); + d.readBlob(6, &m_geometryBytes); d.readBool(100, &m_useReverseAPI, false); d.readString(101, &m_reverseAPIAddress, "127.0.0.1"); diff --git a/plugins/samplesource/kiwisdr/kiwisdrsettings.h b/plugins/samplesource/kiwisdr/kiwisdrsettings.h index 23b09f312..d084df434 100644 --- a/plugins/samplesource/kiwisdr/kiwisdrsettings.h +++ b/plugins/samplesource/kiwisdr/kiwisdrsettings.h @@ -35,6 +35,9 @@ struct KiwiSDRSettings { uint16_t m_reverseAPIPort; uint16_t m_reverseAPIDeviceIndex; + int m_workspaceIndex; + QByteArray m_geometryBytes; + KiwiSDRSettings(); void resetToDefaults(); QByteArray serialize() const; diff --git a/plugins/samplesource/limesdrinput/limesdrinputgui.cpp b/plugins/samplesource/limesdrinput/limesdrinputgui.cpp index fafdac5b8..1cdfcfe58 100644 --- a/plugins/samplesource/limesdrinput/limesdrinputgui.cpp +++ b/plugins/samplesource/limesdrinput/limesdrinputgui.cpp @@ -46,9 +46,12 @@ LimeSDRInputGUI::LimeSDRInputGUI(DeviceUISet *deviceUISet, QWidget* parent) : m_statusCounter(0), m_deviceStatusCounter(0) { + setAttribute(Qt::WA_DeleteOnClose, true); m_limeSDRInput = (LimeSDRInput*) m_deviceUISet->m_deviceAPI->getSampleSource(); - ui->setupUi(this); + ui->setupUi(getContents()); + getContents()->setStyleSheet("#LimeSDRInputGUI { border: 1px solid #C06900 }"); + m_helpURL = "plugins/samplesource/limesdrinput/readme.md"; float minF, maxF; @@ -93,6 +96,7 @@ LimeSDRInputGUI::LimeSDRInputGUI(DeviceUISet *deviceUISet, QWidget* parent) : m_statusTimer.start(500); displaySettings(); + makeUIConnections(); connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()), Qt::QueuedConnection); m_limeSDRInput->setMessageQueueToGUI(&m_inputMessageQueue); @@ -718,3 +722,28 @@ void LimeSDRInputGUI::openDeviceSettingsDialog(const QPoint& p) sendSettings(); } + +void LimeSDRInputGUI::makeUIConnections() +{ + QObject::connect(ui->startStop, &ButtonSwitch::toggled, this, &LimeSDRInputGUI::on_startStop_toggled); + QObject::connect(ui->centerFrequency, &ValueDial::changed, this, &LimeSDRInputGUI::on_centerFrequency_changed); + QObject::connect(ui->ncoFrequency, &ValueDialZ::changed, this, &LimeSDRInputGUI::on_ncoFrequency_changed); + QObject::connect(ui->ncoEnable, &ButtonSwitch::toggled, this, &LimeSDRInputGUI::on_ncoEnable_toggled); + QObject::connect(ui->dcOffset, &ButtonSwitch::toggled, this, &LimeSDRInputGUI::on_dcOffset_toggled); + QObject::connect(ui->iqImbalance, &ButtonSwitch::toggled, this, &LimeSDRInputGUI::on_iqImbalance_toggled); + QObject::connect(ui->sampleRate, &ValueDial::changed, this, &LimeSDRInputGUI::on_sampleRate_changed); + QObject::connect(ui->hwDecim, QOverload::of(&QComboBox::currentIndexChanged), this, &LimeSDRInputGUI::on_hwDecim_currentIndexChanged); + QObject::connect(ui->swDecim, QOverload::of(&QComboBox::currentIndexChanged), this, &LimeSDRInputGUI::on_swDecim_currentIndexChanged); + QObject::connect(ui->lpf, &ValueDial::changed, this, &LimeSDRInputGUI::on_lpf_changed); + QObject::connect(ui->lpFIREnable, &ButtonSwitch::toggled, this, &LimeSDRInputGUI::on_lpFIREnable_toggled); + QObject::connect(ui->lpFIR, &ValueDial::changed, this, &LimeSDRInputGUI::on_lpFIR_changed); + QObject::connect(ui->gainMode, QOverload::of(&QComboBox::currentIndexChanged), this, &LimeSDRInputGUI::on_gainMode_currentIndexChanged); + QObject::connect(ui->gain, &QDial::valueChanged, this, &LimeSDRInputGUI::on_gain_valueChanged); + QObject::connect(ui->lnaGain, &QDial::valueChanged, this, &LimeSDRInputGUI::on_lnaGain_valueChanged); + QObject::connect(ui->tiaGain, QOverload::of(&QComboBox::currentIndexChanged), this, &LimeSDRInputGUI::on_tiaGain_currentIndexChanged); + QObject::connect(ui->pgaGain, &QDial::valueChanged, this, &LimeSDRInputGUI::on_pgaGain_valueChanged); + QObject::connect(ui->antenna, QOverload::of(&QComboBox::currentIndexChanged), this, &LimeSDRInputGUI::on_antenna_currentIndexChanged); + QObject::connect(ui->extClock, &ExternalClockButton::clicked, this, &LimeSDRInputGUI::on_extClock_clicked); + QObject::connect(ui->transverter, &TransverterButton::clicked, this, &LimeSDRInputGUI::on_transverter_clicked); + QObject::connect(ui->sampleRateMode, &QToolButton::toggled, this, &LimeSDRInputGUI::on_sampleRateMode_toggled); +} diff --git a/plugins/samplesource/limesdrinput/limesdrinputgui.h b/plugins/samplesource/limesdrinput/limesdrinputgui.h index 894230e4b..e58cd27dc 100644 --- a/plugins/samplesource/limesdrinput/limesdrinputgui.h +++ b/plugins/samplesource/limesdrinput/limesdrinputgui.h @@ -44,6 +44,10 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } + virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; } + virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; } + virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; } + virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; } private: Ui::LimeSDRInputGUI* ui; @@ -74,6 +78,7 @@ private: void updateFrequencyLimits(); void blockApplySettings(bool block); bool handleMessage(const Message& message); + void makeUIConnections(); private slots: void handleInputMessages(); diff --git a/plugins/samplesource/limesdrinput/limesdrinputsettings.cpp b/plugins/samplesource/limesdrinput/limesdrinputsettings.cpp index da822b06c..ba08ac6c5 100644 --- a/plugins/samplesource/limesdrinput/limesdrinputsettings.cpp +++ b/plugins/samplesource/limesdrinput/limesdrinputsettings.cpp @@ -53,6 +53,7 @@ void LimeSDRInputSettings::resetToDefaults() m_reverseAPIAddress = "127.0.0.1"; m_reverseAPIPort = 8888; m_reverseAPIDeviceIndex = 0; + m_workspaceIndex = 0; } QByteArray LimeSDRInputSettings::serialize() const @@ -86,6 +87,9 @@ QByteArray LimeSDRInputSettings::serialize() const s.writeU32(26, m_reverseAPIPort); s.writeU32(27, m_reverseAPIDeviceIndex); s.writeBool(28, m_iqOrder); + s.writeS32(29, m_workspaceIndex); + s.writeBlob(30, m_geometryBytes); + return s.final(); } @@ -143,6 +147,8 @@ bool LimeSDRInputSettings::deserialize(const QByteArray& data) d.readU32(27, &uintval, 0); m_reverseAPIDeviceIndex = uintval > 99 ? 99 : uintval; d.readBool(28, &m_iqOrder, true); + d.readS32(29, &m_workspaceIndex, 0); + d.readBlob(20, &m_geometryBytes); return true; } diff --git a/plugins/samplesource/limesdrinput/limesdrinputsettings.h b/plugins/samplesource/limesdrinput/limesdrinputsettings.h index ef43bbadf..96f5fdae5 100644 --- a/plugins/samplesource/limesdrinput/limesdrinputsettings.h +++ b/plugins/samplesource/limesdrinput/limesdrinputsettings.h @@ -73,6 +73,8 @@ struct LimeSDRInputSettings QString m_reverseAPIAddress; uint16_t m_reverseAPIPort; uint16_t m_reverseAPIDeviceIndex; + int m_workspaceIndex; + QByteArray m_geometryBytes; LimeSDRInputSettings(); void resetToDefaults(); diff --git a/plugins/samplesource/localinput/localinputgui.cpp b/plugins/samplesource/localinput/localinputgui.cpp index 3221a3de9..dbfb078fb 100644 --- a/plugins/samplesource/localinput/localinputgui.cpp +++ b/plugins/samplesource/localinput/localinputgui.cpp @@ -70,16 +70,20 @@ LocalInputGui::LocalInputGui(DeviceUISet *deviceUISet, QWidget* parent) : m_doApplySettings(true), m_forceSettings(true) { + setAttribute(Qt::WA_DeleteOnClose, true); m_paletteGreenText.setColor(QPalette::WindowText, Qt::green); m_paletteWhiteText.setColor(QPalette::WindowText, Qt::white); m_startingTimeStampms = 0; - ui->setupUi(this); + ui->setupUi(getContents()); + getContents()->setStyleSheet("#LocalInputGui { border: 1px solid #C06900 }"); + m_helpURL = "plugins/samplesource/localinput/readme.md"; CRightClickEnabler *startStopRightClickEnabler = new CRightClickEnabler(ui->startStop); connect(startStopRightClickEnabler, SIGNAL(rightClick(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &))); displaySettings(); + makeUIConnections(); connect(&m_statusTimer, SIGNAL(timeout()), this, SLOT(updateStatus())); m_statusTimer.start(500); @@ -321,3 +325,10 @@ void LocalInputGui::openDeviceSettingsDialog(const QPoint& p) sendSettings(); } + +void LocalInputGui::makeUIConnections() +{ + QObject::connect(ui->dcOffset, &ButtonSwitch::toggled, this, &LocalInputGui::on_dcOffset_toggled); + QObject::connect(ui->iqImbalance, &ButtonSwitch::toggled, this, &LocalInputGui::on_iqImbalance_toggled); + QObject::connect(ui->startStop, &ButtonSwitch::toggled, this, &LocalInputGui::on_startStop_toggled); +} diff --git a/plugins/samplesource/localinput/localinputgui.h b/plugins/samplesource/localinput/localinputgui.h index 0f5472d3e..191b7bea8 100644 --- a/plugins/samplesource/localinput/localinputgui.h +++ b/plugins/samplesource/localinput/localinputgui.h @@ -46,6 +46,10 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } + virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; } + virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; } + virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; } + virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; } private: Ui::LocalInputGui* ui; @@ -100,6 +104,7 @@ private: void sendSettings(); void updateSampleRateAndFrequency(); bool handleMessage(const Message& message); + void makeUIConnections(); private slots: void handleInputMessages(); diff --git a/plugins/samplesource/localinput/localinputsettings.cpp b/plugins/samplesource/localinput/localinputsettings.cpp index 62782c894..7c48abbe3 100644 --- a/plugins/samplesource/localinput/localinputsettings.cpp +++ b/plugins/samplesource/localinput/localinputsettings.cpp @@ -31,6 +31,7 @@ void LocalInputSettings::resetToDefaults() m_reverseAPIAddress = "127.0.0.1"; m_reverseAPIPort = 8888; m_reverseAPIDeviceIndex = 0; + m_workspaceIndex = 0; } QByteArray LocalInputSettings::serialize() const @@ -43,6 +44,8 @@ QByteArray LocalInputSettings::serialize() const s.writeString(4, m_reverseAPIAddress); s.writeU32(5, m_reverseAPIPort); s.writeU32(6, m_reverseAPIDeviceIndex); + s.writeS32(7, m_workspaceIndex); + s.writeBlob(8, m_geometryBytes); return s.final(); } @@ -75,6 +78,9 @@ bool LocalInputSettings::deserialize(const QByteArray& data) d.readU32(6, &uintval, 0); m_reverseAPIDeviceIndex = uintval > 99 ? 99 : uintval; + d.readS32(7, &m_workspaceIndex, 0); + d.readBlob(8, &m_geometryBytes); + return true; } else diff --git a/plugins/samplesource/localinput/localinputsettings.h b/plugins/samplesource/localinput/localinputsettings.h index 42689d497..3f6beb9f0 100644 --- a/plugins/samplesource/localinput/localinputsettings.h +++ b/plugins/samplesource/localinput/localinputsettings.h @@ -28,6 +28,8 @@ struct LocalInputSettings { QString m_reverseAPIAddress; uint16_t m_reverseAPIPort; uint16_t m_reverseAPIDeviceIndex; + int m_workspaceIndex; + QByteArray m_geometryBytes; LocalInputSettings(); void resetToDefaults(); diff --git a/plugins/samplesource/perseus/perseusgui.cpp b/plugins/samplesource/perseus/perseusgui.cpp index 21cfb8759..5e1332f9a 100644 --- a/plugins/samplesource/perseus/perseusgui.cpp +++ b/plugins/samplesource/perseus/perseusgui.cpp @@ -41,9 +41,12 @@ PerseusGui::PerseusGui(DeviceUISet *deviceUISet, QWidget* parent) : m_sampleSource(0), m_lastEngineState(DeviceAPI::StNotStarted) { + setAttribute(Qt::WA_DeleteOnClose, true); m_sampleSource = (PerseusInput*) m_deviceUISet->m_deviceAPI->getSampleSource(); - ui->setupUi(this); + ui->setupUi(getContents()); + getContents()->setStyleSheet("#PerseusGui { border: 1px solid #C06900 }"); + m_helpURL = "plugins/samplesource/perseus/readme.md"; ui->centerFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold)); updateFrequencyLimits(); @@ -62,6 +65,7 @@ PerseusGui::PerseusGui(DeviceUISet *deviceUISet, QWidget* parent) : m_sampleSource->setMessageQueueToGUI(&m_inputMessageQueue); sendSettings(); + makeUIConnections(); } PerseusGui::~PerseusGui() @@ -390,3 +394,18 @@ void PerseusGui::openDeviceSettingsDialog(const QPoint& p) sendSettings(); } + +void PerseusGui::makeUIConnections() +{ + QObject::connect(ui->centerFrequency, &ValueDial::changed, this, &PerseusGui::on_centerFrequency_changed); + QObject::connect(ui->LOppm, &QSlider::valueChanged, this, &PerseusGui::on_LOppm_valueChanged); + QObject::connect(ui->resetLOppm, &QPushButton::clicked, this, &PerseusGui::on_resetLOppm_clicked); + QObject::connect(ui->sampleRate, QOverload::of(&QComboBox::currentIndexChanged), this, &PerseusGui::on_sampleRate_currentIndexChanged); + QObject::connect(ui->wideband, &ButtonSwitch::toggled, this, &PerseusGui::on_wideband_toggled); + QObject::connect(ui->decim, QOverload::of(&QComboBox::currentIndexChanged), this, &PerseusGui::on_decim_currentIndexChanged); + QObject::connect(ui->startStop, &ButtonSwitch::toggled, this, &PerseusGui::on_startStop_toggled); + QObject::connect(ui->transverter, &TransverterButton::clicked, this, &PerseusGui::on_transverter_clicked); + QObject::connect(ui->attenuator, QOverload::of(&QComboBox::currentIndexChanged), this, &PerseusGui::on_attenuator_currentIndexChanged); + QObject::connect(ui->adcDither, &ButtonSwitch::toggled, this, &PerseusGui::on_adcDither_toggled); + QObject::connect(ui->adcPreamp, &ButtonSwitch::toggled, this, &PerseusGui::on_adcPreamp_toggled); +} diff --git a/plugins/samplesource/perseus/perseusgui.h b/plugins/samplesource/perseus/perseusgui.h index 0e217ded6..38a8d1a5c 100644 --- a/plugins/samplesource/perseus/perseusgui.h +++ b/plugins/samplesource/perseus/perseusgui.h @@ -44,6 +44,11 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue* getInputMessageQueue() { return &m_inputMessageQueue; } + virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; } + virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; } + virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; } + virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; } + uint32_t getDevSampleRate(unsigned int index); int getDevSampleRateIndex(uint32_t sampleRate); @@ -70,6 +75,7 @@ private: void sendSettings(); void updateSampleRateAndFrequency(); bool handleMessage(const Message& message); + void makeUIConnections(); private slots: void on_centerFrequency_changed(quint64 value); diff --git a/plugins/samplesource/perseus/perseussettings.cpp b/plugins/samplesource/perseus/perseussettings.cpp index c75e08e00..37e8e34c2 100644 --- a/plugins/samplesource/perseus/perseussettings.cpp +++ b/plugins/samplesource/perseus/perseussettings.cpp @@ -41,6 +41,7 @@ void PerseusSettings::resetToDefaults() m_reverseAPIAddress = "127.0.0.1"; m_reverseAPIPort = 8888; m_reverseAPIDeviceIndex = 0; + m_workspaceIndex = 0; } QByteArray PerseusSettings::serialize() const @@ -61,6 +62,8 @@ QByteArray PerseusSettings::serialize() const s.writeU32(12, m_reverseAPIPort); s.writeU32(13, m_reverseAPIDeviceIndex); s.writeBool(14, m_iqOrder); + s.writeS32(15, m_workspaceIndex); + s.writeBlob(16, m_geometryBytes); return s.final(); } @@ -109,6 +112,8 @@ bool PerseusSettings::deserialize(const QByteArray& data) d.readU32(13, &uintval, 0); m_reverseAPIDeviceIndex = uintval > 99 ? 99 : uintval; d.readBool(14, &m_iqOrder, true); + d.readS32(15, &m_workspaceIndex, 0); + d.readBlob(16, &m_geometryBytes); return true; } diff --git a/plugins/samplesource/perseus/perseussettings.h b/plugins/samplesource/perseus/perseussettings.h index 6c28b142a..284c86395 100644 --- a/plugins/samplesource/perseus/perseussettings.h +++ b/plugins/samplesource/perseus/perseussettings.h @@ -47,6 +47,8 @@ struct PerseusSettings QString m_reverseAPIAddress; uint16_t m_reverseAPIPort; uint16_t m_reverseAPIDeviceIndex; + int m_workspaceIndex; + QByteArray m_geometryBytes; PerseusSettings(); void resetToDefaults(); diff --git a/plugins/samplesource/plutosdrinput/plutosdrinputgui.cpp b/plugins/samplesource/plutosdrinput/plutosdrinputgui.cpp index b57538799..70a037c63 100644 --- a/plugins/samplesource/plutosdrinput/plutosdrinputgui.cpp +++ b/plugins/samplesource/plutosdrinput/plutosdrinputgui.cpp @@ -46,9 +46,12 @@ PlutoSDRInputGui::PlutoSDRInputGui(DeviceUISet *deviceUISet, QWidget* parent) : m_doApplySettings(true), m_statusCounter(0) { + setAttribute(Qt::WA_DeleteOnClose, true); m_sampleSource = (PlutoSDRInput*) m_deviceUISet->m_deviceAPI->getSampleSource(); - ui->setupUi(this); + ui->setupUi(getContents()); + getContents()->setStyleSheet("#PlutoSDRInputGUI { border: 1px solid #C06900 }"); + m_helpURL = "plugins/samplesource/plutosdrinput/readme.md"; ui->centerFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold)); updateFrequencyLimits(); @@ -72,6 +75,7 @@ PlutoSDRInputGui::PlutoSDRInputGui(DeviceUISet *deviceUISet, QWidget* parent) : blockApplySettings(true); displaySettings(); + makeUIConnections(); blockApplySettings(false); connect(&m_updateTimer, SIGNAL(timeout()), this, SLOT(updateHardware())); @@ -565,3 +569,28 @@ void PlutoSDRInputGui::openDeviceSettingsDialog(const QPoint& p) sendSettings(); } + +void PlutoSDRInputGui::makeUIConnections() +{ + QObject::connect(ui->startStop, &ButtonSwitch::toggled, this, &PlutoSDRInputGui::on_startStop_toggled); + QObject::connect(ui->centerFrequency, &ValueDial::changed, this, &PlutoSDRInputGui::on_centerFrequency_changed); + QObject::connect(ui->loPPM, &QSlider::valueChanged, this, &PlutoSDRInputGui::on_loPPM_valueChanged); + QObject::connect(ui->dcOffset, &ButtonSwitch::toggled, this, &PlutoSDRInputGui::on_dcOffset_toggled); + QObject::connect(ui->rfDCOffset, &ButtonSwitch::toggled, this, &PlutoSDRInputGui::on_rfDCOffset_toggled); + QObject::connect(ui->bbDCOffset, &ButtonSwitch::toggled, this, &PlutoSDRInputGui::on_bbDCOffset_toggled); + QObject::connect(ui->hwIQImbalance, &ButtonSwitch::toggled, this, &PlutoSDRInputGui::on_hwIQImbalance_toggled); + QObject::connect(ui->iqImbalance, &ButtonSwitch::toggled, this, &PlutoSDRInputGui::on_iqImbalance_toggled); + QObject::connect(ui->swDecim, QOverload::of(&QComboBox::currentIndexChanged), this, &PlutoSDRInputGui::on_swDecim_currentIndexChanged); + QObject::connect(ui->fcPos, QOverload::of(&QComboBox::currentIndexChanged), this, &PlutoSDRInputGui::on_fcPos_currentIndexChanged); + QObject::connect(ui->sampleRate, &ValueDial::changed, this, &PlutoSDRInputGui::on_sampleRate_changed); + QObject::connect(ui->lpf, &ValueDial::changed, this, &PlutoSDRInputGui::on_lpf_changed); + QObject::connect(ui->lpFIREnable, &ButtonSwitch::toggled, this, &PlutoSDRInputGui::on_lpFIREnable_toggled); + QObject::connect(ui->lpFIR, &ValueDial::changed, this, &PlutoSDRInputGui::on_lpFIR_changed); + QObject::connect(ui->lpFIRDecimation, QOverload::of(&QComboBox::currentIndexChanged), this, &PlutoSDRInputGui::on_lpFIRDecimation_currentIndexChanged); + QObject::connect(ui->lpFIRGain, QOverload::of(&QComboBox::currentIndexChanged), this, &PlutoSDRInputGui::on_lpFIRGain_currentIndexChanged); + QObject::connect(ui->gainMode, QOverload::of(&QComboBox::currentIndexChanged), this, &PlutoSDRInputGui::on_gainMode_currentIndexChanged); + QObject::connect(ui->gain, &QDial::valueChanged, this, &PlutoSDRInputGui::on_gain_valueChanged); + QObject::connect(ui->antenna, QOverload::of(&QComboBox::currentIndexChanged), this, &PlutoSDRInputGui::on_antenna_currentIndexChanged); + QObject::connect(ui->transverter, &TransverterButton::clicked, this, &PlutoSDRInputGui::on_transverter_clicked); + QObject::connect(ui->sampleRateMode, &QToolButton::toggled, this, &PlutoSDRInputGui::on_sampleRateMode_toggled); +} diff --git a/plugins/samplesource/plutosdrinput/plutosdrinputgui.h b/plugins/samplesource/plutosdrinput/plutosdrinputgui.h index abd0f7e84..003c6d298 100644 --- a/plugins/samplesource/plutosdrinput/plutosdrinputgui.h +++ b/plugins/samplesource/plutosdrinput/plutosdrinputgui.h @@ -47,6 +47,10 @@ public: virtual QByteArray serialize() const; virtual bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } + virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; } + virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; } + virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; } + virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; } private: Ui::PlutoSDRInputGUI* ui; @@ -74,6 +78,7 @@ private: void setSampleRateLimits(); void updateFrequencyLimits(); bool handleMessage(const Message& message); + void makeUIConnections(); private slots: void on_startStop_toggled(bool checked); diff --git a/plugins/samplesource/plutosdrinput/plutosdrinputsettings.cpp b/plugins/samplesource/plutosdrinput/plutosdrinputsettings.cpp index 72cb5fae1..4d7c3ab9d 100644 --- a/plugins/samplesource/plutosdrinput/plutosdrinputsettings.cpp +++ b/plugins/samplesource/plutosdrinput/plutosdrinputsettings.cpp @@ -53,6 +53,7 @@ void PlutoSDRInputSettings::resetToDefaults() m_reverseAPIAddress = "127.0.0.1"; m_reverseAPIPort = 8888; m_reverseAPIDeviceIndex = 0; + m_workspaceIndex = 0; } QByteArray PlutoSDRInputSettings::serialize() const @@ -83,6 +84,8 @@ QByteArray PlutoSDRInputSettings::serialize() const s.writeBool(23, m_hwRFDCBlock); s.writeBool(24, m_hwIQCorrection); s.writeBool(25, m_iqOrder); + s.writeS32(26, m_workspaceIndex); + s.writeBlob(27, m_geometryBytes); return s.final(); } @@ -155,6 +158,8 @@ bool PlutoSDRInputSettings::deserialize(const QByteArray& data) d.readBool(23, &m_hwRFDCBlock, true); d.readBool(24, &m_hwIQCorrection, true); d.readBool(25, &m_iqOrder, true); + d.readS32(26, &m_workspaceIndex, 0); + d.readBlob(27, &m_geometryBytes); return true; } diff --git a/plugins/samplesource/plutosdrinput/plutosdrinputsettings.h b/plugins/samplesource/plutosdrinput/plutosdrinputsettings.h index 7dabb7310..0a3397534 100644 --- a/plugins/samplesource/plutosdrinput/plutosdrinputsettings.h +++ b/plugins/samplesource/plutosdrinput/plutosdrinputsettings.h @@ -83,6 +83,8 @@ struct PlutoSDRInputSettings { QString m_reverseAPIAddress; uint16_t m_reverseAPIPort; uint16_t m_reverseAPIDeviceIndex; + int m_workspaceIndex; + QByteArray m_geometryBytes; PlutoSDRInputSettings(); void resetToDefaults(); diff --git a/plugins/samplesource/remoteinput/remoteinputgui.cpp b/plugins/samplesource/remoteinput/remoteinputgui.cpp index 41c173d5b..6de87f3e4 100644 --- a/plugins/samplesource/remoteinput/remoteinputgui.cpp +++ b/plugins/samplesource/remoteinput/remoteinputgui.cpp @@ -67,11 +67,14 @@ RemoteInputGui::RemoteInputGui(DeviceUISet *deviceUISet, QWidget* parent) : m_doApplySettings(true), m_forceSettings(true) { + setAttribute(Qt::WA_DeleteOnClose, true); m_paletteGreenText.setColor(QPalette::WindowText, Qt::green); m_paletteWhiteText.setColor(QPalette::WindowText, Qt::white); m_startingTimeStampms = 0; - ui->setupUi(this); + ui->setupUi(getContents()); + getContents()->setStyleSheet("#RemoteInputGui { border: 1px solid #C06900 }"); + m_helpURL = "plugins/samplesource/remoteinput/readme.md"; ui->remoteDeviceFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold)); ui->remoteDeviceFrequency->setValueRange(8, 0, 99999999); @@ -97,6 +100,7 @@ RemoteInputGui::RemoteInputGui(DeviceUISet *deviceUISet, QWidget* parent) : m_forceSettings = true; sendSettings(); + makeUIConnections(); } RemoteInputGui::~RemoteInputGui() @@ -684,3 +688,22 @@ void RemoteInputGui::openDeviceSettingsDialog(const QPoint& p) sendSettings(); } + +void RemoteInputGui::makeUIConnections() +{ + QObject::connect(ui->remoteDeviceFrequency, &ValueDial::changed, this, &RemoteInputGui::on_remoteDeviceFrequency_changed); + QObject::connect(ui->decimationFactor, QOverload::of(&QComboBox::currentIndexChanged), this, &RemoteInputGui::on_decimationFactor_currentIndexChanged); + QObject::connect(ui->position, &QSlider::valueChanged, this, &RemoteInputGui::on_position_valueChanged); + QObject::connect(ui->apiApplyButton, &QPushButton::clicked, this, &RemoteInputGui::on_apiApplyButton_clicked); + QObject::connect(ui->dataApplyButton, &QPushButton::clicked, this, &RemoteInputGui::on_dataApplyButton_clicked); + QObject::connect(ui->dcOffset, &ButtonSwitch::toggled, this, &RemoteInputGui::on_dcOffset_toggled); + QObject::connect(ui->iqImbalance, &ButtonSwitch::toggled, this, &RemoteInputGui::on_iqImbalance_toggled); + QObject::connect(ui->apiAddress, &QLineEdit::editingFinished, this, &RemoteInputGui::on_apiAddress_editingFinished); + QObject::connect(ui->apiPort, &QLineEdit::editingFinished, this, &RemoteInputGui::on_apiPort_editingFinished); + QObject::connect(ui->dataAddress, &QLineEdit::editingFinished, this, &RemoteInputGui::on_dataAddress_editingFinished); + QObject::connect(ui->dataPort, &QLineEdit::editingFinished, this, &RemoteInputGui::on_dataPort_editingFinished); + QObject::connect(ui->multicastAddress, &QLineEdit::editingFinished, this, &RemoteInputGui::on_multicastAddress_editingFinished); + QObject::connect(ui->multicastJoin, &ButtonSwitch::toggled, this, &RemoteInputGui::on_multicastJoin_toggled); + QObject::connect(ui->startStop, &ButtonSwitch::toggled, this, &RemoteInputGui::on_startStop_toggled); + QObject::connect(ui->eventCountsReset, &QPushButton::clicked, this, &RemoteInputGui::on_eventCountsReset_clicked); +} diff --git a/plugins/samplesource/remoteinput/remoteinputgui.h b/plugins/samplesource/remoteinput/remoteinputgui.h index 1aaa91f14..6de55ffc2 100644 --- a/plugins/samplesource/remoteinput/remoteinputgui.h +++ b/plugins/samplesource/remoteinput/remoteinputgui.h @@ -48,6 +48,10 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } + virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; } + virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; } + virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; } + virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; } private: Ui::RemoteInputGui* ui; @@ -116,6 +120,7 @@ private: void applyPosition(); void applyRemoteSettings(); bool handleMessage(const Message& message); + void makeUIConnections(); private slots: void handleInputMessages(); diff --git a/plugins/samplesource/remoteinput/remoteinputsettings.cpp b/plugins/samplesource/remoteinput/remoteinputsettings.cpp index a731506f6..97ede685d 100644 --- a/plugins/samplesource/remoteinput/remoteinputsettings.cpp +++ b/plugins/samplesource/remoteinput/remoteinputsettings.cpp @@ -37,6 +37,7 @@ void RemoteInputSettings::resetToDefaults() m_reverseAPIAddress = "127.0.0.1"; m_reverseAPIPort = 8888; m_reverseAPIDeviceIndex = 0; + m_workspaceIndex = 0; } QByteArray RemoteInputSettings::serialize() const @@ -55,6 +56,8 @@ QByteArray RemoteInputSettings::serialize() const s.writeString(12, m_reverseAPIAddress); s.writeU32(13, m_reverseAPIPort); s.writeU32(14, m_reverseAPIDeviceIndex); + s.writeS32(15, m_workspaceIndex); + s.writeBlob(16, m_geometryBytes); return s.final(); } @@ -95,6 +98,9 @@ bool RemoteInputSettings::deserialize(const QByteArray& data) d.readU32(14, &uintval, 0); m_reverseAPIDeviceIndex = uintval > 99 ? 99 : uintval; + d.readS32(15, &m_workspaceIndex, 0); + d.readBlob(16, &m_geometryBytes); + return true; } else diff --git a/plugins/samplesource/remoteinput/remoteinputsettings.h b/plugins/samplesource/remoteinput/remoteinputsettings.h index 60cec764d..bb544070e 100644 --- a/plugins/samplesource/remoteinput/remoteinputsettings.h +++ b/plugins/samplesource/remoteinput/remoteinputsettings.h @@ -34,6 +34,8 @@ struct RemoteInputSettings { QString m_reverseAPIAddress; uint16_t m_reverseAPIPort; uint16_t m_reverseAPIDeviceIndex; + int m_workspaceIndex; + QByteArray m_geometryBytes; RemoteInputSettings(); void resetToDefaults(); diff --git a/plugins/samplesource/rtlsdr/rtlsdrgui.cpp b/plugins/samplesource/rtlsdr/rtlsdrgui.cpp index 228bbf3b7..36c7edee0 100644 --- a/plugins/samplesource/rtlsdr/rtlsdrgui.cpp +++ b/plugins/samplesource/rtlsdr/rtlsdrgui.cpp @@ -43,9 +43,12 @@ RTLSDRGui::RTLSDRGui(DeviceUISet *deviceUISet, QWidget* parent) : m_sampleSource(0), m_lastEngineState(DeviceAPI::StNotStarted) { + setAttribute(Qt::WA_DeleteOnClose, true); m_sampleSource = (RTLSDRInput*) m_deviceUISet->m_deviceAPI->getSampleSource(); - ui->setupUi(this); + ui->setupUi(getContents()); + getContents()->setStyleSheet("#RTLSDRGui { border: 1px solid #C06900 }"); + m_helpURL = "plugins/samplesource/rtlsdr/readme.md"; ui->centerFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold)); updateFrequencyLimits(); @@ -59,6 +62,7 @@ RTLSDRGui::RTLSDRGui(DeviceUISet *deviceUISet, QWidget* parent) : m_statusTimer.start(500); displaySettings(); + makeUIConnections(); m_gains = m_sampleSource->getGains(); displayGains(); @@ -533,3 +537,24 @@ void RTLSDRGui::openDeviceSettingsDialog(const QPoint& p) sendSettings(); } + +void RTLSDRGui::makeUIConnections() +{ + QObject::connect(ui->centerFrequency, &ValueDial::changed, this, &RTLSDRGui::on_centerFrequency_changed); + QObject::connect(ui->sampleRate, &ValueDial::changed, this, &RTLSDRGui::on_sampleRate_changed); + QObject::connect(ui->offsetTuning, &QCheckBox::toggled, this, &RTLSDRGui::on_offsetTuning_toggled); + QObject::connect(ui->rfBW, &ValueDial::changed, this, &RTLSDRGui::on_rfBW_changed); + QObject::connect(ui->lowSampleRate, &ButtonSwitch::toggled, this, &RTLSDRGui::on_lowSampleRate_toggled); + QObject::connect(ui->dcOffset, &ButtonSwitch::toggled, this, &RTLSDRGui::on_dcOffset_toggled); + QObject::connect(ui->iqImbalance, &ButtonSwitch::toggled, this, &RTLSDRGui::on_iqImbalance_toggled); + QObject::connect(ui->decim, QOverload::of(&QComboBox::currentIndexChanged), this, &RTLSDRGui::on_decim_currentIndexChanged); + QObject::connect(ui->fcPos, QOverload::of(&QComboBox::currentIndexChanged), this, &RTLSDRGui::on_fcPos_currentIndexChanged); + QObject::connect(ui->ppm, &QSlider::valueChanged, this, &RTLSDRGui::on_ppm_valueChanged); + QObject::connect(ui->gain, &QSlider::valueChanged, this, &RTLSDRGui::on_gain_valueChanged); + QObject::connect(ui->checkBox, &QCheckBox::stateChanged, this, &RTLSDRGui::on_checkBox_stateChanged); + QObject::connect(ui->agc, &QCheckBox::stateChanged, this, &RTLSDRGui::on_agc_stateChanged); + QObject::connect(ui->startStop, &ButtonSwitch::toggled, this, &RTLSDRGui::on_startStop_toggled); + QObject::connect(ui->transverter, &TransverterButton::clicked, this, &RTLSDRGui::on_transverter_clicked); + QObject::connect(ui->sampleRateMode, &QToolButton::toggled, this, &RTLSDRGui::on_sampleRateMode_toggled); + QObject::connect(ui->biasT, &QCheckBox::stateChanged, this, &RTLSDRGui::on_biasT_stateChanged); +} diff --git a/plugins/samplesource/rtlsdr/rtlsdrgui.h b/plugins/samplesource/rtlsdr/rtlsdrgui.h index 9c09d10e3..a6c3f0e98 100644 --- a/plugins/samplesource/rtlsdr/rtlsdrgui.h +++ b/plugins/samplesource/rtlsdr/rtlsdrgui.h @@ -46,6 +46,10 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } + virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; } + virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; } + virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; } + virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; } private: Ui::RTLSDRGui* ui; @@ -73,6 +77,7 @@ private: void updateFrequencyLimits(); void blockApplySettings(bool block); bool handleMessage(const Message& message); + void makeUIConnections(); private slots: void handleInputMessages(); diff --git a/plugins/samplesource/rtlsdr/rtlsdrgui.ui b/plugins/samplesource/rtlsdr/rtlsdrgui.ui index bdbe8b255..d4a6d75af 100644 --- a/plugins/samplesource/rtlsdr/rtlsdrgui.ui +++ b/plugins/samplesource/rtlsdr/rtlsdrgui.ui @@ -6,8 +6,8 @@ 0 0 - 320 - 220 + 360 + 229 @@ -18,8 +18,8 @@ - 320 - 220 + 360 + 0 @@ -251,6 +251,12 @@ + + + 55 + 0 + + 50 @@ -636,44 +642,20 @@ - - - - Qt::Horizontal - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - ButtonSwitch - QToolButton -
gui/buttonswitch.h
-
ValueDial QWidget
gui/valuedial.h
1
+ + ButtonSwitch + QToolButton +
gui/buttonswitch.h
+
TransverterButton QPushButton diff --git a/plugins/samplesource/rtlsdr/rtlsdrsettings.cpp b/plugins/samplesource/rtlsdr/rtlsdrsettings.cpp index ace5e0f89..c8326fdf9 100644 --- a/plugins/samplesource/rtlsdr/rtlsdrsettings.cpp +++ b/plugins/samplesource/rtlsdr/rtlsdrsettings.cpp @@ -47,6 +47,7 @@ void RTLSDRSettings::resetToDefaults() m_reverseAPIAddress = "127.0.0.1"; m_reverseAPIPort = 8888; m_reverseAPIDeviceIndex = 0; + m_workspaceIndex = 0; } QByteArray RTLSDRSettings::serialize() const @@ -73,6 +74,8 @@ QByteArray RTLSDRSettings::serialize() const s.writeU32(19, m_reverseAPIDeviceIndex); s.writeBool(20, m_iqOrder); s.writeBool(21, m_biasTee); + s.writeS32(22, m_workspaceIndex); + s.writeBlob(23, m_geometryBytes); return s.final(); } @@ -121,6 +124,8 @@ bool RTLSDRSettings::deserialize(const QByteArray& data) m_reverseAPIDeviceIndex = utmp > 99 ? 99 : utmp; d.readBool(20, &m_iqOrder, true); d.readBool(21, &m_biasTee, false); + d.readS32(22, &m_workspaceIndex, 0); + d.readBlob(23, &m_geometryBytes); return true; } diff --git a/plugins/samplesource/rtlsdr/rtlsdrsettings.h b/plugins/samplesource/rtlsdr/rtlsdrsettings.h index a80649339..2d9913816 100644 --- a/plugins/samplesource/rtlsdr/rtlsdrsettings.h +++ b/plugins/samplesource/rtlsdr/rtlsdrsettings.h @@ -48,6 +48,8 @@ struct RTLSDRSettings { QString m_reverseAPIAddress; uint16_t m_reverseAPIPort; uint16_t m_reverseAPIDeviceIndex; + int m_workspaceIndex; + QByteArray m_geometryBytes; RTLSDRSettings(); void resetToDefaults(); diff --git a/plugins/samplesource/sdrplay/sdrplaygui.cpp b/plugins/samplesource/sdrplay/sdrplaygui.cpp index a214a0b54..bba28d7e0 100644 --- a/plugins/samplesource/sdrplay/sdrplaygui.cpp +++ b/plugins/samplesource/sdrplay/sdrplaygui.cpp @@ -40,9 +40,12 @@ SDRPlayGui::SDRPlayGui(DeviceUISet *deviceUISet, QWidget* parent) : m_doApplySettings(true), m_forceSettings(true) { + setAttribute(Qt::WA_DeleteOnClose, true); m_sampleSource = (SDRPlayInput*) m_deviceUISet->m_deviceAPI->getSampleSource(); - ui->setupUi(this); + ui->setupUi(getContents()); + getContents()->setStyleSheet("#SDRPlayGui { border: 1px solid #C06900 }"); + m_helpURL = "plugins/samplesource/sdrplay/readme.md"; ui->centerFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold)); ui->centerFrequency->setValueRange(7, 10U, 12000U); @@ -78,6 +81,7 @@ SDRPlayGui::SDRPlayGui(DeviceUISet *deviceUISet, QWidget* parent) : connect(startStopRightClickEnabler, SIGNAL(rightClick(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &))); displaySettings(); + makeUIConnections(); connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()), Qt::QueuedConnection); m_sampleSource->setMessageQueueToGUI(&m_inputMessageQueue); @@ -456,3 +460,24 @@ void SDRPlayGui::openDeviceSettingsDialog(const QPoint& p) sendSettings(); } + +void SDRPlayGui::makeUIConnections() +{ + QObject::connect(ui->centerFrequency, &ValueDial::changed, this, &SDRPlayGui::on_centerFrequency_changed); + QObject::connect(ui->ppm, &QSlider::valueChanged, this, &SDRPlayGui::on_ppm_valueChanged); + QObject::connect(ui->dcOffset, &ButtonSwitch::toggled, this, &SDRPlayGui::on_dcOffset_toggled); + QObject::connect(ui->iqImbalance, &ButtonSwitch::toggled, this, &SDRPlayGui::on_iqImbalance_toggled); + QObject::connect(ui->fBand, QOverload::of(&QComboBox::currentIndexChanged), this, &SDRPlayGui::on_fBand_currentIndexChanged); + QObject::connect(ui->bandwidth, QOverload::of(&QComboBox::currentIndexChanged), this, &SDRPlayGui::on_bandwidth_currentIndexChanged); + QObject::connect(ui->samplerate, QOverload::of(&QComboBox::currentIndexChanged), this, &SDRPlayGui::on_samplerate_currentIndexChanged); + QObject::connect(ui->ifFrequency, QOverload::of(&QComboBox::currentIndexChanged), this, &SDRPlayGui::on_ifFrequency_currentIndexChanged); + QObject::connect(ui->decim, QOverload::of(&QComboBox::currentIndexChanged), this, &SDRPlayGui::on_decim_currentIndexChanged); + QObject::connect(ui->fcPos, QOverload::of(&QComboBox::currentIndexChanged), this, &SDRPlayGui::on_fcPos_currentIndexChanged); + QObject::connect(ui->gainTunerOn, &QRadioButton::toggled, this, &SDRPlayGui::on_gainTunerOn_toggled); + QObject::connect(ui->gainTuner, &QDial::valueChanged, this, &SDRPlayGui::on_gainTuner_valueChanged); + QObject::connect(ui->gainManualOn, &QRadioButton::toggled, this, &SDRPlayGui::on_gainManualOn_toggled); + QObject::connect(ui->gainLNA, &ButtonSwitch::toggled, this, &SDRPlayGui::on_gainLNA_toggled); + QObject::connect(ui->gainMixer, &ButtonSwitch::toggled, this, &SDRPlayGui::on_gainMixer_toggled); + QObject::connect(ui->gainBaseband, &QDial::valueChanged, this, &SDRPlayGui::on_gainBaseband_valueChanged); + QObject::connect(ui->startStop, &ButtonSwitch::toggled, this, &SDRPlayGui::on_startStop_toggled); +} diff --git a/plugins/samplesource/sdrplay/sdrplaygui.h b/plugins/samplesource/sdrplay/sdrplaygui.h index 0e0595f04..31e783831 100644 --- a/plugins/samplesource/sdrplay/sdrplaygui.h +++ b/plugins/samplesource/sdrplay/sdrplaygui.h @@ -46,6 +46,10 @@ public: virtual QByteArray serialize() const; virtual bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } + virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; } + virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; } + virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; } + virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; } private: Ui::SDRPlayGui* ui; @@ -67,6 +71,7 @@ private: void sendSettings(); void updateSampleRateAndFrequency(); bool handleMessage(const Message& message); + void makeUIConnections(); private slots: void updateHardware(); diff --git a/plugins/samplesource/sdrplay/sdrplaysettings.cpp b/plugins/samplesource/sdrplay/sdrplaysettings.cpp index 4828bb086..626ab4ec0 100644 --- a/plugins/samplesource/sdrplay/sdrplaysettings.cpp +++ b/plugins/samplesource/sdrplay/sdrplaysettings.cpp @@ -47,6 +47,7 @@ void SDRPlaySettings::resetToDefaults() m_reverseAPIAddress = "127.0.0.1"; m_reverseAPIPort = 8888; m_reverseAPIDeviceIndex = 0; + m_workspaceIndex = 0; } QByteArray SDRPlaySettings::serialize() const @@ -72,6 +73,8 @@ QByteArray SDRPlaySettings::serialize() const s.writeU32(17, m_reverseAPIPort); s.writeU32(18, m_reverseAPIDeviceIndex); s.writeBool(19, m_iqOrder); + s.writeS32(20, m_workspaceIndex); + s.writeBlob(21, m_geometryBytes); return s.final(); } @@ -119,6 +122,8 @@ bool SDRPlaySettings::deserialize(const QByteArray& data) d.readU32(18, &uintval, 0); m_reverseAPIDeviceIndex = uintval > 99 ? 99 : uintval; d.readBool(19, &m_iqOrder, true); + d.readS32(20, &m_workspaceIndex, 0); + d.readBlob(21, &m_geometryBytes); return true; } diff --git a/plugins/samplesource/sdrplay/sdrplaysettings.h b/plugins/samplesource/sdrplay/sdrplaysettings.h index 7dbe2765c..1242b532a 100644 --- a/plugins/samplesource/sdrplay/sdrplaysettings.h +++ b/plugins/samplesource/sdrplay/sdrplaysettings.h @@ -50,6 +50,8 @@ struct SDRPlaySettings { QString m_reverseAPIAddress; uint16_t m_reverseAPIPort; uint16_t m_reverseAPIDeviceIndex; + int m_workspaceIndex; + QByteArray m_geometryBytes; SDRPlaySettings(); void resetToDefaults(); diff --git a/plugins/samplesource/sdrplayv3/sdrplayv3gui.cpp b/plugins/samplesource/sdrplayv3/sdrplayv3gui.cpp index 7b78455b4..56344262e 100644 --- a/plugins/samplesource/sdrplayv3/sdrplayv3gui.cpp +++ b/plugins/samplesource/sdrplayv3/sdrplayv3gui.cpp @@ -40,9 +40,12 @@ SDRPlayV3Gui::SDRPlayV3Gui(DeviceUISet *deviceUISet, QWidget* parent) : m_doApplySettings(true), m_forceSettings(true) { + setAttribute(Qt::WA_DeleteOnClose, true); m_sdrPlayV3Input = (SDRPlayV3Input*) m_deviceUISet->m_deviceAPI->getSampleSource(); - ui->setupUi(this); + ui->setupUi(getContents()); + getContents()->setStyleSheet("#SDRPlayV3Gui { border: 1px solid #C06900 }"); + m_helpURL = "plugins/samplesource/sdrplayv3/readme.md"; ui->centerFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold)); updateFrequencyLimits(); @@ -114,6 +117,7 @@ SDRPlayV3Gui::SDRPlayV3Gui(DeviceUISet *deviceUISet, QWidget* parent) : ui->antenna->blockSignals(false); displaySettings(); + makeUIConnections(); connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()), Qt::QueuedConnection); m_sdrPlayV3Input->setMessageQueueToGUI(&m_inputMessageQueue); @@ -520,3 +524,28 @@ void SDRPlayV3Gui::openDeviceSettingsDialog(const QPoint& p) sendSettings(); } + +void SDRPlayV3Gui::makeUIConnections() +{ + QObject::connect(ui->centerFrequency, &ValueDial::changed, this, &SDRPlayV3Gui::on_centerFrequency_changed); + QObject::connect(ui->ppm, &QSlider::valueChanged, this, &SDRPlayV3Gui::on_ppm_valueChanged); + QObject::connect(ui->tuner, QOverload::of(&QComboBox::currentIndexChanged), this, &SDRPlayV3Gui::on_tuner_currentIndexChanged); + QObject::connect(ui->antenna, QOverload::of(&QComboBox::currentIndexChanged), this, &SDRPlayV3Gui::on_antenna_currentIndexChanged); + QObject::connect(ui->dcOffset, &ButtonSwitch::toggled, this, &SDRPlayV3Gui::on_dcOffset_toggled); + QObject::connect(ui->iqImbalance, &ButtonSwitch::toggled, this, &SDRPlayV3Gui::on_iqImbalance_toggled); + QObject::connect(ui->extRef, &ButtonSwitch::toggled, this, &SDRPlayV3Gui::on_extRef_toggled); + QObject::connect(ui->biasTee, &ButtonSwitch::toggled, this, &SDRPlayV3Gui::on_biasTee_toggled); + QObject::connect(ui->amNotch, &ButtonSwitch::toggled, this, &SDRPlayV3Gui::on_amNotch_toggled); + QObject::connect(ui->fmNotch, &ButtonSwitch::toggled, this, &SDRPlayV3Gui::on_fmNotch_toggled); + QObject::connect(ui->dabNotch, &ButtonSwitch::toggled, this, &SDRPlayV3Gui::on_dabNotch_toggled); + QObject::connect(ui->bandwidth, QOverload::of(&QComboBox::currentIndexChanged), this, &SDRPlayV3Gui::on_bandwidth_currentIndexChanged); + QObject::connect(ui->samplerate, &ValueDial::changed, this, &SDRPlayV3Gui::on_samplerate_changed); + QObject::connect(ui->ifFrequency, QOverload::of(&QComboBox::currentIndexChanged), this, &SDRPlayV3Gui::on_ifFrequency_currentIndexChanged); + QObject::connect(ui->decim, QOverload::of(&QComboBox::currentIndexChanged), this, &SDRPlayV3Gui::on_decim_currentIndexChanged); + QObject::connect(ui->fcPos, QOverload::of(&QComboBox::currentIndexChanged), this, &SDRPlayV3Gui::on_fcPos_currentIndexChanged); + QObject::connect(ui->gainLNA, QOverload::of(&QComboBox::currentIndexChanged), this, &SDRPlayV3Gui::on_gainLNA_currentIndexChanged); + QObject::connect(ui->gainIFAGC, &ButtonSwitch::toggled, this, &SDRPlayV3Gui::on_gainIFAGC_toggled); + QObject::connect(ui->gainIF, &QDial::valueChanged, this, &SDRPlayV3Gui::on_gainIF_valueChanged); + QObject::connect(ui->startStop, &ButtonSwitch::toggled, this, &SDRPlayV3Gui::on_startStop_toggled); + QObject::connect(ui->transverter, &TransverterButton::clicked, this, &SDRPlayV3Gui::on_transverter_clicked); +} diff --git a/plugins/samplesource/sdrplayv3/sdrplayv3gui.h b/plugins/samplesource/sdrplayv3/sdrplayv3gui.h index 0c790c961..b3fb7df14 100644 --- a/plugins/samplesource/sdrplayv3/sdrplayv3gui.h +++ b/plugins/samplesource/sdrplayv3/sdrplayv3gui.h @@ -46,6 +46,10 @@ public: virtual QByteArray serialize() const; virtual bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } + virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; } + virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; } + virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; } + virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; } private: Ui::SDRPlayV3Gui* ui; @@ -69,6 +73,7 @@ private: void updateSampleRateAndFrequency(); void updateFrequencyLimits(); bool handleMessage(const Message& message); + void makeUIConnections(); private slots: void updateHardware(); diff --git a/plugins/samplesource/sdrplayv3/sdrplayv3settings.cpp b/plugins/samplesource/sdrplayv3/sdrplayv3settings.cpp index 3787f473b..2cc56029d 100644 --- a/plugins/samplesource/sdrplayv3/sdrplayv3settings.cpp +++ b/plugins/samplesource/sdrplayv3/sdrplayv3settings.cpp @@ -54,6 +54,7 @@ void SDRPlayV3Settings::resetToDefaults() m_reverseAPIAddress = "127.0.0.1"; m_reverseAPIPort = 8888; m_reverseAPIDeviceIndex = 0; + m_workspaceIndex = 0; } QByteArray SDRPlayV3Settings::serialize() const @@ -85,6 +86,8 @@ QByteArray SDRPlayV3Settings::serialize() const s.writeBool(26, m_transverterMode); s.writeS64(27, m_transverterDeltaFrequency); s.writeBool(28, m_iqOrder); + s.writeS32(29, m_workspaceIndex); + s.writeBlob(30, m_geometryBytes); return s.final(); } @@ -138,6 +141,8 @@ bool SDRPlayV3Settings::deserialize(const QByteArray& data) d.readBool(26, &m_transverterMode, false); d.readS64(27, &m_transverterDeltaFrequency, 0); d.readBool(28, &m_iqOrder, true); + d.readS32(29, &m_workspaceIndex, 0); + d.readBlob(30, &m_geometryBytes); return true; } diff --git a/plugins/samplesource/sdrplayv3/sdrplayv3settings.h b/plugins/samplesource/sdrplayv3/sdrplayv3settings.h index 6a2ed2a90..f5c0c6466 100644 --- a/plugins/samplesource/sdrplayv3/sdrplayv3settings.h +++ b/plugins/samplesource/sdrplayv3/sdrplayv3settings.h @@ -57,6 +57,8 @@ struct SDRPlayV3Settings { QString m_reverseAPIAddress; uint16_t m_reverseAPIPort; uint16_t m_reverseAPIDeviceIndex; + int m_workspaceIndex; + QByteArray m_geometryBytes; SDRPlayV3Settings(); void resetToDefaults(); diff --git a/plugins/samplesource/sigmffileinput/sigmffileinputgui.cpp b/plugins/samplesource/sigmffileinput/sigmffileinputgui.cpp index 2e60cef5d..1e07aab74 100644 --- a/plugins/samplesource/sigmffileinput/sigmffileinputgui.cpp +++ b/plugins/samplesource/sigmffileinput/sigmffileinputgui.cpp @@ -61,7 +61,10 @@ SigMFFileInputGUI::SigMFFileInputGUI(DeviceUISet *deviceUISet, QWidget* parent) m_enableFullNavTime(false), m_lastEngineState(DeviceAPI::StNotStarted) { - ui->setupUi(this); + setAttribute(Qt::WA_DeleteOnClose, true); + ui->setupUi(getContents()); + getContents()->setStyleSheet("#SigMFFileInputGUI { border: 1px solid #C06900 }"); + m_helpURL = "plugins/samplesource/sigmffileinput/readme.md"; ui->fileNameText->setText(m_metaFileName); ui->crcLabel->setStyleSheet("QLabel { background:rgb(79,79,79); }"); @@ -76,6 +79,7 @@ SigMFFileInputGUI::SigMFFileInputGUI(DeviceUISet *deviceUISet, QWidget* parent) setAccelerationCombo(); displaySettings(); + makeUIConnections(); updateStartStop(); ui->trackNavTimeSlider->setEnabled(false); @@ -679,3 +683,18 @@ void SigMFFileInputGUI::openDeviceSettingsDialog(const QPoint& p) sendSettings(); } + +void SigMFFileInputGUI::makeUIConnections() +{ + QObject::connect(ui->startStop, &ButtonSwitch::toggled, this, &SigMFFileInputGUI::on_startStop_toggled); + QObject::connect(ui->infoDetails, &QPushButton::clicked, this, &SigMFFileInputGUI::on_infoDetails_clicked); + QObject::connect(ui->captureTable, &QTableWidget::itemSelectionChanged, this, &SigMFFileInputGUI::on_captureTable_itemSelectionChanged); + QObject::connect(ui->trackNavTimeSlider, &QSlider::valueChanged, this, &SigMFFileInputGUI::on_trackNavTimeSlider_valueChanged); + QObject::connect(ui->playTrack, &ButtonSwitch::toggled, this, &SigMFFileInputGUI::on_playTrack_toggled); + QObject::connect(ui->playTrackLoop, &ButtonSwitch::toggled, this, &SigMFFileInputGUI::on_playTrackLoop_toggled); + QObject::connect(ui->fullNavTimeSlider, &QSlider::valueChanged, this, &SigMFFileInputGUI::on_fullNavTimeSlider_valueChanged); + QObject::connect(ui->playFull, &ButtonSwitch::toggled, this, &SigMFFileInputGUI::on_playFull_toggled); + QObject::connect(ui->playFullLoop, &ButtonSwitch::toggled, this, &SigMFFileInputGUI::on_playFullLoop_toggled); + QObject::connect(ui->showFileDialog, &QPushButton::clicked, this, &SigMFFileInputGUI::on_showFileDialog_clicked); + QObject::connect(ui->acceleration, QOverload::of(&QComboBox::currentIndexChanged), this, &SigMFFileInputGUI::on_acceleration_currentIndexChanged); +} diff --git a/plugins/samplesource/sigmffileinput/sigmffileinputgui.h b/plugins/samplesource/sigmffileinput/sigmffileinputgui.h index fea452537..f468bcc03 100644 --- a/plugins/samplesource/sigmffileinput/sigmffileinputgui.h +++ b/plugins/samplesource/sigmffileinput/sigmffileinputgui.h @@ -46,6 +46,10 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } + virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; } + virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; } + virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; } + virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; } private: Ui::SigMFFileInputGUI* ui; @@ -94,6 +98,7 @@ private: void setAccelerationCombo(); void setNumberStr(int n, QString& s); bool handleMessage(const Message& message); + void makeUIConnections(); private slots: void handleInputMessages(); diff --git a/plugins/samplesource/sigmffileinput/sigmffileinputsettings.cpp b/plugins/samplesource/sigmffileinput/sigmffileinputsettings.cpp index 5abdce315..2104b9c16 100644 --- a/plugins/samplesource/sigmffileinput/sigmffileinputsettings.cpp +++ b/plugins/samplesource/sigmffileinput/sigmffileinputsettings.cpp @@ -36,6 +36,7 @@ void SigMFFileInputSettings::resetToDefaults() m_reverseAPIAddress = "127.0.0.1"; m_reverseAPIPort = 8888; m_reverseAPIDeviceIndex = 0; + m_workspaceIndex = 0; } QByteArray SigMFFileInputSettings::serialize() const @@ -49,6 +50,8 @@ QByteArray SigMFFileInputSettings::serialize() const s.writeString(6, m_reverseAPIAddress); s.writeU32(7, m_reverseAPIPort); s.writeU32(8, m_reverseAPIDeviceIndex); + s.writeS32(9, m_workspaceIndex); + s.writeBlob(10, m_geometryBytes); return s.final(); } @@ -82,6 +85,8 @@ bool SigMFFileInputSettings::deserialize(const QByteArray& data) d.readU32(8, &uintval, 0); m_reverseAPIDeviceIndex = uintval > 99 ? 99 : uintval; + d.readS32(9, &m_workspaceIndex, 0); + d.readBlob(10, &m_geometryBytes); return true; } diff --git a/plugins/samplesource/sigmffileinput/sigmffileinputsettings.h b/plugins/samplesource/sigmffileinput/sigmffileinputsettings.h index 9e365494f..30ce9149e 100644 --- a/plugins/samplesource/sigmffileinput/sigmffileinputsettings.h +++ b/plugins/samplesource/sigmffileinput/sigmffileinputsettings.h @@ -30,6 +30,8 @@ struct SigMFFileInputSettings { QString m_reverseAPIAddress; uint16_t m_reverseAPIPort; uint16_t m_reverseAPIDeviceIndex; + int m_workspaceIndex; + QByteArray m_geometryBytes; static const unsigned int m_accelerationMaxScale; //!< Max power of 10 multiplier to 2,5,10 base ex: 2 -> 2,5,10,20,50,100,200,500,1000 diff --git a/plugins/samplesource/soapysdrinput/soapysdrinputgui.cpp b/plugins/samplesource/soapysdrinput/soapysdrinputgui.cpp index b4b60e20d..b13efebe3 100644 --- a/plugins/samplesource/soapysdrinput/soapysdrinputgui.cpp +++ b/plugins/samplesource/soapysdrinput/soapysdrinputgui.cpp @@ -59,8 +59,11 @@ SoapySDRInputGui::SoapySDRInputGui(DeviceUISet *deviceUISet, QWidget* parent) : m_autoDCCorrection(0), m_autoIQCorrection(0) { + setAttribute(Qt::WA_DeleteOnClose, true); m_sampleSource = (SoapySDRInput*) m_deviceUISet->m_deviceAPI->getSampleSource(); - ui->setupUi(this); + ui->setupUi(getContents()); + getContents()->setStyleSheet("#SoapySDRInputGui { border: 1px solid #C06900 }"); + m_helpURL = "plugins/samplesource/soapysdrinput/readme.md"; ui->centerFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold)); uint64_t f_min, f_max; @@ -101,6 +104,7 @@ SoapySDRInputGui::SoapySDRInputGui(DeviceUISet *deviceUISet, QWidget* parent) : m_sampleSource->setMessageQueueToGUI(&m_inputMessageQueue); sendSettings(); + makeUIConnections(); } SoapySDRInputGui::~SoapySDRInputGui() @@ -924,3 +928,15 @@ void SoapySDRInputGui::openDeviceSettingsDialog(const QPoint& p) sendSettings(); } + +void SoapySDRInputGui::makeUIConnections() +{ + QObject::connect(ui->centerFrequency, &ValueDial::changed, this, &SoapySDRInputGui::on_centerFrequency_changed); + QObject::connect(ui->LOppm, &QSlider::valueChanged, this, &SoapySDRInputGui::on_LOppm_valueChanged); + QObject::connect(ui->dcOffset, &ButtonSwitch::toggled, this, &SoapySDRInputGui::on_dcOffset_toggled); + QObject::connect(ui->iqImbalance, &ButtonSwitch::toggled, this, &SoapySDRInputGui::on_iqImbalance_toggled); + QObject::connect(ui->decim, QOverload::of(&QComboBox::currentIndexChanged), this, &SoapySDRInputGui::on_decim_currentIndexChanged); + QObject::connect(ui->fcPos, QOverload::of(&QComboBox::currentIndexChanged), this, &SoapySDRInputGui::on_fcPos_currentIndexChanged); + QObject::connect(ui->transverter, &TransverterButton::clicked, this, &SoapySDRInputGui::on_transverter_clicked); + QObject::connect(ui->startStop, &ButtonSwitch::toggled, this, &SoapySDRInputGui::on_startStop_toggled); +} diff --git a/plugins/samplesource/soapysdrinput/soapysdrinputgui.h b/plugins/samplesource/soapysdrinput/soapysdrinputgui.h index 56bd33042..b1bc40230 100644 --- a/plugins/samplesource/soapysdrinput/soapysdrinputgui.h +++ b/plugins/samplesource/soapysdrinput/soapysdrinputgui.h @@ -51,6 +51,10 @@ public: virtual QByteArray serialize() const; virtual bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } + virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; } + virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; } + virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; } + virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; } private: void createRangesControl( @@ -64,6 +68,7 @@ private: void createIndividualGainsControl(const std::vector& individualGainsList); void createCorrectionsControl(); void createArgumentsControl(const SoapySDR::ArgInfoList& argInfoList, bool deviceArguments); + void makeUIConnections(); Ui::SoapySDRInputGui* ui; diff --git a/plugins/samplesource/soapysdrinput/soapysdrinputsettings.cpp b/plugins/samplesource/soapysdrinput/soapysdrinputsettings.cpp index 6583dde0a..0709f7f3f 100644 --- a/plugins/samplesource/soapysdrinput/soapysdrinputsettings.cpp +++ b/plugins/samplesource/soapysdrinput/soapysdrinputsettings.cpp @@ -50,6 +50,7 @@ void SoapySDRInputSettings::resetToDefaults() m_reverseAPIAddress = "127.0.0.1"; m_reverseAPIPort = 8888; m_reverseAPIDeviceIndex = 0; + m_workspaceIndex = 0; } QByteArray SoapySDRInputSettings::serialize() const @@ -83,6 +84,8 @@ QByteArray SoapySDRInputSettings::serialize() const s.writeU32(25, m_reverseAPIPort); s.writeU32(26, m_reverseAPIDeviceIndex); s.writeBool(27, m_iqOrder); + s.writeS32(28, m_workspaceIndex); + s.writeBlob(29, m_geometryBytes); return s.final(); } @@ -146,6 +149,8 @@ bool SoapySDRInputSettings::deserialize(const QByteArray& data) d.readU32(26, &uintval, 0); m_reverseAPIDeviceIndex = uintval > 99 ? 99 : uintval; d.readBool(27, &m_iqOrder, true); + d.readS32(28, &m_workspaceIndex, 0); + d.readBlob(29, &m_geometryBytes); return true; } diff --git a/plugins/samplesource/soapysdrinput/soapysdrinputsettings.h b/plugins/samplesource/soapysdrinput/soapysdrinputsettings.h index baab1b235..d68416238 100644 --- a/plugins/samplesource/soapysdrinput/soapysdrinputsettings.h +++ b/plugins/samplesource/soapysdrinput/soapysdrinputsettings.h @@ -56,6 +56,8 @@ struct SoapySDRInputSettings { QString m_reverseAPIAddress; uint16_t m_reverseAPIPort; uint16_t m_reverseAPIDeviceIndex; + int m_workspaceIndex; + QByteArray m_geometryBytes; SoapySDRInputSettings(); void resetToDefaults(); diff --git a/plugins/samplesource/testsource/testsourcegui.cpp b/plugins/samplesource/testsource/testsourcegui.cpp index b13a30f4d..211d411cd 100644 --- a/plugins/samplesource/testsource/testsourcegui.cpp +++ b/plugins/samplesource/testsource/testsourcegui.cpp @@ -51,9 +51,12 @@ TestSourceGui::TestSourceGui(DeviceUISet *deviceUISet, QWidget* parent) : m_lastEngineState(DeviceAPI::StNotStarted) { qDebug("TestSourceGui::TestSourceGui"); + setAttribute(Qt::WA_DeleteOnClose, true); m_sampleSource = m_deviceUISet->m_deviceAPI->getSampleSource(); - ui->setupUi(this); + ui->setupUi(getContents()); + getContents()->setStyleSheet("#TestSourceGui { border: 1px solid #C06900 }"); + m_helpURL = "plugins/samplesource/testsource/readme.md"; ui->centerFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold)); ui->centerFrequency->setValueRange(7, 0, 9999999); ui->sampleRate->setColorMapper(ColorMapper(ColorMapper::GrayGreenYellow)); @@ -63,6 +66,7 @@ TestSourceGui::TestSourceGui(DeviceUISet *deviceUISet, QWidget* parent) : ui->frequencyShiftLabel->setText(QString("%1").arg(QChar(0x94, 0x03))); displaySettings(); + makeUIConnections(); connect(&m_updateTimer, SIGNAL(timeout()), this, SLOT(updateHardware())); connect(&m_statusTimer, SIGNAL(timeout()), this, SLOT(updateStatus())); @@ -503,3 +507,25 @@ void TestSourceGui::openDeviceSettingsDialog(const QPoint& p) sendSettings(); } + +void TestSourceGui::makeUIConnections() +{ + QObject::connect(ui->startStop, &ButtonSwitch::toggled, this, &TestSourceGui::on_startStop_toggled); + QObject::connect(ui->centerFrequency, &ValueDial::changed, this, &TestSourceGui::on_centerFrequency_changed); + QObject::connect(ui->autoCorr, QOverload::of(&QComboBox::currentIndexChanged), this, &TestSourceGui::on_autoCorr_currentIndexChanged); + QObject::connect(ui->frequencyShift, &ValueDialZ::changed, this, &TestSourceGui::on_frequencyShift_changed); + QObject::connect(ui->decimation, QOverload::of(&QComboBox::currentIndexChanged), this, &TestSourceGui::on_decimation_currentIndexChanged); + QObject::connect(ui->fcPos, QOverload::of(&QComboBox::currentIndexChanged), this, &TestSourceGui::on_fcPos_currentIndexChanged); + QObject::connect(ui->sampleRate, &ValueDial::changed, this, &TestSourceGui::on_sampleRate_changed); + QObject::connect(ui->sampleSize, QOverload::of(&QComboBox::currentIndexChanged), this, &TestSourceGui::on_sampleSize_currentIndexChanged); + QObject::connect(ui->amplitudeCoarse, &QSlider::valueChanged, this, &TestSourceGui::on_amplitudeCoarse_valueChanged); + QObject::connect(ui->amplitudeFine, &QSlider::valueChanged, this, &TestSourceGui::on_amplitudeFine_valueChanged); + QObject::connect(ui->modulation, QOverload::of(&QComboBox::currentIndexChanged), this, &TestSourceGui::on_modulation_currentIndexChanged); + QObject::connect(ui->modulationFrequency, &QDial::valueChanged, this, &TestSourceGui::on_modulationFrequency_valueChanged); + QObject::connect(ui->amModulation, &QDial::valueChanged, this, &TestSourceGui::on_amModulation_valueChanged); + QObject::connect(ui->fmDeviation, &QDial::valueChanged, this, &TestSourceGui::on_fmDeviation_valueChanged); + QObject::connect(ui->dcBias, &QSlider::valueChanged, this, &TestSourceGui::on_dcBias_valueChanged); + QObject::connect(ui->iBias, &QSlider::valueChanged, this, &TestSourceGui::on_iBias_valueChanged); + QObject::connect(ui->qBias, &QSlider::valueChanged, this, &TestSourceGui::on_qBias_valueChanged); + QObject::connect(ui->phaseImbalance, &QSlider::valueChanged, this, &TestSourceGui::on_phaseImbalance_valueChanged); +} diff --git a/plugins/samplesource/testsource/testsourcegui.h b/plugins/samplesource/testsource/testsourcegui.h index f4fad8d1d..61b34eab1 100644 --- a/plugins/samplesource/testsource/testsourcegui.h +++ b/plugins/samplesource/testsource/testsourcegui.h @@ -45,6 +45,10 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } + virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; } + virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; } + virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; } + virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; } private: Ui::TestSourceGui* ui; @@ -71,6 +75,7 @@ private: void updateAmpFineLimit(); void updateFrequencyShiftLimit(); bool handleMessage(const Message& message); + void makeUIConnections(); private slots: void handleInputMessages(); diff --git a/plugins/samplesource/testsource/testsourcesettings.cpp b/plugins/samplesource/testsource/testsourcesettings.cpp index 2c343f242..3db304e51 100644 --- a/plugins/samplesource/testsource/testsourcesettings.cpp +++ b/plugins/samplesource/testsource/testsourcesettings.cpp @@ -46,6 +46,7 @@ void TestSourceSettings::resetToDefaults() m_reverseAPIAddress = "127.0.0.1"; m_reverseAPIPort = 8888; m_reverseAPIDeviceIndex = 0; + m_workspaceIndex = 0; } QByteArray TestSourceSettings::serialize() const @@ -71,6 +72,9 @@ QByteArray TestSourceSettings::serialize() const s.writeString(19, m_reverseAPIAddress); s.writeU32(20, m_reverseAPIPort); s.writeU32(21, m_reverseAPIDeviceIndex); + s.writeS32(22, m_workspaceIndex); + s.writeBlob(23, m_geometryBytes); + return s.final(); } @@ -132,6 +136,8 @@ bool TestSourceSettings::deserialize(const QByteArray& data) d.readU32(21, &utmp, 0); m_reverseAPIDeviceIndex = utmp > 99 ? 99 : utmp; + d.readS32(22, &m_workspaceIndex, 0); + d.readBlob(23, &m_geometryBytes); return true; } diff --git a/plugins/samplesource/testsource/testsourcesettings.h b/plugins/samplesource/testsource/testsourcesettings.h index e7529d51a..c22ffbb25 100644 --- a/plugins/samplesource/testsource/testsourcesettings.h +++ b/plugins/samplesource/testsource/testsourcesettings.h @@ -64,6 +64,8 @@ struct TestSourceSettings { QString m_reverseAPIAddress; uint16_t m_reverseAPIPort; uint16_t m_reverseAPIDeviceIndex; + int m_workspaceIndex; + QByteArray m_geometryBytes; TestSourceSettings(); void resetToDefaults(); diff --git a/plugins/samplesource/usrpinput/usrpinputgui.cpp b/plugins/samplesource/usrpinput/usrpinputgui.cpp index db606d134..3325f91d9 100644 --- a/plugins/samplesource/usrpinput/usrpinputgui.cpp +++ b/plugins/samplesource/usrpinput/usrpinputgui.cpp @@ -47,9 +47,12 @@ USRPInputGUI::USRPInputGUI(DeviceUISet *deviceUISet, QWidget* parent) : m_statusCounter(0), m_deviceStatusCounter(0) { + setAttribute(Qt::WA_DeleteOnClose, true); m_usrpInput = (USRPInput*) m_deviceUISet->m_deviceAPI->getSampleSource(); - ui->setupUi(this); + ui->setupUi(getContents()); + getContents()->setStyleSheet("#USRPInputGUI { border: 1px solid #C06900 }"); + m_helpURL = "plugins/samplesource/usrpinput/readme.md"; float minF, maxF; @@ -81,6 +84,7 @@ USRPInputGUI::USRPInputGUI(DeviceUISet *deviceUISet, QWidget* parent) : m_statusTimer.start(500); displaySettings(); + makeUIConnections(); connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()), Qt::QueuedConnection); m_usrpInput->setMessageQueueToGUI(&m_inputMessageQueue); @@ -620,3 +624,21 @@ void USRPInputGUI::openDeviceSettingsDialog(const QPoint& p) sendSettings(); } + +void USRPInputGUI::makeUIConnections() +{ + QObject::connect(ui->startStop, &ButtonSwitch::toggled, this, &USRPInputGUI::on_startStop_toggled); + QObject::connect(ui->centerFrequency, &ValueDial::changed, this, &USRPInputGUI::on_centerFrequency_changed); + QObject::connect(ui->dcOffset, &ButtonSwitch::toggled, this, &USRPInputGUI::on_dcOffset_toggled); + QObject::connect(ui->iqImbalance, &ButtonSwitch::toggled, this, &USRPInputGUI::on_iqImbalance_toggled); + QObject::connect(ui->sampleRate, &ValueDial::changed, this, &USRPInputGUI::on_sampleRate_changed); + QObject::connect(ui->swDecim, QOverload::of(&QComboBox::currentIndexChanged), this, &USRPInputGUI::on_swDecim_currentIndexChanged); + QObject::connect(ui->lpf, &ValueDial::changed, this, &USRPInputGUI::on_lpf_changed); + QObject::connect(ui->loOffset, &ValueDialZ::changed, this, &USRPInputGUI::on_loOffset_changed); + QObject::connect(ui->gainMode, QOverload::of(&QComboBox::currentIndexChanged), this, &USRPInputGUI::on_gainMode_currentIndexChanged); + QObject::connect(ui->gain, &QSlider::valueChanged, this, &USRPInputGUI::on_gain_valueChanged); + QObject::connect(ui->antenna, QOverload::of(&QComboBox::currentIndexChanged), this, &USRPInputGUI::on_antenna_currentIndexChanged); + QObject::connect(ui->clockSource, QOverload::of(&QComboBox::currentIndexChanged), this, &USRPInputGUI::on_clockSource_currentIndexChanged); + QObject::connect(ui->transverter, &TransverterButton::clicked, this, &USRPInputGUI::on_transverter_clicked); + QObject::connect(ui->sampleRateMode, &QToolButton::toggled, this, &USRPInputGUI::on_sampleRateMode_toggled); +} diff --git a/plugins/samplesource/usrpinput/usrpinputgui.h b/plugins/samplesource/usrpinput/usrpinputgui.h index 9790d7fb2..4facc1e5a 100644 --- a/plugins/samplesource/usrpinput/usrpinputgui.h +++ b/plugins/samplesource/usrpinput/usrpinputgui.h @@ -50,7 +50,10 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } - virtual bool handleMessage(const Message& message); + virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; } + virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; } + virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; } + virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; } private: Ui::USRPInputGUI* ui; @@ -79,6 +82,8 @@ private: void updateSampleRate(); void updateFrequencyLimits(); void blockApplySettings(bool block); + bool handleMessage(const Message& message); + void makeUIConnections(); private slots: void handleInputMessages(); diff --git a/plugins/samplesource/usrpinput/usrpinputsettings.cpp b/plugins/samplesource/usrpinput/usrpinputsettings.cpp index 1a7c0b2be..76cfc3061 100644 --- a/plugins/samplesource/usrpinput/usrpinputsettings.cpp +++ b/plugins/samplesource/usrpinput/usrpinputsettings.cpp @@ -44,6 +44,7 @@ void USRPInputSettings::resetToDefaults() m_reverseAPIAddress = "127.0.0.1"; m_reverseAPIPort = 8888; m_reverseAPIDeviceIndex = 0; + m_workspaceIndex = 0; } QByteArray USRPInputSettings::serialize() const @@ -66,6 +67,9 @@ QByteArray USRPInputSettings::serialize() const s.writeU32(14, m_reverseAPIPort); s.writeU32(15, m_reverseAPIDeviceIndex); s.writeS32(16, m_loOffset); + s.writeS32(17, m_workspaceIndex); + s.writeBlob(18, m_geometryBytes); + return s.final(); } @@ -109,6 +113,8 @@ bool USRPInputSettings::deserialize(const QByteArray& data) d.readU32(15, &uintval, 0); m_reverseAPIDeviceIndex = uintval > 99 ? 99 : uintval; d.readS32(16, &m_loOffset, 0); + d.readS32(17, &m_workspaceIndex, 0); + d.readBlob(18, &m_geometryBytes); return true; } diff --git a/plugins/samplesource/usrpinput/usrpinputsettings.h b/plugins/samplesource/usrpinput/usrpinputsettings.h index 9f6aa5372..e843f0092 100644 --- a/plugins/samplesource/usrpinput/usrpinputsettings.h +++ b/plugins/samplesource/usrpinput/usrpinputsettings.h @@ -54,6 +54,8 @@ struct USRPInputSettings QString m_reverseAPIAddress; uint16_t m_reverseAPIPort; uint16_t m_reverseAPIDeviceIndex; + int m_workspaceIndex; + QByteArray m_geometryBytes; USRPInputSettings(); void resetToDefaults(); diff --git a/plugins/samplesource/xtrxinput/xtrxinputgui.cpp b/plugins/samplesource/xtrxinput/xtrxinputgui.cpp index 5065b0db9..17ff6f57c 100644 --- a/plugins/samplesource/xtrxinput/xtrxinputgui.cpp +++ b/plugins/samplesource/xtrxinput/xtrxinputgui.cpp @@ -47,9 +47,12 @@ XTRXInputGUI::XTRXInputGUI(DeviceUISet *deviceUISet, QWidget* parent) : m_statusCounter(0), m_deviceStatusCounter(0) { + setAttribute(Qt::WA_DeleteOnClose, true); m_XTRXInput = (XTRXInput*) m_deviceUISet->m_deviceAPI->getSampleSource(); - ui->setupUi(this); + ui->setupUi(getContents()); + getContents()->setStyleSheet("#XTRXInputGUI { border: 1px solid #C06900 }"); + m_helpURL = "plugins/samplesource/xtrxinput/readme.md"; float minF, maxF, stepF; @@ -77,6 +80,7 @@ XTRXInputGUI::XTRXInputGUI(DeviceUISet *deviceUISet, QWidget* parent) : connect(startStopRightClickEnabler, SIGNAL(rightClick(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &))); displaySettings(); + makeUIConnections(); connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()), Qt::QueuedConnection); } @@ -647,3 +651,26 @@ void XTRXInputGUI::openDeviceSettingsDialog(const QPoint& p) sendSettings(); } + +void XTRXInputGUI::makeUIConnections() +{ + QObject::connect(ui->startStop, &ButtonSwitch::toggled, this, &XTRXInputGUI::on_startStop_toggled); + QObject::connect(ui->centerFrequency, &ValueDial::changed, this, &XTRXInputGUI::on_centerFrequency_changed); + QObject::connect(ui->ncoFrequency, &ValueDialZ::changed, this, &XTRXInputGUI::on_ncoFrequency_changed); + QObject::connect(ui->ncoEnable, &ButtonSwitch::toggled, this, &XTRXInputGUI::on_ncoEnable_toggled); + QObject::connect(ui->dcOffset, &ButtonSwitch::toggled, this, &XTRXInputGUI::on_dcOffset_toggled); + QObject::connect(ui->iqImbalance, &ButtonSwitch::toggled, this, &XTRXInputGUI::on_iqImbalance_toggled); + QObject::connect(ui->sampleRate, &ValueDial::changed, this, &XTRXInputGUI::on_sampleRate_changed); + QObject::connect(ui->hwDecim, QOverload::of(&QComboBox::currentIndexChanged), this, &XTRXInputGUI::on_hwDecim_currentIndexChanged); + QObject::connect(ui->swDecim, QOverload::of(&QComboBox::currentIndexChanged), this, &XTRXInputGUI::on_swDecim_currentIndexChanged); + QObject::connect(ui->lpf, &ValueDial::changed, this, &XTRXInputGUI::on_lpf_changed); + QObject::connect(ui->gainMode, QOverload::of(&QComboBox::currentIndexChanged), this, &XTRXInputGUI::on_gainMode_currentIndexChanged); + QObject::connect(ui->gain, &QDial::valueChanged, this, &XTRXInputGUI::on_gain_valueChanged); + QObject::connect(ui->lnaGain, &QDial::valueChanged, this, &XTRXInputGUI::on_lnaGain_valueChanged); + QObject::connect(ui->tiaGain, QOverload::of(&QComboBox::currentIndexChanged), this, &XTRXInputGUI::on_tiaGain_currentIndexChanged); + QObject::connect(ui->pgaGain, &QDial::valueChanged, this, &XTRXInputGUI::on_pgaGain_valueChanged); + QObject::connect(ui->antenna, QOverload::of(&QComboBox::currentIndexChanged), this, &XTRXInputGUI::on_antenna_currentIndexChanged); + QObject::connect(ui->extClock, &ExternalClockButton::clicked, this, &XTRXInputGUI::on_extClock_clicked); + QObject::connect(ui->pwrmode, QOverload::of(&QComboBox::currentIndexChanged), this, &XTRXInputGUI::on_pwrmode_currentIndexChanged); + QObject::connect(ui->sampleRateMode, &QToolButton::toggled, this, &XTRXInputGUI::on_sampleRateMode_toggled); +} diff --git a/plugins/samplesource/xtrxinput/xtrxinputgui.h b/plugins/samplesource/xtrxinput/xtrxinputgui.h index 36a793791..d727a5c4d 100644 --- a/plugins/samplesource/xtrxinput/xtrxinputgui.h +++ b/plugins/samplesource/xtrxinput/xtrxinputgui.h @@ -45,6 +45,10 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } + virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; } + virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; } + virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; } + virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; } private: Ui::XTRXInputGUI* ui; @@ -74,6 +78,7 @@ private: void updateADCRate(); void blockApplySettings(bool block); bool handleMessage(const Message& message); + void makeUIConnections(); private slots: void handleInputMessages(); diff --git a/plugins/samplesource/xtrxinput/xtrxinputsettings.cpp b/plugins/samplesource/xtrxinput/xtrxinputsettings.cpp index 98584835d..47bbe02d3 100644 --- a/plugins/samplesource/xtrxinput/xtrxinputsettings.cpp +++ b/plugins/samplesource/xtrxinput/xtrxinputsettings.cpp @@ -48,6 +48,7 @@ void XTRXInputSettings::resetToDefaults() m_reverseAPIAddress = "127.0.0.1"; m_reverseAPIPort = 8888; m_reverseAPIDeviceIndex = 0; + m_workspaceIndex = 0; } QByteArray XTRXInputSettings::serialize() const @@ -76,6 +77,8 @@ QByteArray XTRXInputSettings::serialize() const s.writeU32(24, m_reverseAPIPort); s.writeU32(25, m_reverseAPIDeviceIndex); s.writeBool(26, m_iqOrder); + s.writeS32(27, m_workspaceIndex); + s.writeBlob(28, m_geometryBytes); return s.final(); } @@ -127,6 +130,8 @@ bool XTRXInputSettings::deserialize(const QByteArray& data) d.readU32(25, &uintval, 0); m_reverseAPIDeviceIndex = uintval > 99 ? 99 : uintval; d.readBool(26, &m_iqOrder, true); + d.readS32(27, &m_workspaceIndex, 0); + d.readBlob(28, &m_geometryBytes); return true; } diff --git a/plugins/samplesource/xtrxinput/xtrxinputsettings.h b/plugins/samplesource/xtrxinput/xtrxinputsettings.h index fb0e9a63e..40c771af8 100644 --- a/plugins/samplesource/xtrxinput/xtrxinputsettings.h +++ b/plugins/samplesource/xtrxinput/xtrxinputsettings.h @@ -61,6 +61,8 @@ struct XTRXInputSettings QString m_reverseAPIAddress; uint16_t m_reverseAPIPort; uint16_t m_reverseAPIDeviceIndex; + int m_workspaceIndex; + QByteArray m_geometryBytes; XTRXInputSettings(); void resetToDefaults(); diff --git a/sdrbase/pipes/messagepipeslegacy.cpp b/sdrbase/pipes/messagepipeslegacy.cpp new file mode 100644 index 000000000..6d9355bba --- /dev/null +++ b/sdrbase/pipes/messagepipeslegacy.cpp @@ -0,0 +1,78 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2020 Edouard Griffiths, F4EXB // +// // +// This program 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 as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program 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 V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#include + +#include "util/messagequeue.h" + +#include "messagepipeslegacygcworker.h" +#include "messagepipeslegacy.h" +#include "pipeendpoint.h" + +MessagePipesLegacy::MessagePipesLegacy() +{ + m_gcWorker = new MessagePipesLegacyGCWorker(); + m_gcWorker->setC2FRegistrations( + m_registrations.getMutex(), + m_registrations.getElements(), + m_registrations.getConsumers() + ); + m_gcWorker->moveToThread(&m_gcThread); + startGC(); +} + +MessagePipesLegacy::~MessagePipesLegacy() +{ + if (m_gcWorker->isRunning()) { + stopGC(); + } +} + +MessageQueue *MessagePipesLegacy::registerChannelToFeature(const PipeEndPoint *source, PipeEndPoint *dest, const QString& type) +{ + qDebug("MessagePipesLegacy::registerChannelToFeature: %p %p %s", source, dest, qPrintable(type)); + return m_registrations.registerProducerToConsumer(source, dest, type); +} + +MessageQueue *MessagePipesLegacy::unregisterChannelToFeature(const PipeEndPoint *source, PipeEndPoint *dest, const QString& type) +{ + qDebug("MessagePipesLegacy::unregisterChannelToFeature: %p %p %s", source, dest, qPrintable(type)); + MessageQueue *messageQueue = m_registrations.unregisterProducerToConsumer(source, dest, type); + m_gcWorker->addMessageQueueToDelete(messageQueue); + return messageQueue; +} + +QList* MessagePipesLegacy::getMessageQueues(const PipeEndPoint *source, const QString& type) +{ + //qDebug("MessagePipesLegacy::getMessageQueues: %p %s", source, qPrintable(type)); + return m_registrations.getElements(source, type); +} + +void MessagePipesLegacy::startGC() +{ + qDebug("MessagePipesLegacy::startGC"); + m_gcWorker->startWork(); + m_gcThread.start(); +} + +void MessagePipesLegacy::stopGC() +{ + qDebug("MessagePipesLegacy::stopGC"); + m_gcWorker->stopWork(); + m_gcThread.quit(); + m_gcThread.wait(); +} diff --git a/sdrbase/pipes/messagepipeslegacy.h b/sdrbase/pipes/messagepipeslegacy.h new file mode 100644 index 000000000..562e582a8 --- /dev/null +++ b/sdrbase/pipes/messagepipeslegacy.h @@ -0,0 +1,59 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2020 Edouard Griffiths, F4EXB // +// // +// This program 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 as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program 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 V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef SDRBASE_PIPES_MESSAGEPIPESLEGACY_H_ +#define SDRBASE_PIPES_MESSAGEPIPESLEGACY_H_ + +#include +#include +#include +#include +#include + +#include "export.h" + +#include "messagepipeslegacycommon.h" +#include "elementpipesregistrations.h" + +class PipeEndPoint; +class MessagePipesLegacyGCWorker; +class MessageQueue; + +class SDRBASE_API MessagePipesLegacy : public QObject +{ + Q_OBJECT +public: + MessagePipesLegacy(); + MessagePipesLegacy(const MessagePipesLegacy&) = delete; + MessagePipesLegacy& operator=(const MessagePipesLegacy&) = delete; + ~MessagePipesLegacy(); + + // FIXME: Names of these functions should probably change, as we now support channel or feature at either end + MessageQueue *registerChannelToFeature(const PipeEndPoint *source, PipeEndPoint *dest, const QString& type); + MessageQueue *unregisterChannelToFeature(const PipeEndPoint *source, PipeEndPoint *dest, const QString& type); + QList* getMessageQueues(const PipeEndPoint *source, const QString& type); + +private: + ElementPipesRegistrations m_registrations; + QThread m_gcThread; //!< Garbage collector thread + MessagePipesLegacyGCWorker *m_gcWorker; //!< Garbage collector + + void startGC(); //!< Start garbage collector + void stopGC(); //!< Stop garbage collector +}; + +#endif // SDRBASE_PIPES_MESSAGEPIPESLEGACY_H_ diff --git a/sdrgui/CMakeLists.txt b/sdrgui/CMakeLists.txt index 4ed80997d..357850af4 100644 --- a/sdrgui/CMakeLists.txt +++ b/sdrgui/CMakeLists.txt @@ -83,6 +83,7 @@ set(sdrgui_SOURCES dsp/scopevisxy.cpp + device/devicegui.cpp device/deviceuiset.cpp channel/channelgui.cpp @@ -183,6 +184,7 @@ set(sdrgui_HEADERS dsp/scopevisxy.h + device/devicegui.h device/deviceuiset.h channel/channelgui.h @@ -190,8 +192,6 @@ set(sdrgui_HEADERS feature/featuregui.h feature/featureuiset.h - device/devicegui.h - soapygui/discreterangegui.h soapygui/intervalrangegui.h soapygui/itemsettinggui.h diff --git a/sdrgui/device/devicegui.cpp b/sdrgui/device/devicegui.cpp new file mode 100644 index 000000000..c1c1b1727 --- /dev/null +++ b/sdrgui/device/devicegui.cpp @@ -0,0 +1,313 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2022 Edouard Griffiths, F4EXB // +// // +// This program 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 as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program 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 V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mainwindow.h" +#include "gui/workspaceselectiondialog.h" +#include "gui/samplingdevicedialog.h" +#include "devicegui.h" + +DeviceGUI::DeviceGUI(QWidget *parent) : + QMdiSubWindow(parent), + m_deviceType(DeviceRx), + m_deviceSetIndex(0), + m_contextMenuType(ContextMenuNone), + m_drag(false), + m_currentDeviceIndex(-1) +{ + qDebug("DeviceGUI::DeviceGUI: %p", parent); + setWindowFlags(windowFlags() | Qt::FramelessWindowHint); + + m_indexLabel = new QLabel(); + m_indexLabel->setFixedSize(32, 16); + m_indexLabel->setStyleSheet("QLabel { background-color: rgb(128, 128, 128); qproperty-alignment: AlignCenter; }"); + m_indexLabel->setText(tr("X:%1").arg(m_deviceSetIndex)); + m_indexLabel->setToolTip("Device type and set index"); + + m_changeDeviceButton = new QPushButton(); + m_changeDeviceButton->setFixedSize(20, 20); + QIcon changeDeviceIcon(":/swap.png"); + m_changeDeviceButton->setIcon(changeDeviceIcon); + m_changeDeviceButton->setToolTip("Change device"); + + m_reloadDeviceButton = new QPushButton(); + m_reloadDeviceButton->setFixedSize(20, 20); + QIcon reloadDeviceIcon(":/recycle.png"); + m_reloadDeviceButton->setIcon(reloadDeviceIcon); + m_reloadDeviceButton->setToolTip("Reload device"); + + m_addChannelsButton = new QPushButton(); + m_addChannelsButton->setFixedSize(20, 20); + QIcon addChannelsIcon(":/create.png"); + m_addChannelsButton->setIcon(addChannelsIcon); + m_addChannelsButton->setToolTip("Add channels"); + + m_titleLabel = new QLabel(); + m_titleLabel->setText("Device"); + m_titleLabel->setToolTip("Device identification"); + m_titleLabel->setFixedHeight(20); + + m_helpButton = new QPushButton(); + m_helpButton->setFixedSize(20, 20); + QIcon helpIcon(":/help.png"); + m_helpButton->setIcon(helpIcon); + m_helpButton->setToolTip("Show device documentation in browser"); + + m_moveButton = new QPushButton(); + m_moveButton->setFixedSize(20, 20); + QIcon moveIcon(":/exit.png"); + m_moveButton->setIcon(moveIcon); + m_moveButton->setToolTip("Move to workspace"); + + m_shrinkButton = new QPushButton(); + m_shrinkButton->setFixedSize(20, 20); + QIcon shrinkIcon(":/shrink.png"); + m_shrinkButton->setIcon(shrinkIcon); + m_shrinkButton->setToolTip("Adjust window to minimum size"); + + m_closeButton = new QPushButton(); + m_closeButton->setFixedSize(20, 20); + QIcon closeIcon(":/cross.png"); + m_closeButton->setIcon(closeIcon); + m_closeButton->setToolTip("Close device"); + + m_statusLabel = new QLabel(); + // m_statusLabel->setText("OK"); // for future use + m_statusLabel->setFixedHeight(20); + m_statusLabel->setToolTip("Device status"); + + m_layouts = new QVBoxLayout(); + m_layouts->setContentsMargins(0, 4, 0, 4); + m_layouts->setSpacing(2); + + m_topLayout = new QHBoxLayout(); + m_topLayout->setContentsMargins(0, 0, 0, 0); + m_topLayout->addWidget(m_indexLabel); + m_topLayout->addWidget(m_changeDeviceButton); + m_topLayout->addWidget(m_reloadDeviceButton); + m_topLayout->addWidget(m_addChannelsButton); + m_topLayout->addWidget(m_titleLabel); + m_topLayout->addStretch(1); + m_topLayout->addWidget(m_helpButton); + m_topLayout->addWidget(m_moveButton); + m_topLayout->addWidget(m_shrinkButton); + m_topLayout->addWidget(m_closeButton); + m_sizeGripTopRight = new QSizeGrip(this); + m_sizeGripTopRight->setStyleSheet("QSizeGrip { background-color: rgb(128, 128, 128); width: 10px; height: 10px; }"); + m_topLayout->addWidget(m_sizeGripTopRight, 0, Qt::AlignTop | Qt::AlignRight); + + m_centerLayout = new QHBoxLayout(); + m_contents = new QWidget(); + m_centerLayout->addWidget(m_contents); + + m_bottomLayout = new QHBoxLayout(); + m_bottomLayout->setContentsMargins(0, 0, 0, 0); + m_bottomLayout->addWidget(m_statusLabel); + m_sizeGripBottomRight = new QSizeGrip(this); + m_sizeGripBottomRight->setStyleSheet("QSizeGrip { background-color: rgb(128, 128, 128); width: 10px; height: 10px; }"); + m_bottomLayout->addStretch(1); + m_bottomLayout->addWidget(m_sizeGripBottomRight, 0, Qt::AlignBottom | Qt::AlignRight); + + m_layouts->addLayout(m_topLayout); + m_layouts->addLayout(m_centerLayout); + m_layouts->addLayout(m_bottomLayout); + + QObjectCleanupHandler().add(layout()); + setLayout(m_layouts); + + connect(m_changeDeviceButton, SIGNAL(clicked()), this, SLOT(openChangeDeviceDialog())); + connect(m_reloadDeviceButton, SIGNAL(clicked()), this, SLOT(deviceReload())); + connect(m_helpButton, SIGNAL(clicked()), this, SLOT(showHelp())); + connect(m_moveButton, SIGNAL(clicked()), this, SLOT(openMoveToWorkspaceDialog())); + connect(m_shrinkButton, SIGNAL(clicked()), this, SLOT(shrinkWindow())); + connect(this, SIGNAL(forceShrink()), this, SLOT(shrinkWindow())); + connect(m_closeButton, SIGNAL(clicked()), this, SLOT(close())); + connect(this, SIGNAL(forceClose()), this, SLOT(close())); +} + +DeviceGUI::~DeviceGUI() +{ + qDebug("DeviceGUI::~DeviceGUI"); + delete m_sizeGripBottomRight; + delete m_bottomLayout; + delete m_centerLayout; + delete m_sizeGripTopRight; + delete m_topLayout; + delete m_layouts; + delete m_statusLabel; + delete m_closeButton; + delete m_shrinkButton; + delete m_moveButton; + delete m_helpButton; + delete m_titleLabel; + delete m_addChannelsButton; + delete m_reloadDeviceButton; + delete m_changeDeviceButton; + delete m_indexLabel; + delete m_contents; + qDebug("DeviceGUI::~DeviceGUI: end"); +} + +void DeviceGUI::closeEvent(QCloseEvent *event) +{ + qDebug("DeviceGUI::closeEvent"); + emit closing(); + event->accept(); +} + +void DeviceGUI::mousePressEvent(QMouseEvent* event) +{ + if ((event->button() == Qt::LeftButton) && isOnMovingPad()) + { + m_drag = true; + m_DragPosition = event->globalPos() - pos(); + event->accept(); + } +} + +void DeviceGUI::mouseMoveEvent(QMouseEvent* event) +{ + if ((event->buttons() & Qt::LeftButton) && isOnMovingPad()) + { + move(event->globalPos() - m_DragPosition); + event->accept(); + } +} + +void DeviceGUI::openChangeDeviceDialog() +{ + SamplingDeviceDialog dialog((int) m_deviceType, this); + + if (dialog.exec() == QDialog::Accepted) + { + m_currentDeviceIndex = dialog.getSelectedDeviceIndex(); + emit deviceChange(m_currentDeviceIndex); + } +} + +void DeviceGUI::deviceReload() +{ + if (m_currentDeviceIndex >= 0) { + emit deviceChange(m_currentDeviceIndex); + } +} + +void DeviceGUI::showHelp() +{ + if (m_helpURL.isEmpty()) { + return; + } + + QString url; + + if (m_helpURL.startsWith("http")) { + url = m_helpURL; + } else { + url = QString("https://github.com/f4exb/sdrangel/blob/master/%1").arg(m_helpURL); // Something like "plugins/channelrx/chanalyzer/readme.md" + } + + QDesktopServices::openUrl(QUrl(url)); +} + +void DeviceGUI::openMoveToWorkspaceDialog() +{ + int numberOfWorkspaces = MainWindow::getInstance()->getNumberOfWorkspaces(); + WorkspaceSelectionDialog dialog(numberOfWorkspaces, this); + dialog.exec(); + + if (dialog.hasChanged()) { + emit moveToWorkspace(dialog.getSelectedIndex()); + } +} + +void DeviceGUI::shrinkWindow() +{ + qDebug("DeviceGUI::shrinkWindow"); + adjustSize(); +} + +void DeviceGUI::setTitle(const QString& title) +{ + m_titleLabel->setText(title); +} + +QString DeviceGUI::getTitle() const +{ + return m_titleLabel->text(); +} + +bool DeviceGUI::isOnMovingPad() +{ + return m_indexLabel->underMouse() || m_titleLabel->underMouse() || m_statusLabel->underMouse(); +} + +void DeviceGUI::setIndex(int index) +{ + m_deviceSetIndex = index; + m_indexLabel->setText(tr("%1:%2").arg(getDeviceTypeTag()).arg(m_deviceSetIndex)); +} + +void DeviceGUI::setDeviceType(DeviceType type) +{ + m_deviceType = type; + m_indexLabel->setStyleSheet(tr("QLabel { background-color: %1; qproperty-alignment: AlignCenter; }").arg(getDeviceTypeColor())); +} + +void DeviceGUI::setToolTip(const QString& tooltip) +{ + m_titleLabel->setToolTip(tooltip); +} + +QString DeviceGUI::getDeviceTypeColor() +{ + switch(m_deviceType) + { + case DeviceRx: + return "rgb(0, 128, 0)"; + case DeviceTx: + return "rgb(204, 0, 0)"; + case DeviceMIMO: + return "rgb(0, 0, 192)"; + default: + return "rgb(128, 128, 128)"; + } +} + +QString DeviceGUI::getDeviceTypeTag() +{ + switch(m_deviceType) + { + case DeviceRx: + return "R"; + case DeviceTx: + return "T"; + case DeviceMIMO: + return "M"; + default: + return "X"; + } +} diff --git a/sdrgui/device/devicegui.h b/sdrgui/device/devicegui.h index bc3f6f80d..e6a947c93 100644 --- a/sdrgui/device/devicegui.h +++ b/sdrgui/device/devicegui.h @@ -1,5 +1,5 @@ /////////////////////////////////////////////////////////////////////////////////// -// Copyright (C) 2020 Edouard Griffiths, F4EXB // +// Copyright (C) 2020-2022 Edouard Griffiths, F4EXB // // // // This program is free software; you can redistribute it and/or modify // // it under the terms of the GNU General Public License as published by // @@ -18,6 +18,8 @@ #ifndef INCLUDE_DEVICEGUI_H #define INCLUDE_DEVICEGUI_H +#include + #include #include #include @@ -25,20 +27,109 @@ #include "export.h" +class QCloseEvent; class Message; class MessageQueue; - -class SDRGUI_API DeviceGUI : public QWidget { +class QLabel; +class QPushButton; +class QVBoxLayout; +class QHBoxLayout; +class QSizeGrip; +class SDRGUI_API DeviceGUI : public QMdiSubWindow { + Q_OBJECT public: - DeviceGUI(QWidget *parent = nullptr) : QWidget(parent) { }; - virtual ~DeviceGUI() { }; + enum DeviceType + { + DeviceRx, + DeviceTx, + DeviceMIMO + }; + + enum ContextMenuType + { + ContextMenuNone, + ContextMenuChannelSettings, + ContextMenuStreamSettings + }; + + DeviceGUI(QWidget *parent = nullptr); + virtual ~DeviceGUI(); virtual void destroy() = 0; virtual void resetToDefaults() = 0; virtual QByteArray serialize() const = 0; virtual bool deserialize(const QByteArray& data) = 0; + // Data saved in the derived settings + virtual void setWorkspaceIndex(int index)= 0; + virtual int getWorkspaceIndex() const = 0; + virtual void setGeometryBytes(const QByteArray& blob) = 0; + virtual QByteArray getGeometryBytes() const = 0; virtual MessageQueue* getInputMessageQueue() = 0; + + QWidget *getContents() { return m_contents; } + void setDeviceType(DeviceType type); + DeviceType getDeviceType() const { return m_deviceType; } + void setTitle(const QString& title); + QString getTitle() const; + void setToolTip(const QString& tooltip); + void setIndex(int index); + int getIndex() const { return m_deviceSetIndex; } + void setCurrentDeviceIndex(int index) { m_currentDeviceIndex = index; } //!< index in plugins list + +protected: + void closeEvent(QCloseEvent *event); + void mousePressEvent(QMouseEvent* event); + void mouseMoveEvent(QMouseEvent* event); + void resetContextMenuType() { m_contextMenuType = ContextMenuNone; } + + DeviceType m_deviceType; + int m_deviceSetIndex; + QString m_helpURL; + QWidget *m_contents; + ContextMenuType m_contextMenuType; + +protected slots: + void shrinkWindow(); + +private: + bool isOnMovingPad(); + QString getDeviceTypeColor(); + QString getDeviceTypeTag(); + + QLabel *m_indexLabel; + QPushButton *m_changeDeviceButton; + QPushButton *m_reloadDeviceButton; + QPushButton *m_addChannelsButton; + QLabel *m_titleLabel; + QPushButton *m_helpButton; + QPushButton *m_moveButton; + QPushButton *m_shrinkButton; + QPushButton *m_closeButton; + QLabel *m_statusLabel; + QVBoxLayout *m_layouts; + QHBoxLayout *m_topLayout; + QHBoxLayout *m_centerLayout; + QHBoxLayout *m_bottomLayout; + QSizeGrip *m_sizeGripTopRight; + QSizeGrip *m_sizeGripBottomRight; + bool m_drag; + QPoint m_DragPosition; + int m_currentDeviceIndex; //!< Index in device plugins registrations + +private slots: + void openChangeDeviceDialog(); + void deviceReload(); + void showHelp(); + void openMoveToWorkspaceDialog(); + +signals: + void forceClose(); + void closing(); + void moveToWorkspace(int workspaceIndex); + void forceShrink(); + void deviceAdd(int deviceType, int deviceIndex); + void deviceChange(int newDeviceIndex); }; #endif // INCLUDE_DEVICEGUI_H diff --git a/sdrgui/feature/featuregui.cpp b/sdrgui/feature/featuregui.cpp index 05e5f8d02..41638097e 100644 --- a/sdrgui/feature/featuregui.cpp +++ b/sdrgui/feature/featuregui.cpp @@ -130,6 +130,7 @@ FeatureGUI::FeatureGUI(QWidget *parent) : FeatureGUI::~FeatureGUI() { + qDebug("FeatureGUI::~FeatureGUI"); delete m_sizeGripBottomRight; delete m_bottomLayout; delete m_centerLayout; @@ -144,6 +145,7 @@ FeatureGUI::~FeatureGUI() delete m_titleLabel; delete m_settingsButton; delete m_indexLabel; + qDebug("FeatureGUI::~FeatureGUI: end"); } void FeatureGUI::closeEvent(QCloseEvent *event) diff --git a/sdrgui/gui/samplingdevicedialog.cpp b/sdrgui/gui/samplingdevicedialog.cpp index cf42381b2..fbbbbfec1 100644 --- a/sdrgui/gui/samplingdevicedialog.cpp +++ b/sdrgui/gui/samplingdevicedialog.cpp @@ -24,11 +24,10 @@ #include "device/deviceenumerator.h" -SamplingDeviceDialog::SamplingDeviceDialog(int deviceType, int deviceTabIndex, QWidget* parent) : +SamplingDeviceDialog::SamplingDeviceDialog(int deviceType, QWidget* parent) : QDialog(parent), ui(new Ui::SamplingDeviceDialog), m_deviceType(deviceType), - m_deviceTabIndex(deviceTabIndex), m_selectedDeviceIndex(-1), m_hasChanged(false) { @@ -81,15 +80,6 @@ void SamplingDeviceDialog::on_deviceSelect_currentIndexChanged(int index) void SamplingDeviceDialog::accept() { m_selectedDeviceIndex = m_deviceIndexes[ui->deviceSelect->currentIndex()]; - - if (m_deviceType == 0) { // Single Rx - DeviceEnumerator::instance()->changeRxSelection(m_deviceTabIndex, m_selectedDeviceIndex); - } else if (m_deviceType == 1) { // Single Tx - DeviceEnumerator::instance()->changeTxSelection(m_deviceTabIndex, m_selectedDeviceIndex); - } else if (m_deviceType == 2) { // MIMO - DeviceEnumerator::instance()->changeMIMOSelection(m_deviceTabIndex, m_selectedDeviceIndex); - } - QDialog::accept(); } diff --git a/sdrgui/gui/samplingdevicedialog.h b/sdrgui/gui/samplingdevicedialog.h index 9a5a3dc9c..7662e1cec 100644 --- a/sdrgui/gui/samplingdevicedialog.h +++ b/sdrgui/gui/samplingdevicedialog.h @@ -35,11 +35,10 @@ class SDRGUI_API SamplingDeviceDialog : public QDialog { Q_OBJECT public: - explicit SamplingDeviceDialog(int deviceType, int deviceTabIndex, QWidget* parent = nullptr); + explicit SamplingDeviceDialog(int deviceType, QWidget* parent = nullptr); ~SamplingDeviceDialog(); int getSelectedDeviceIndex() const { return m_selectedDeviceIndex; } void setSelectedDeviceIndex(int deviceIndex); - void setTabIndex(int deviceTabIndex) { m_deviceTabIndex = deviceTabIndex; } void getDeviceId(QString& id) const; int exec(); bool hasChanged() const { return m_hasChanged; } @@ -47,7 +46,6 @@ public: private: Ui::SamplingDeviceDialog* ui; int m_deviceType; - int m_deviceTabIndex; int m_selectedDeviceIndex; std::vector m_deviceIndexes; bool m_hasChanged; diff --git a/sdrgui/gui/samplingdevicesdock.cpp b/sdrgui/gui/samplingdevicesdock.cpp index 48e0cfa04..6cdea954b 100644 --- a/sdrgui/gui/samplingdevicesdock.cpp +++ b/sdrgui/gui/samplingdevicesdock.cpp @@ -111,7 +111,7 @@ void SamplingDevicesDock::addDevice(int deviceType, int deviceTabIndex) m_devicesInfo.push_back(DeviceInfo{ deviceType, deviceTabIndex, - new SamplingDeviceDialog(deviceType, deviceTabIndex, this) + new SamplingDeviceDialog(deviceType, this) }); setCurrentTabIndex(deviceTabIndex); diff --git a/sdrgui/gui/workspace.cpp b/sdrgui/gui/workspace.cpp index fc7eed526..724f10116 100644 --- a/sdrgui/gui/workspace.cpp +++ b/sdrgui/gui/workspace.cpp @@ -24,6 +24,7 @@ #include #include +#include "gui/samplingdevicedialog.h" #include "workspace.h" Workspace::Workspace(int index, QWidget *parent, Qt::WindowFlags flags) : @@ -130,21 +131,21 @@ Workspace::Workspace(int index, QWidget *parent, Qt::WindowFlags flags) : m_addRxDeviceButton, &QPushButton::clicked, this, - &Workspace::addRxDevice + &Workspace::addRxDeviceClicked ); QObject::connect( m_addTxDeviceButton, &QPushButton::clicked, this, - &Workspace::addTxDevice + &Workspace::addTxDeviceClicked ); QObject::connect( m_addMIMODeviceButton, &QPushButton::clicked, this, - &Workspace::addMIMODevice + &Workspace::addMIMODeviceClicked ); QObject::connect( @@ -194,6 +195,7 @@ Workspace::Workspace(int index, QWidget *parent, Qt::WindowFlags flags) : Workspace::~Workspace() { + qDebug("Workspace::~Workspace"); delete m_closeButton; delete m_normalButton; delete m_tileSubWindows; @@ -208,7 +210,9 @@ Workspace::~Workspace() delete m_titleLabel; delete m_titleBarLayout; delete m_titleBar; + qDebug("Workspace::~Workspace: about to delete MDI"); delete m_mdi; + qDebug("Workspace::~Workspace: end"); } void Workspace::toggleFloating() @@ -216,19 +220,31 @@ void Workspace::toggleFloating() setFloating(!isFloating()); } -void Workspace::addRxDevice() +void Workspace::addRxDeviceClicked() { + SamplingDeviceDialog dialog(0, this); + if (dialog.exec() == QDialog::Accepted) { + emit addRxDevice(this, dialog.getSelectedDeviceIndex()); + } } -void Workspace::addTxDevice() +void Workspace::addTxDeviceClicked() { + SamplingDeviceDialog dialog(1, this); + if (dialog.exec() == QDialog::Accepted) { + emit addTxDevice(this, dialog.getSelectedDeviceIndex()); + } } -void Workspace::addMIMODevice() +void Workspace::addMIMODeviceClicked() { + SamplingDeviceDialog dialog(2, this); + if (dialog.exec() == QDialog::Accepted) { + emit addMIMODevice(this, dialog.getSelectedDeviceIndex()); + } } void Workspace::addFeatureDialog() @@ -261,7 +277,6 @@ void Workspace::tileSubWindows() void Workspace::addToMdiArea(QMdiSubWindow *sub) { - sub->setParent(m_mdi); m_mdi->addSubWindow(sub); sub->show(); } diff --git a/sdrgui/gui/workspace.h b/sdrgui/gui/workspace.h index 3944ddfad..309332c15 100644 --- a/sdrgui/gui/workspace.h +++ b/sdrgui/gui/workspace.h @@ -68,9 +68,9 @@ private: QMdiArea *m_mdi; private slots: - void addRxDevice(); - void addTxDevice(); - void addMIMODevice(); + void addRxDeviceClicked(); + void addTxDeviceClicked(); + void addMIMODeviceClicked(); void addFeatureDialog(); void featurePresetsDialog(); void cascadeSubWindows(); @@ -79,6 +79,9 @@ private slots: void toggleFloating(); signals: + void addRxDevice(Workspace *inWorkspace, int deviceIndex); + void addTxDevice(Workspace *inWorkspace, int deviceIndex); + void addMIMODevice(Workspace *inWorkspace, int deviceIndex); void addFeature(Workspace*, int); void featurePresetsDialogRequested(QPoint, Workspace*); }; diff --git a/sdrgui/mainwindow.cpp b/sdrgui/mainwindow.cpp index 65a464ee5..76d0881ee 100644 --- a/sdrgui/mainwindow.cpp +++ b/sdrgui/mainwindow.cpp @@ -234,40 +234,40 @@ MainWindow::MainWindow(qtwebapp::LoggerWithFile *logger, const MainParser& parse m_pluginManager->loadPlugins(QString("plugins")); m_pluginManager->loadPluginsNonDiscoverable(m_mainCore->m_settings.getDeviceUserArgs()); - splash->showStatusMessage("load initial feature set...", Qt::white); - QStringList featureNames; - m_pluginManager->listFeatures(featureNames); + splash->showStatusMessage("Add unique feature set...", Qt::white); + // QStringList featureNames; + // m_pluginManager->listFeatures(featureNames); // ui->featureDock->addAvailableFeatures(featureNames); addFeatureSet(); // ui->featureDock->setFeatureUISet(m_featureUIs[0]); // ui->featureDock->setPresets(m_mainCore->m_settings.getFeatureSetPresets()); // ui->featureDock->setPluginAPI(m_pluginManager->getPluginAPI()); - splash->showStatusMessage("load last device or file input...", Qt::white); - qDebug() << "MainWindow::MainWindow: select SampleSource from settings or default (file input)..."; - qDebug() << "MainWindow::MainWindow: look for" - << m_mainCore->m_settings.getSourceDevice() - << "at index" << m_mainCore->m_settings.getSourceIndex() - << "and item index" << m_mainCore->m_settings.getSourceItemIndex(); + // splash->showStatusMessage("load last device or file input...", Qt::white); + // qDebug() << "MainWindow::MainWindow: select SampleSource from settings or default (file input)..."; + // qDebug() << "MainWindow::MainWindow: look for" + // << m_mainCore->m_settings.getSourceDevice() + // << "at index" << m_mainCore->m_settings.getSourceIndex() + // << "and item index" << m_mainCore->m_settings.getSourceItemIndex(); - int deviceIndex = DeviceEnumerator::instance()->getRxSamplingDeviceIndex( - m_mainCore->m_settings.getSourceDevice(), - m_mainCore->m_settings.getSourceIndex(), - m_mainCore->m_settings.getSourceItemIndex()); - addSourceDevice(deviceIndex); // add the first device set with file input device as default if device in settings is not enumerated - m_deviceUIs.back()->m_deviceAPI->setBuddyLeader(true); // the first device is always the leader + // int deviceIndex = DeviceEnumerator::instance()->getRxSamplingDeviceIndex( + // m_mainCore->m_settings.getSourceDevice(), + // m_mainCore->m_settings.getSourceIndex(), + // m_mainCore->m_settings.getSourceItemIndex()); + // sampleSourceAdd(deviceIndex); // add the first device set with file input device as default if device in settings is not enumerated + // m_deviceUIs.back()->m_deviceAPI->setBuddyLeader(true); // the first device is always the leader tabChannelsIndexChanged(); // force channel selection list update - splash->showStatusMessage("load current preset settings...", Qt::white); - qDebug() << "MainWindow::MainWindow: load current preset settings..."; + splash->showStatusMessage("load current configuration...", Qt::white); + qDebug() << "MainWindow::MainWindow: load current configuration..."; // loadPresetSettings(m_mainCore->m_settings.getWorkingPreset(), 0); m_apiAdapter = new WebAPIAdapter(); // loadFeatureSetPresetSettings(m_mainCore->m_settings.getWorkingFeatureSetPreset(), 0); loadConfiguration(m_mainCore->m_settings.getWorkingConfiguration()); - splash->showStatusMessage("update preset controls...", Qt::white); - qDebug() << "MainWindow::MainWindow: update preset controls..."; + // splash->showStatusMessage("update preset controls...", Qt::white); + // qDebug() << "MainWindow::MainWindow: update preset controls..."; // updatePresetControls(); @@ -304,7 +304,7 @@ MainWindow::MainWindow(qtwebapp::LoggerWithFile *logger, const MainParser& parse m_dspEngine->setMIMOSupport(parser.getMIMOSupport()); // if (!parser.getMIMOSupport()) { - // ui->menu_Devices->removeAction(ui->action_addMIMODevice); + // ui->menu_Devices->removeAction(ui->action_sampleMIMOAdd); // } #ifdef __APPLE__ @@ -326,6 +326,8 @@ MainWindow::MainWindow(qtwebapp::LoggerWithFile *logger, const MainParser& parse MainWindow::~MainWindow() { + qDebug() << "MainWindow::~MainWindow"; + m_mainCore->m_settings.save(); m_apiServer->stop(); delete m_apiServer; @@ -343,15 +345,16 @@ MainWindow::~MainWindow() // delete ui; // delete m_spectrumToggleViewAction; - qDebug() << "MainWindow::~MainWindow: end"; delete m_commandKeyReceiver; for (const auto& workspace : m_workspaces) { delete workspace; } + + qDebug() << "MainWindow::~MainWindow: end"; } -void MainWindow::addSourceDevice(int deviceIndex) +void MainWindow::sampleSourceAdd(Workspace *workspace, int deviceIndex) { DSPDeviceSourceEngine *dspDeviceSourceEngine = m_dspEngine->addDeviceSourceEngine(); dspDeviceSourceEngine->start(); @@ -360,11 +363,10 @@ void MainWindow::addSourceDevice(int deviceIndex) char uidCStr[16]; sprintf(uidCStr, "UID:%d", dspDeviceSourceEngineUID); - int deviceTabIndex = m_deviceUIs.size(); - // ui->inputViewDock->addDevice(0, deviceTabIndex); + int deviceSetIndex = m_deviceUIs.size(); m_mainCore->appendDeviceSet(0); - m_deviceUIs.push_back(new DeviceUISet(deviceTabIndex, m_mainCore->m_deviceSets.back())); + m_deviceUIs.push_back(new DeviceUISet(deviceSetIndex, m_mainCore->m_deviceSets.back())); m_deviceUIs.back()->m_deviceSourceEngine = dspDeviceSourceEngine; m_mainCore->m_deviceSets.back()->m_deviceSourceEngine = dspDeviceSourceEngine; m_deviceUIs.back()->m_deviceSinkEngine = nullptr; @@ -372,10 +374,7 @@ void MainWindow::addSourceDevice(int deviceIndex) m_deviceUIs.back()->m_deviceMIMOEngine = nullptr; m_mainCore->m_deviceSets.back()->m_deviceMIMOEngine = nullptr; - char tabNameCStr[16]; - sprintf(tabNameCStr, "R%d", deviceTabIndex); - - DeviceAPI *deviceAPI = new DeviceAPI(DeviceAPI::StreamSingleRx, deviceTabIndex, dspDeviceSourceEngine, nullptr, nullptr); + DeviceAPI *deviceAPI = new DeviceAPI(DeviceAPI::StreamSingleRx, deviceSetIndex, dspDeviceSourceEngine, nullptr, nullptr); m_deviceUIs.back()->m_deviceAPI = deviceAPI; m_mainCore->m_deviceSets.back()->m_deviceAPI = deviceAPI; @@ -384,15 +383,26 @@ void MainWindow::addSourceDevice(int deviceIndex) m_deviceUIs.back()->setNumberOfAvailableRxChannels(channelNames.size()); dspDeviceSourceEngine->addSink(m_deviceUIs.back()->m_spectrumVis); - // ui->tabSpectra->addTab(m_deviceUIs.back()->m_spectrum, tabNameCStr); - // ui->tabSpectraGUI->addTab(m_deviceUIs.back()->m_spectrumGUI, tabNameCStr); - // ui->tabChannels->addTab(m_deviceUIs.back()->m_channelWindow, tabNameCStr); // Create a file source instance by default if requested device was not enumerated (index = -1) if (deviceIndex < 0) { deviceIndex = DeviceEnumerator::instance()->getFileInputDeviceIndex(); } + sampleSourceImplement(deviceSetIndex, deviceIndex, deviceAPI, m_deviceUIs.back(), workspace); + emit m_mainCore->deviceSetAdded(deviceSetIndex, deviceAPI); +} + +void MainWindow::sampleSourceImplement( + int deviceSetIndex, + int deviceIndex, + DeviceAPI *deviceAPI, + DeviceUISet *deviceUISet, + Workspace *workspace +) +{ + int selectedDeviceIndex = deviceIndex; + DeviceEnumerator::instance()->changeRxSelection(deviceSetIndex, deviceIndex); const PluginInterface::SamplingDevice *samplingDevice = DeviceEnumerator::instance()->getRxSamplingDevice(deviceIndex); deviceAPI->setSamplingDeviceSequence(samplingDevice->sequence); deviceAPI->setDeviceNbItems(samplingDevice->deviceNbItems); @@ -403,35 +413,111 @@ void MainWindow::addSourceDevice(int deviceIndex) deviceAPI->setSamplingDeviceDisplayName(samplingDevice->displayedName); deviceAPI->setSamplingDevicePluginInterface(DeviceEnumerator::instance()->getRxPluginInterface(deviceIndex)); + qDebug() << "MainWindow::sampleSourceImplement:" + << "deviceIndex:" << deviceIndex + << "hardwareId:" << samplingDevice->hardwareId + << "sequence:" << samplingDevice->sequence + << "id:" << samplingDevice->id + << "serial:" << samplingDevice->serial + << "displayedName:" << samplingDevice->displayedName; + + if (deviceAPI->getSamplingDeviceId().size() == 0) // non existent device => replace by default + { + qDebug("MainWindow::sampleSourceAdd: non existent device replaced by File Input"); + int fileInputDeviceIndex = DeviceEnumerator::instance()->getFileInputDeviceIndex(); + selectedDeviceIndex = fileInputDeviceIndex; + samplingDevice = DeviceEnumerator::instance()->getRxSamplingDevice(fileInputDeviceIndex); + deviceAPI->setSamplingDeviceSequence(samplingDevice->sequence); + deviceAPI->setDeviceNbItems(samplingDevice->deviceNbItems); + deviceAPI->setDeviceItemIndex(samplingDevice->deviceItemIndex); + deviceAPI->setHardwareId(samplingDevice->hardwareId); + deviceAPI->setSamplingDeviceId(samplingDevice->id); + deviceAPI->setSamplingDeviceSerial(samplingDevice->serial); + deviceAPI->setSamplingDeviceDisplayName(samplingDevice->displayedName); + deviceAPI->setSamplingDevicePluginInterface(DeviceEnumerator::instance()->getRxPluginInterface(fileInputDeviceIndex)); + } + QString userArgs = m_mainCore->m_settings.getDeviceUserArgs().findUserArgs(samplingDevice->hardwareId, samplingDevice->sequence); if (userArgs.size() > 0) { deviceAPI->setHardwareUserArguments(userArgs); } - // ui->inputViewDock->setSelectedDeviceIndex(deviceTabIndex, deviceIndex); + // add to buddies list + std::vector::iterator it = m_deviceUIs.begin(); + int nbOfBuddies = 0; - // delete previous plugin GUI if it exists - if (m_deviceUIs.back()->m_deviceGUI) { - m_deviceUIs.back()->m_deviceGUI->destroy(); + for (; it != m_deviceUIs.end(); ++it) + { + if (*it != deviceUISet) // do not add to itself + { + if ((*it)->m_deviceSourceEngine) // it is a source device + { + if ((deviceUISet->m_deviceAPI->getHardwareId() == (*it)->m_deviceAPI->getHardwareId()) && + (deviceUISet->m_deviceAPI->getSamplingDeviceSerial() == (*it)->m_deviceAPI->getSamplingDeviceSerial())) + { + (*it)->m_deviceAPI->addSourceBuddy(deviceUISet->m_deviceAPI); + nbOfBuddies++; + } + } + + if ((*it)->m_deviceSinkEngine) // it is a sink device + { + if ((deviceUISet->m_deviceAPI->getHardwareId() == (*it)->m_deviceAPI->getHardwareId()) && + (deviceUISet->m_deviceAPI->getSamplingDeviceSerial() == (*it)->m_deviceAPI->getSamplingDeviceSerial())) + { + (*it)->m_deviceAPI->addSourceBuddy(deviceUISet->m_deviceAPI); + nbOfBuddies++; + } + } + } } + if (nbOfBuddies == 0) { + deviceUISet->m_deviceAPI->setBuddyLeader(true); + } + + // delete previous GUI if it exists + if (deviceUISet->m_deviceGUI) { + emit deviceUISet->m_deviceGUI->forceClose(); + } + + // constructs new GUI and input object DeviceSampleSource *source = deviceAPI->getPluginInterface()->createSampleSourcePluginInstance( deviceAPI->getSamplingDeviceId(), deviceAPI); deviceAPI->setSampleSource(source); QWidget *gui; - DeviceGUI *pluginGUI = deviceAPI->getPluginInterface()->createSampleSourcePluginInstanceGUI( + DeviceGUI *deviceGUI = deviceAPI->getPluginInterface()->createSampleSourcePluginInstanceGUI( deviceAPI->getSamplingDeviceId(), &gui, - m_deviceUIs.back()); - deviceAPI->getSampleSource()->setMessageQueueToGUI(pluginGUI->getInputMessageQueue()); - m_deviceUIs.back()->m_deviceGUI = pluginGUI; - m_deviceUIs.back()->m_deviceAPI->getSampleSource()->init(); - setDeviceGUI(deviceTabIndex, gui, deviceAPI->getSamplingDeviceDisplayName()); - emit m_mainCore->deviceSetAdded(deviceTabIndex, deviceAPI); + deviceUISet + ); + QObject::connect( + deviceGUI, + &DeviceGUI::moveToWorkspace, + this, + [=](int wsIndexDest){ this->deviceMove(deviceGUI, wsIndexDest); } + ); + QObject::connect( + deviceGUI, + &DeviceGUI::deviceChange, + this, + [=](int newDeviceIndex){ this->sampleDeviceChangeHandler(deviceGUI, newDeviceIndex); } + ); + deviceAPI->getSampleSource()->setMessageQueueToGUI(deviceGUI->getInputMessageQueue()); + deviceUISet->m_deviceGUI = deviceGUI; + deviceUISet->m_deviceAPI->getSampleSource()->init(); + // Finalize GUI setup and add it to workspace MDI + deviceGUI->setDeviceType(DeviceGUI::DeviceRx); + deviceGUI->setIndex(deviceSetIndex); + deviceGUI->setWorkspaceIndex(workspace->getIndex()); + deviceGUI->setToolTip(samplingDevice->displayedName); + deviceGUI->setTitle(samplingDevice->displayedName.split(" ")[0]); + deviceGUI->setCurrentDeviceIndex(selectedDeviceIndex); + workspace->addToMdiArea((QMdiSubWindow*) deviceGUI); } -void MainWindow::addSinkDevice() +void MainWindow::sampleSinkAdd(Workspace *workspace, int deviceIndex) { DSPDeviceSinkEngine *dspDeviceSinkEngine = m_dspEngine->addDeviceSinkEngine(); dspDeviceSinkEngine->start(); @@ -440,11 +526,10 @@ void MainWindow::addSinkDevice() char uidCStr[16]; sprintf(uidCStr, "UID:%d", dspDeviceSinkEngineUID); - int deviceTabIndex = m_deviceUIs.size(); - // ui->inputViewDock->addDevice(1, deviceTabIndex); + int deviceSetIndex = m_deviceUIs.size(); m_mainCore->appendDeviceSet(1); - m_deviceUIs.push_back(new DeviceUISet(deviceTabIndex, m_mainCore->m_deviceSets.back())); + m_deviceUIs.push_back(new DeviceUISet(deviceSetIndex, m_mainCore->m_deviceSets.back())); m_deviceUIs.back()->m_deviceSourceEngine = nullptr; m_mainCore->m_deviceSets.back()->m_deviceSourceEngine = nullptr; m_deviceUIs.back()->m_deviceSinkEngine = dspDeviceSinkEngine; @@ -452,10 +537,7 @@ void MainWindow::addSinkDevice() m_deviceUIs.back()->m_deviceMIMOEngine = nullptr; m_mainCore->m_deviceSets.back()->m_deviceMIMOEngine = nullptr; - char tabNameCStr[16]; - sprintf(tabNameCStr, "T%d", deviceTabIndex); - - DeviceAPI *deviceAPI = new DeviceAPI(DeviceAPI::StreamSingleTx, deviceTabIndex, nullptr, dspDeviceSinkEngine, nullptr); + DeviceAPI *deviceAPI = new DeviceAPI(DeviceAPI::StreamSingleTx, deviceSetIndex, nullptr, dspDeviceSinkEngine, nullptr); m_deviceUIs.back()->m_deviceAPI = deviceAPI; m_mainCore->m_deviceSets.back()->m_deviceAPI = deviceAPI; @@ -465,13 +547,26 @@ void MainWindow::addSinkDevice() dspDeviceSinkEngine->addSpectrumSink(m_deviceUIs.back()->m_spectrumVis); m_deviceUIs.back()->m_spectrum->setDisplayedStream(false, 0); - // ui->tabSpectra->addTab(m_deviceUIs.back()->m_spectrum, tabNameCStr); - // ui->tabSpectraGUI->addTab(m_deviceUIs.back()->m_spectrumGUI, tabNameCStr); - // ui->tabChannels->addTab(m_deviceUIs.back()->m_channelWindow, tabNameCStr); - // create a file sink by default - int fileSinkDeviceIndex = DeviceEnumerator::instance()->getFileOutputDeviceIndex(); - const PluginInterface::SamplingDevice *samplingDevice = DeviceEnumerator::instance()->getTxSamplingDevice(fileSinkDeviceIndex); + if (deviceIndex < 0) { + deviceIndex = DeviceEnumerator::instance()->getFileOutputDeviceIndex(); // create a file output by default + } + + sampleSinkImplement(deviceSetIndex, deviceIndex, deviceAPI, m_deviceUIs.back(), workspace); + emit m_mainCore->deviceSetAdded(deviceSetIndex, deviceAPI); +} + +void MainWindow::sampleSinkImplement( + int deviceSetIndex, + int deviceIndex, + DeviceAPI *deviceAPI, + DeviceUISet *deviceUISet, + Workspace *workspace +) +{ + int selectedDeviceIndex = deviceIndex; + DeviceEnumerator::instance()->changeTxSelection(deviceSetIndex, deviceIndex); + const PluginInterface::SamplingDevice *samplingDevice = DeviceEnumerator::instance()->getTxSamplingDevice(deviceIndex); deviceAPI->setSamplingDeviceSequence(samplingDevice->sequence); deviceAPI->setDeviceNbItems(samplingDevice->deviceNbItems); deviceAPI->setDeviceItemIndex(samplingDevice->deviceItemIndex); @@ -479,7 +574,31 @@ void MainWindow::addSinkDevice() deviceAPI->setSamplingDeviceId(samplingDevice->id); deviceAPI->setSamplingDeviceSerial(samplingDevice->serial); deviceAPI->setSamplingDeviceDisplayName(samplingDevice->displayedName); - deviceAPI->setSamplingDevicePluginInterface(DeviceEnumerator::instance()->getTxPluginInterface(fileSinkDeviceIndex)); + deviceAPI->setSamplingDevicePluginInterface(DeviceEnumerator::instance()->getTxPluginInterface(deviceIndex)); + + qDebug() << "MainWindow::sampleSinkImplement:" + << "newDeviceIndex:" << deviceIndex + << "hardwareId:" << samplingDevice->hardwareId + << "sequence:" << samplingDevice->sequence + << "id:" << samplingDevice->id + << "serial:" << samplingDevice->serial + << "displayedName:" << samplingDevice->displayedName; + + if (deviceAPI->getSamplingDeviceId().size() == 0) // non existent device => replace by default + { + qDebug("MainWindow::sampleSinkImplement: non existent device replaced by File Sink"); + int fileSinkDeviceIndex = DeviceEnumerator::instance()->getFileOutputDeviceIndex(); + selectedDeviceIndex = fileSinkDeviceIndex; + const PluginInterface::SamplingDevice *samplingDevice = DeviceEnumerator::instance()->getTxSamplingDevice(fileSinkDeviceIndex); + deviceAPI->setSamplingDeviceSequence(samplingDevice->sequence); + deviceAPI->setDeviceNbItems(samplingDevice->deviceNbItems); + deviceAPI->setDeviceItemIndex(samplingDevice->deviceItemIndex); + deviceAPI->setHardwareId(samplingDevice->hardwareId); + deviceAPI->setSamplingDeviceId(samplingDevice->id); + deviceAPI->setSamplingDeviceSerial(samplingDevice->serial); + deviceAPI->setSamplingDeviceDisplayName(samplingDevice->displayedName); + deviceAPI->setSamplingDevicePluginInterface(DeviceEnumerator::instance()->getTxPluginInterface(fileSinkDeviceIndex)); + } QString userArgs = m_mainCore->m_settings.getDeviceUserArgs().findUserArgs(samplingDevice->hardwareId, samplingDevice->sequence); @@ -487,29 +606,81 @@ void MainWindow::addSinkDevice() deviceAPI->setHardwareUserArguments(userArgs); } - // ui->inputViewDock->setSelectedDeviceIndex(deviceTabIndex, fileSinkDeviceIndex); + // add to buddies list + std::vector::iterator it = m_deviceUIs.begin(); + int nbOfBuddies = 0; - // delete previous plugin GUI if it exists - if (m_deviceUIs.back()->m_deviceGUI) { - m_deviceUIs.back()->m_deviceGUI->destroy(); + for (; it != m_deviceUIs.end(); ++it) + { + if (*it != deviceUISet) // do not add to itself + { + if ((*it)->m_deviceSourceEngine) // it is a source device + { + if ((deviceAPI->getHardwareId() == (*it)->m_deviceAPI->getHardwareId()) && + (deviceAPI->getSamplingDeviceSerial() == (*it)->m_deviceAPI->getSamplingDeviceSerial())) + { + (*it)->m_deviceAPI->addSinkBuddy(deviceAPI); + nbOfBuddies++; + } + } + + if ((*it)->m_deviceSinkEngine) // it is a sink device + { + if ((deviceAPI->getHardwareId() == (*it)->m_deviceAPI->getHardwareId()) && + (deviceAPI->getSamplingDeviceSerial() == (*it)->m_deviceAPI->getSamplingDeviceSerial())) + { + (*it)->m_deviceAPI->addSinkBuddy(deviceAPI); + nbOfBuddies++; + } + } + } } + if (nbOfBuddies == 0) { + deviceAPI->setBuddyLeader(true); + } + + // delete previous plugin GUI if it exists + if (deviceUISet->m_deviceGUI) { + emit deviceUISet->m_deviceGUI->forceClose(); + } + + // constructs new GUI and output object DeviceSampleSink *sink = deviceAPI->getPluginInterface()->createSampleSinkPluginInstance( deviceAPI->getSamplingDeviceId(), deviceAPI); deviceAPI->setSampleSink(sink); QWidget *gui; - DeviceGUI *pluginGUI = deviceAPI->getPluginInterface()->createSampleSinkPluginInstanceGUI( + DeviceGUI *deviceGUI = deviceAPI->getPluginInterface()->createSampleSinkPluginInstanceGUI( deviceAPI->getSamplingDeviceId(), &gui, - m_deviceUIs.back()); - deviceAPI->getSampleSink()->setMessageQueueToGUI(pluginGUI->getInputMessageQueue()); - m_deviceUIs.back()->m_deviceGUI = pluginGUI; - m_deviceUIs.back()->m_deviceAPI->getSampleSink()->init(); - setDeviceGUI(deviceTabIndex, gui, deviceAPI->getSamplingDeviceDisplayName(), 1); - emit m_mainCore->deviceSetAdded(deviceTabIndex, deviceAPI); + deviceUISet + ); + QObject::connect( + deviceGUI, + &DeviceGUI::moveToWorkspace, + this, + [=](int wsIndexDest){ this->deviceMove(deviceGUI, wsIndexDest); } + ); + QObject::connect( + deviceGUI, + &DeviceGUI::deviceChange, + this, + [=](int newDeviceIndex){ this->sampleDeviceChangeHandler(deviceGUI, newDeviceIndex); } + ); + deviceAPI->getSampleSink()->setMessageQueueToGUI(deviceGUI->getInputMessageQueue()); + deviceUISet->m_deviceGUI = deviceGUI; + deviceUISet->m_deviceAPI->getSampleSink()->init(); + // Finalize GUI setup and add it to workspace MDI + deviceGUI->setDeviceType(DeviceGUI::DeviceTx); + deviceGUI->setIndex(deviceSetIndex); + deviceGUI->setWorkspaceIndex(workspace->getIndex()); + deviceGUI->setToolTip(samplingDevice->displayedName); + deviceGUI->setTitle(samplingDevice->displayedName.split(" ")[0]); + deviceGUI->setCurrentDeviceIndex(selectedDeviceIndex); + workspace->addToMdiArea((QMdiSubWindow*) deviceGUI); } -void MainWindow::addMIMODevice() +void MainWindow::sampleMIMOAdd(Workspace *workspace, int deviceIndex) { DSPDeviceMIMOEngine *dspDeviceMIMOEngine = m_dspEngine->addDeviceMIMOEngine(); dspDeviceMIMOEngine->start(); @@ -518,11 +689,10 @@ void MainWindow::addMIMODevice() char uidCStr[16]; sprintf(uidCStr, "UID:%d", dspDeviceMIMOEngineUID); - int deviceTabIndex = m_deviceUIs.size(); - // ui->inputViewDock->addDevice(2, deviceTabIndex); + int deviceSetIndex = m_deviceUIs.size(); m_mainCore->appendDeviceSet(2); - m_deviceUIs.push_back(new DeviceUISet(deviceTabIndex, m_mainCore->m_deviceSets.back())); + m_deviceUIs.push_back(new DeviceUISet(deviceSetIndex, m_mainCore->m_deviceSets.back())); m_deviceUIs.back()->m_deviceSourceEngine = nullptr; m_mainCore->m_deviceSets.back()->m_deviceSourceEngine = nullptr; m_deviceUIs.back()->m_deviceSinkEngine = nullptr; @@ -530,10 +700,7 @@ void MainWindow::addMIMODevice() m_deviceUIs.back()->m_deviceMIMOEngine = dspDeviceMIMOEngine; m_mainCore->m_deviceSets.back()->m_deviceMIMOEngine = dspDeviceMIMOEngine; - char tabNameCStr[16]; - sprintf(tabNameCStr, "M%d", deviceTabIndex); - - DeviceAPI *deviceAPI = new DeviceAPI(DeviceAPI::StreamMIMO, deviceTabIndex, nullptr, nullptr, dspDeviceMIMOEngine); + DeviceAPI *deviceAPI = new DeviceAPI(DeviceAPI::StreamMIMO, deviceSetIndex, nullptr, nullptr, dspDeviceMIMOEngine); m_deviceUIs.back()->m_deviceAPI = deviceAPI; m_mainCore->m_deviceSets.back()->m_deviceAPI = deviceAPI; @@ -551,13 +718,26 @@ void MainWindow::addMIMODevice() m_deviceUIs.back()->setNumberOfAvailableTxChannels(txChannelNames.size()); dspDeviceMIMOEngine->addSpectrumSink(m_deviceUIs.back()->m_spectrumVis); - // ui->tabSpectra->addTab(m_deviceUIs.back()->m_spectrum, tabNameCStr); - // ui->tabSpectraGUI->addTab(m_deviceUIs.back()->m_spectrumGUI, tabNameCStr); - // ui->tabChannels->addTab(m_deviceUIs.back()->m_channelWindow, tabNameCStr); - // create a test MIMO by default - int testMIMODeviceIndex = DeviceEnumerator::instance()->getTestMIMODeviceIndex(); - const PluginInterface::SamplingDevice *samplingDevice = DeviceEnumerator::instance()->getMIMOSamplingDevice(testMIMODeviceIndex); + if (deviceIndex < 0) { + deviceIndex = DeviceEnumerator::instance()->getTestMIMODeviceIndex(); // create a test MIMO by default + } + + sampleMIMOImplement(deviceSetIndex, deviceIndex, deviceAPI, m_deviceUIs.back(), workspace); + emit m_mainCore->deviceSetAdded(deviceSetIndex, deviceAPI); +} + +void MainWindow::sampleMIMOImplement( + int deviceSetIndex, + int deviceIndex, + DeviceAPI *deviceAPI, + DeviceUISet *deviceUISet, + Workspace *workspace +) +{ + int selectedDeviceIndex = deviceIndex; + DeviceEnumerator::instance()->changeMIMOSelection(deviceSetIndex, deviceIndex); + const PluginInterface::SamplingDevice *samplingDevice = DeviceEnumerator::instance()->getMIMOSamplingDevice(deviceIndex); deviceAPI->setSamplingDeviceSequence(samplingDevice->sequence); deviceAPI->setDeviceNbItems(samplingDevice->deviceNbItems); deviceAPI->setDeviceItemIndex(samplingDevice->deviceItemIndex); @@ -565,7 +745,31 @@ void MainWindow::addMIMODevice() deviceAPI->setSamplingDeviceId(samplingDevice->id); deviceAPI->setSamplingDeviceSerial(samplingDevice->serial); deviceAPI->setSamplingDeviceDisplayName(samplingDevice->displayedName); - deviceAPI->setSamplingDevicePluginInterface(DeviceEnumerator::instance()->getMIMOPluginInterface(testMIMODeviceIndex)); + deviceAPI->setSamplingDevicePluginInterface(DeviceEnumerator::instance()->getMIMOPluginInterface(deviceIndex)); + + qDebug() << "MainWindow::sampleMIMOImplement:" + << "newDeviceIndex:" << deviceIndex + << "hardwareId:" << samplingDevice->hardwareId + << "sequence:" << samplingDevice->sequence + << "id:" << samplingDevice->id + << "serial:" << samplingDevice->serial + << "displayedName:" << samplingDevice->displayedName; + + if (deviceAPI->getSamplingDeviceId().size() == 0) // non existent device => replace by default + { + qDebug("MainWindow::sampleMIMOImplement: non existent device replaced by Test MIMO"); + int testMIMODeviceIndex = DeviceEnumerator::instance()->getTestMIMODeviceIndex(); + selectedDeviceIndex = testMIMODeviceIndex; + const PluginInterface::SamplingDevice *samplingDevice = DeviceEnumerator::instance()->getMIMOSamplingDevice(testMIMODeviceIndex); + deviceAPI->setSamplingDeviceSequence(samplingDevice->sequence); + deviceAPI->setDeviceNbItems(samplingDevice->deviceNbItems); + deviceAPI->setDeviceItemIndex(samplingDevice->deviceItemIndex); + deviceAPI->setHardwareId(samplingDevice->hardwareId); + deviceAPI->setSamplingDeviceId(samplingDevice->id); + deviceAPI->setSamplingDeviceSerial(samplingDevice->serial); + deviceAPI->setSamplingDeviceDisplayName(samplingDevice->displayedName); + deviceAPI->setSamplingDevicePluginInterface(DeviceEnumerator::instance()->getMIMOPluginInterface(testMIMODeviceIndex)); + } QString userArgs = m_mainCore->m_settings.getDeviceUserArgs().findUserArgs(samplingDevice->hardwareId, samplingDevice->sequence); @@ -573,26 +777,44 @@ void MainWindow::addMIMODevice() deviceAPI->setHardwareUserArguments(userArgs); } - // ui->inputViewDock->setSelectedDeviceIndex(deviceTabIndex, testMIMODeviceIndex); - // delete previous plugin GUI if it exists - if (m_deviceUIs.back()->m_deviceGUI) { - m_deviceUIs.back()->m_deviceGUI->destroy(); + if (deviceUISet->m_deviceGUI) { + emit deviceUISet->m_deviceGUI->forceClose(); } + // constructs new GUI and output object DeviceSampleMIMO *mimo = deviceAPI->getPluginInterface()->createSampleMIMOPluginInstance( deviceAPI->getSamplingDeviceId(), deviceAPI); deviceAPI->setSampleMIMO(mimo); QWidget *gui; - DeviceGUI *pluginGUI = deviceAPI->getPluginInterface()->createSampleMIMOPluginInstanceGUI( + DeviceGUI *deviceGUI = deviceAPI->getPluginInterface()->createSampleMIMOPluginInstanceGUI( deviceAPI->getSamplingDeviceId(), &gui, - m_deviceUIs.back()); - deviceAPI->getSampleMIMO()->setMessageQueueToGUI(pluginGUI->getInputMessageQueue()); - m_deviceUIs.back()->m_deviceGUI = pluginGUI; - m_deviceUIs.back()->m_deviceAPI->getSampleMIMO()->init(); - setDeviceGUI(deviceTabIndex, gui, deviceAPI->getSamplingDeviceDisplayName(), 2); - emit m_mainCore->deviceSetAdded(deviceTabIndex, deviceAPI); + deviceUISet + ); + QObject::connect( + deviceGUI, + &DeviceGUI::moveToWorkspace, + this, + [=](int wsIndexDest){ this->deviceMove(deviceGUI, wsIndexDest); } + ); + QObject::connect( + deviceGUI, + &DeviceGUI::deviceChange, + this, + [=](int newDeviceIndex){ this->sampleDeviceChangeHandler(deviceGUI, newDeviceIndex); } + ); + deviceAPI->getSampleMIMO()->setMessageQueueToGUI(deviceGUI->getInputMessageQueue()); + deviceUISet->m_deviceGUI = deviceGUI; + deviceUISet->m_deviceAPI->getSampleMIMO()->init(); + // Finalize GUI setup and add it to workspace MDI + deviceGUI->setDeviceType(DeviceGUI::DeviceMIMO); + deviceGUI->setIndex(deviceSetIndex); + deviceGUI->setWorkspaceIndex(workspace->getIndex()); + deviceGUI->setToolTip(samplingDevice->displayedName); + deviceGUI->setTitle(samplingDevice->displayedName.split(" ")[0]); + deviceGUI->setCurrentDeviceIndex(selectedDeviceIndex); + workspace->addToMdiArea((QMdiSubWindow*) deviceGUI); } void MainWindow::removeLastDevice() @@ -621,7 +843,7 @@ void MainWindow::removeLastDevice() // ui->tabChannels->removeTab(ui->tabChannels->count() - 1); m_deviceWidgetTabs.removeLast(); - restoreDeviceTabs(); + // restoreDeviceTabs(); DeviceAPI *sourceAPI = m_deviceUIs.back()->m_deviceAPI; delete m_deviceUIs.back(); @@ -653,7 +875,7 @@ void MainWindow::removeLastDevice() // ui->tabChannels->removeTab(ui->tabChannels->count() - 1); m_deviceWidgetTabs.removeLast(); - restoreDeviceTabs(); + // restoreDeviceTabs(); DeviceAPI *sinkAPI = m_deviceUIs.back()->m_deviceAPI; delete m_deviceUIs.back(); @@ -685,7 +907,7 @@ void MainWindow::removeLastDevice() // ui->tabChannels->removeTab(ui->tabChannels->count() - 1); m_deviceWidgetTabs.removeLast(); - restoreDeviceTabs(); + // restoreDeviceTabs(); DeviceAPI *mimoAPI = m_deviceUIs.back()->m_deviceAPI; delete m_deviceUIs.back(); @@ -740,36 +962,6 @@ void MainWindow::deleteChannel(int deviceSetIndex, int channelIndex) } } -void MainWindow::addViewAction(QAction* action) -{ - // ui->menu_Window->addAction(action); -} - -void MainWindow::setDeviceGUI(int deviceTabIndex, QWidget* gui, const QString& deviceDisplayName, int deviceType) -{ - QString tabName; - - if (deviceType == 0) { - tabName = tr("R%1").arg(deviceTabIndex); - } else if (deviceType == 1) { - tabName = tr("T%1").arg(deviceTabIndex); - } else if (deviceType == 2) { - tabName = tr("M%1").arg(deviceTabIndex); - } - - qDebug("MainWindow::setDeviceGUI: insert device type %d tab at %d", deviceType, deviceTabIndex); - - if (deviceTabIndex < m_deviceWidgetTabs.size()) { - m_deviceWidgetTabs[deviceTabIndex] = {gui, deviceDisplayName, tabName}; - } else { - m_deviceWidgetTabs.append({gui, deviceDisplayName, tabName}); - } - - restoreDeviceTabs(); - - // ui->tabInputsView->setCurrentIndex(deviceTabIndex); -} - void MainWindow::loadSettings() { qDebug() << "MainWindow::loadSettings"; @@ -1154,45 +1346,6 @@ QTreeWidgetItem* MainWindow::addPresetToTree(const Preset* preset) return nullptr; } -QTreeWidgetItem* MainWindow::addCommandToTree(const Command* command) -{ - // QTreeWidgetItem* group = 0; - - // for(int i = 0; i < ui->commandTree->topLevelItemCount(); i++) - // { - // if(ui->commandTree->topLevelItem(i)->text(0) == command->getGroup()) - // { - // group = ui->commandTree->topLevelItem(i); - // break; - // } - // } - - // if(group == 0) - // { - // QStringList sl; - // sl.append(command->getGroup()); - // group = new QTreeWidgetItem(ui->commandTree, sl, PGroup); - // group->setFirstColumnSpanned(true); - // group->setExpanded(true); - // ui->commandTree->sortByColumn(0, Qt::AscendingOrder); - // } - - // QStringList sl; - // sl.append(QString("%1").arg(command->getDescription())); // Descriptions column - // sl.append(QString("%1").arg(command->getAssociateKey() ? command->getRelease() ? "R" : "P" : "-")); // key press/release column - // sl.append(QString("%1").arg(command->getKeyLabel())); // key column - // CommandItem* item = new CommandItem(group, sl, command->getDescription(), PItem); - // item->setData(0, Qt::UserRole, QVariant::fromValue(command)); - // item->setTextAlignment(0, Qt::AlignLeft); - // ui->commandTree->resizeColumnToContents(0); // Resize description column to minimum - // ui->commandTree->resizeColumnToContents(1); // Resize key column to minimum - // ui->commandTree->resizeColumnToContents(2); // Resize key press/release column to minimum - - // //updatePresetControls(); - // return item; - return nullptr; -} - void MainWindow::applySettings() { // loadPresetSettings(m_mainCore->m_settings.getWorkingPreset(), 0); @@ -1216,9 +1369,9 @@ void MainWindow::applySettings() // m_mainCore->m_settings.sortCommands(); // ui->commandTree->clear(); - for (int i = 0; i < m_mainCore->m_settings.getCommandCount(); ++i) { - treeItem = addCommandToTree(m_mainCore->m_settings.getCommand(i)); - } + // for (int i = 0; i < m_mainCore->m_settings.getCommandCount(); ++i) { + // treeItem = addCommandToTree(m_mainCore->m_settings.getCommand(i)); + // } m_mainCore->setLoggingOptions(); } @@ -1302,12 +1455,15 @@ bool MainWindow::handleMessage(const Message& cmd) MainCore::MsgAddDeviceSet& notif = (MainCore::MsgAddDeviceSet&) cmd; int direction = notif.getDirection(); + // TODO: implement add workspace API. Will have to give the workspace index that will be ignored + // in Server flavor. Set nullptr for workspace if index is out of bonds + if (direction == 1) { // Single stream Tx - addSinkDevice(); + sampleSinkAdd(nullptr, -1); // create with file output device by default } else if (direction == 0) { // Single stream Rx - addSourceDevice(-1); // create with file source device by default + sampleSourceAdd(nullptr, -1); // create with file input device by default } else if (direction == 2) { // MIMO - addMIMODevice(); + sampleMIMOAdd(nullptr, -1); // create with testMI MIMO device y default } return true; @@ -1323,9 +1479,11 @@ bool MainWindow::handleMessage(const Message& cmd) else if (MainCore::MsgSetDevice::match(cmd)) { MainCore::MsgSetDevice& notif = (MainCore::MsgSetDevice&) cmd; - // ui->tabInputsView->setCurrentIndex(notif.getDeviceSetIndex()); - // ui->inputViewDock->setSelectedDeviceIndex(notif.getDeviceSetIndex(), notif.getDeviceIndex()); - samplingDeviceChanged(notif.getDeviceType(), notif.getDeviceSetIndex(), notif.getDeviceIndex()); + + // TODO: implement add workspace API. Will have to give the workspace index that will be ignored + // in Server flavor. Set nullptr for workspace if index is out of bonds + + sampleDeviceChange(notif.getDeviceType(), notif.getDeviceSetIndex(), notif.getDeviceIndex(), nullptr); return true; } @@ -1450,6 +1608,27 @@ void MainWindow::addWorkspace() m_workspaces.back()->addAvailableFeatures(featureNames); this->addDockWidget(Qt::LeftDockWidgetArea, m_workspaces.back()); + QObject::connect( + m_workspaces.back(), + &Workspace::addRxDevice, + this, + &MainWindow::sampleSourceAdd + ); + + QObject::connect( + m_workspaces.back(), + &Workspace::addTxDevice, + this, + &MainWindow::sampleSinkAdd + ); + + QObject::connect( + m_workspaces.back(), + &Workspace::addMIMODevice, + this, + &MainWindow::sampleMIMOAdd + ); + QObject::connect( m_workspaces.back(), &Workspace::addFeature, @@ -1975,15 +2154,6 @@ void MainWindow::fftWisdomProcessFinished(int exitCode, QProcess::ExitStatus exi m_fftWisdomProcess = nullptr; } -// void MainWindow::toggleSpectrumView(bool checked) -// { - // if (checked) { - // ui->centralWidget->show(); - // } else { - // ui->centralWidget->hide(); - // } -// } - void MainWindow::on_action_AMBE_triggered() { qDebug("MainWindow::on_action_AMBE_triggered"); @@ -2004,315 +2174,87 @@ void MainWindow::on_action_LimeRFE_triggered() #endif } -void MainWindow::samplingDeviceChanged(int deviceType, int tabIndex, int newDeviceIndex) +void MainWindow::sampleDeviceChangeHandler(DeviceGUI *deviceGUI, int newDeviceIndex) { - qDebug("MainWindow::samplingDeviceChanged: deviceType: %d tabIndex: %d newDeviceIndex: %d", - deviceType, tabIndex, newDeviceIndex); - - if (deviceType == 0) { - sampleSourceChanged(tabIndex, newDeviceIndex); - } else if (deviceType == 1) { - sampleSinkChanged(tabIndex, newDeviceIndex); - } else if (deviceType == 2) { - sampleMIMOChanged(tabIndex, newDeviceIndex); - } - - emit MainCore::instance()->deviceChanged(tabIndex); + int deviceType = (int) deviceGUI->getDeviceType(); + int deviceSetIndex = deviceGUI->getIndex(); + Workspace *workspace = m_workspaces[deviceGUI->getWorkspaceIndex()]; + sampleDeviceChange(deviceType, deviceSetIndex, newDeviceIndex, workspace); } -void MainWindow::sampleSourceChanged(int tabIndex, int newDeviceIndex) +void MainWindow::sampleDeviceChange(int deviceType, int deviceSetIndex, int newDeviceIndex, Workspace *workspace) { - if (tabIndex >= 0) + qDebug("MainWindow::sampleDeviceChange: deviceType: %d deviceSetIndex: %d newDeviceIndex: %d", + deviceType, deviceSetIndex, newDeviceIndex); + if (deviceType == 0) { + sampleSourceChange(deviceSetIndex, newDeviceIndex, workspace); + } else if (deviceType == 1) { + sampleSinkChange(deviceSetIndex, newDeviceIndex, workspace); + } else if (deviceType == 2) { + sampleMIMOChange(deviceSetIndex, newDeviceIndex, workspace); + } + + emit MainCore::instance()->deviceChanged(deviceSetIndex); +} + +void MainWindow::sampleSourceChange(int deviceSetIndex, int newDeviceIndex, Workspace *workspace) +{ + if (deviceSetIndex >= 0) { - qDebug("MainWindow::sampleSourceChanged: tab at %d", tabIndex); - DeviceUISet *deviceUI = m_deviceUIs[tabIndex]; - deviceUI->m_deviceAPI->saveSamplingDeviceSettings(m_mainCore->m_settings.getWorkingPreset()); // save old API settings - deviceUI->m_deviceAPI->stopDeviceEngine(); + qDebug("MainWindow::sampleSourceChange: deviceSet %d workspace: %d", deviceSetIndex, workspace->getIndex()); + DeviceUISet *deviceUISet = m_deviceUIs[deviceSetIndex]; + DeviceGUI *gui = deviceUISet->m_deviceGUI; + workspace->removeFromMdiArea(gui); + // deviceUI->m_deviceAPI->saveSamplingDeviceSettings(m_mainCore->m_settings.getWorkingPreset()); // save old API settings + deviceUISet->m_deviceAPI->stopDeviceEngine(); // deletes old UI and input object - deviceUI->m_deviceAPI->getSampleSource()->setMessageQueueToGUI(nullptr); // have source stop sending messages to the GUI - m_deviceUIs[tabIndex]->m_deviceGUI->destroy(); - deviceUI->m_deviceAPI->resetSamplingDeviceId(); - deviceUI->m_deviceAPI->getPluginInterface()->deleteSampleSourcePluginInstanceInput(deviceUI->m_deviceAPI->getSampleSource()); - deviceUI->m_deviceAPI->clearBuddiesLists(); // clear old API buddies lists + deviceUISet->m_deviceAPI->getSampleSource()->setMessageQueueToGUI(nullptr); // have source stop sending messages to the GUI + //deviceUISet->m_deviceGUI->destroy(); + deviceUISet->m_deviceAPI->resetSamplingDeviceId(); + deviceUISet->m_deviceAPI->getPluginInterface()->deleteSampleSourcePluginInstanceInput(deviceUISet->m_deviceAPI->getSampleSource()); + deviceUISet->m_deviceAPI->clearBuddiesLists(); // clear old API buddies lists - const PluginInterface::SamplingDevice *samplingDevice = DeviceEnumerator::instance()->getRxSamplingDevice(newDeviceIndex); - qDebug("MainWindow::sampleSourceChanged: %s", qPrintable(samplingDevice->hardwareId)); - - deviceUI->m_deviceAPI->setSamplingDeviceSequence(samplingDevice->sequence); - deviceUI->m_deviceAPI->setDeviceNbItems(samplingDevice->deviceNbItems); - deviceUI->m_deviceAPI->setDeviceItemIndex(samplingDevice->deviceItemIndex); - deviceUI->m_deviceAPI->setHardwareId(samplingDevice->hardwareId); - deviceUI->m_deviceAPI->setSamplingDeviceId(samplingDevice->id); - deviceUI->m_deviceAPI->setSamplingDeviceSerial(samplingDevice->serial); - deviceUI->m_deviceAPI->setSamplingDeviceDisplayName(samplingDevice->displayedName); - deviceUI->m_deviceAPI->setSamplingDevicePluginInterface(DeviceEnumerator::instance()->getRxPluginInterface(newDeviceIndex)); - - qDebug() << "MainWindow::sampleSourceChanged:" - << "newDeviceIndex:" << newDeviceIndex - << "hardwareId:" << samplingDevice->hardwareId - << "sequence:" << samplingDevice->sequence - << "id:" << samplingDevice->id - << "serial:" << samplingDevice->serial - << "displayedName:" << samplingDevice->displayedName; - - if (deviceUI->m_deviceAPI->getSamplingDeviceId().size() == 0) // non existent device => replace by default - { - qDebug("MainWindow::sampleSourceChanged: non existent device replaced by File Input"); - int fileInputDeviceIndex = DeviceEnumerator::instance()->getFileInputDeviceIndex(); - samplingDevice = DeviceEnumerator::instance()->getRxSamplingDevice(fileInputDeviceIndex); - deviceUI->m_deviceAPI->setSamplingDeviceSequence(samplingDevice->sequence); - deviceUI->m_deviceAPI->setDeviceNbItems(samplingDevice->deviceNbItems); - deviceUI->m_deviceAPI->setDeviceItemIndex(samplingDevice->deviceItemIndex); - deviceUI->m_deviceAPI->setHardwareId(samplingDevice->hardwareId); - deviceUI->m_deviceAPI->setSamplingDeviceId(samplingDevice->id); - deviceUI->m_deviceAPI->setSamplingDeviceSerial(samplingDevice->serial); - deviceUI->m_deviceAPI->setSamplingDeviceDisplayName(samplingDevice->displayedName); - deviceUI->m_deviceAPI->setSamplingDevicePluginInterface(DeviceEnumerator::instance()->getRxPluginInterface(fileInputDeviceIndex)); - } - - QString userArgs = m_mainCore->m_settings.getDeviceUserArgs().findUserArgs(samplingDevice->hardwareId, samplingDevice->sequence); - - if (userArgs.size() > 0) { - deviceUI->m_deviceAPI->setHardwareUserArguments(userArgs); - } - - // add to buddies list - std::vector::iterator it = m_deviceUIs.begin(); - int nbOfBuddies = 0; - - for (; it != m_deviceUIs.end(); ++it) - { - if (*it != deviceUI) // do not add to itself - { - if ((*it)->m_deviceSourceEngine) // it is a source device - { - if ((deviceUI->m_deviceAPI->getHardwareId() == (*it)->m_deviceAPI->getHardwareId()) && - (deviceUI->m_deviceAPI->getSamplingDeviceSerial() == (*it)->m_deviceAPI->getSamplingDeviceSerial())) - { - (*it)->m_deviceAPI->addSourceBuddy(deviceUI->m_deviceAPI); - nbOfBuddies++; - } - } - - if ((*it)->m_deviceSinkEngine) // it is a sink device - { - if ((deviceUI->m_deviceAPI->getHardwareId() == (*it)->m_deviceAPI->getHardwareId()) && - (deviceUI->m_deviceAPI->getSamplingDeviceSerial() == (*it)->m_deviceAPI->getSamplingDeviceSerial())) - { - (*it)->m_deviceAPI->addSourceBuddy(deviceUI->m_deviceAPI); - nbOfBuddies++; - } - } - } - } - - if (nbOfBuddies == 0) { - deviceUI->m_deviceAPI->setBuddyLeader(true); - } - - // constructs new GUI and input object - DeviceSampleSource *source = deviceUI->m_deviceAPI->getPluginInterface()->createSampleSourcePluginInstance( - deviceUI->m_deviceAPI->getSamplingDeviceId(), deviceUI->m_deviceAPI); - deviceUI->m_deviceAPI->setSampleSource(source); - QWidget *gui; - DeviceGUI *pluginGUI = deviceUI->m_deviceAPI->getPluginInterface()->createSampleSourcePluginInstanceGUI( - deviceUI->m_deviceAPI->getSamplingDeviceId(), - &gui, - deviceUI); - deviceUI->m_deviceAPI->getSampleSource()->setMessageQueueToGUI(pluginGUI->getInputMessageQueue()); - deviceUI->m_deviceGUI = pluginGUI; - setDeviceGUI(tabIndex, gui, deviceUI->m_deviceAPI->getSamplingDeviceDisplayName()); - deviceUI->m_deviceAPI->getSampleSource()->init(); - - deviceUI->m_deviceAPI->loadSamplingDeviceSettings(m_mainCore->m_settings.getWorkingPreset()); // load new API settings - - if (tabIndex == 0) // save as default starting device - { - qDebug("MainWindow::sampleSourceChanged: save default starting device %s[%d:%d]", - qPrintable(samplingDevice->id), samplingDevice->sequence, samplingDevice->deviceItemIndex); - m_mainCore->m_settings.setSourceIndex(samplingDevice->sequence); - m_mainCore->m_settings.setSourceDevice(samplingDevice->id); - m_mainCore->m_settings.setSourceItemIndex(samplingDevice->deviceItemIndex); - } + sampleSourceImplement(deviceSetIndex, newDeviceIndex, deviceUISet->m_deviceAPI, deviceUISet, workspace); } } -void MainWindow::sampleSinkChanged(int tabIndex, int newDeviceIndex) +void MainWindow::sampleSinkChange(int deviceSetIndex, int newDeviceIndex, Workspace *workspace) { - if (tabIndex >= 0) + if (deviceSetIndex >= 0) { - qDebug("MainWindow::sampleSinkChanged: tab at %d", tabIndex); - DeviceUISet *deviceUI = m_deviceUIs[tabIndex]; - deviceUI->m_deviceAPI->saveSamplingDeviceSettings(m_mainCore->m_settings.getWorkingPreset()); // save old API settings - deviceUI->m_deviceAPI->stopDeviceEngine(); + qDebug("MainWindow::sampleSinkChange: deviceSet %d workspace: %d", deviceSetIndex, workspace->getIndex()); + DeviceUISet *deviceUISet = m_deviceUIs[deviceSetIndex]; + deviceUISet->m_deviceAPI->saveSamplingDeviceSettings(m_mainCore->m_settings.getWorkingPreset()); // save old API settings + deviceUISet->m_deviceAPI->stopDeviceEngine(); // deletes old UI and output object - deviceUI->m_deviceAPI->getSampleSink()->setMessageQueueToGUI(nullptr); // have sink stop sending messages to the GUI - m_deviceUIs[tabIndex]->m_deviceGUI->destroy(); - deviceUI->m_deviceAPI->resetSamplingDeviceId(); - deviceUI->m_deviceAPI->getPluginInterface()->deleteSampleSinkPluginInstanceOutput(deviceUI->m_deviceAPI->getSampleSink()); - deviceUI->m_deviceAPI->clearBuddiesLists(); // clear old API buddies lists + deviceUISet->m_deviceAPI->getSampleSink()->setMessageQueueToGUI(nullptr); // have sink stop sending messages to the GUI + // m_deviceUIs[deviceSetIndex]->m_deviceGUI->destroy(); + deviceUISet->m_deviceAPI->resetSamplingDeviceId(); + deviceUISet->m_deviceAPI->getPluginInterface()->deleteSampleSinkPluginInstanceOutput(deviceUISet->m_deviceAPI->getSampleSink()); + deviceUISet->m_deviceAPI->clearBuddiesLists(); // clear old API buddies lists - const PluginInterface::SamplingDevice *samplingDevice = DeviceEnumerator::instance()->getTxSamplingDevice(newDeviceIndex); - deviceUI->m_deviceAPI->setSamplingDeviceSequence(samplingDevice->sequence); - deviceUI->m_deviceAPI->setDeviceNbItems(samplingDevice->deviceNbItems); - deviceUI->m_deviceAPI->setDeviceItemIndex(samplingDevice->deviceItemIndex); - deviceUI->m_deviceAPI->setHardwareId(samplingDevice->hardwareId); - deviceUI->m_deviceAPI->setSamplingDeviceId(samplingDevice->id); - deviceUI->m_deviceAPI->setSamplingDeviceSerial(samplingDevice->serial); - deviceUI->m_deviceAPI->setSamplingDeviceDisplayName(samplingDevice->displayedName); - deviceUI->m_deviceAPI->setSamplingDevicePluginInterface(DeviceEnumerator::instance()->getTxPluginInterface(newDeviceIndex)); - - qDebug() << "MainWindow::sampleSinkChanged:" - << "newDeviceIndex:" << newDeviceIndex - << "hardwareId:" << samplingDevice->hardwareId - << "sequence:" << samplingDevice->sequence - << "id:" << samplingDevice->id - << "serial:" << samplingDevice->serial - << "displayedName:" << samplingDevice->displayedName; - - if (deviceUI->m_deviceAPI->getSamplingDeviceId().size() == 0) // non existent device => replace by default - { - qDebug("MainWindow::sampleSinkChanged: non existent device replaced by File Sink"); - int fileSinkDeviceIndex = DeviceEnumerator::instance()->getFileOutputDeviceIndex(); - const PluginInterface::SamplingDevice *samplingDevice = DeviceEnumerator::instance()->getTxSamplingDevice(fileSinkDeviceIndex); - deviceUI->m_deviceAPI->setSamplingDeviceSequence(samplingDevice->sequence); - deviceUI->m_deviceAPI->setDeviceNbItems(samplingDevice->deviceNbItems); - deviceUI->m_deviceAPI->setDeviceItemIndex(samplingDevice->deviceItemIndex); - deviceUI->m_deviceAPI->setHardwareId(samplingDevice->hardwareId); - deviceUI->m_deviceAPI->setSamplingDeviceId(samplingDevice->id); - deviceUI->m_deviceAPI->setSamplingDeviceSerial(samplingDevice->serial); - deviceUI->m_deviceAPI->setSamplingDeviceDisplayName(samplingDevice->displayedName); - deviceUI->m_deviceAPI->setSamplingDevicePluginInterface(DeviceEnumerator::instance()->getTxPluginInterface(fileSinkDeviceIndex)); - } - - QString userArgs = m_mainCore->m_settings.getDeviceUserArgs().findUserArgs(samplingDevice->hardwareId, samplingDevice->sequence); - - if (userArgs.size() > 0) { - deviceUI->m_deviceAPI->setHardwareUserArguments(userArgs); - } - - // add to buddies list - std::vector::iterator it = m_deviceUIs.begin(); - int nbOfBuddies = 0; - - for (; it != m_deviceUIs.end(); ++it) - { - if (*it != deviceUI) // do not add to itself - { - if ((*it)->m_deviceSourceEngine) // it is a source device - { - if ((deviceUI->m_deviceAPI->getHardwareId() == (*it)->m_deviceAPI->getHardwareId()) && - (deviceUI->m_deviceAPI->getSamplingDeviceSerial() == (*it)->m_deviceAPI->getSamplingDeviceSerial())) - { - (*it)->m_deviceAPI->addSinkBuddy(deviceUI->m_deviceAPI); - nbOfBuddies++; - } - } - - if ((*it)->m_deviceSinkEngine) // it is a sink device - { - if ((deviceUI->m_deviceAPI->getHardwareId() == (*it)->m_deviceAPI->getHardwareId()) && - (deviceUI->m_deviceAPI->getSamplingDeviceSerial() == (*it)->m_deviceAPI->getSamplingDeviceSerial())) - { - (*it)->m_deviceAPI->addSinkBuddy(deviceUI->m_deviceAPI); - nbOfBuddies++; - } - } - } - } - - if (nbOfBuddies == 0) { - deviceUI->m_deviceAPI->setBuddyLeader(true); - } - - // constructs new GUI and output object - DeviceSampleSink *sink = deviceUI->m_deviceAPI->getPluginInterface()->createSampleSinkPluginInstance( - deviceUI->m_deviceAPI->getSamplingDeviceId(), deviceUI->m_deviceAPI); - deviceUI->m_deviceAPI->setSampleSink(sink); - QWidget *gui; - DeviceGUI *pluginGUI = deviceUI->m_deviceAPI->getPluginInterface()->createSampleSinkPluginInstanceGUI( - deviceUI->m_deviceAPI->getSamplingDeviceId(), - &gui, - deviceUI); - deviceUI->m_deviceAPI->getSampleSink()->setMessageQueueToGUI(pluginGUI->getInputMessageQueue()); - deviceUI->m_deviceGUI = pluginGUI; - setDeviceGUI(tabIndex, gui, deviceUI->m_deviceAPI->getSamplingDeviceDisplayName(), 1); - deviceUI->m_deviceAPI->getSampleSink()->init(); - - deviceUI->m_deviceAPI->loadSamplingDeviceSettings(m_mainCore->m_settings.getWorkingPreset()); // load new API settings + sampleSinkImplement(deviceSetIndex, newDeviceIndex, deviceUISet->m_deviceAPI, deviceUISet, workspace); } } -void MainWindow::sampleMIMOChanged(int tabIndex, int newDeviceIndex) +void MainWindow::sampleMIMOChange(int deviceSetIndex, int newDeviceIndex, Workspace *workspace) { - if (tabIndex >= 0) + if (deviceSetIndex >= 0) { - qDebug("MainWindow::sampleMIMOChanged: tab at %d", tabIndex); - DeviceUISet *deviceUI = m_deviceUIs[tabIndex]; - deviceUI->m_deviceAPI->saveSamplingDeviceSettings(m_mainCore->m_settings.getWorkingPreset()); // save old API settings - deviceUI->m_deviceAPI->stopDeviceEngine(); + qDebug("MainWindow::sampleSinkChange: deviceSet %d workspace: %d", deviceSetIndex, workspace->getIndex()); + DeviceUISet *deviceUISet = m_deviceUIs[deviceSetIndex]; + deviceUISet->m_deviceAPI->saveSamplingDeviceSettings(m_mainCore->m_settings.getWorkingPreset()); // save old API settings + deviceUISet->m_deviceAPI->stopDeviceEngine(); // deletes old UI and output object - deviceUI->m_deviceAPI->getSampleMIMO()->setMessageQueueToGUI(nullptr); // have sink stop sending messages to the GUI - m_deviceUIs[tabIndex]->m_deviceGUI->destroy(); - deviceUI->m_deviceAPI->resetSamplingDeviceId(); - deviceUI->m_deviceAPI->getPluginInterface()->deleteSampleMIMOPluginInstanceMIMO(deviceUI->m_deviceAPI->getSampleMIMO()); + deviceUISet->m_deviceAPI->getSampleMIMO()->setMessageQueueToGUI(nullptr); // have sink stop sending messages to the GUI + // m_deviceUIs[tabIndex]->m_deviceGUI->destroy(); + deviceUISet->m_deviceAPI->resetSamplingDeviceId(); + deviceUISet->m_deviceAPI->getPluginInterface()->deleteSampleMIMOPluginInstanceMIMO(deviceUISet->m_deviceAPI->getSampleMIMO()); - const PluginInterface::SamplingDevice *samplingDevice = DeviceEnumerator::instance()->getMIMOSamplingDevice(newDeviceIndex); - deviceUI->m_deviceAPI->setSamplingDeviceSequence(samplingDevice->sequence); - deviceUI->m_deviceAPI->setDeviceNbItems(samplingDevice->deviceNbItems); - deviceUI->m_deviceAPI->setDeviceItemIndex(samplingDevice->deviceItemIndex); - deviceUI->m_deviceAPI->setHardwareId(samplingDevice->hardwareId); - deviceUI->m_deviceAPI->setSamplingDeviceId(samplingDevice->id); - deviceUI->m_deviceAPI->setSamplingDeviceSerial(samplingDevice->serial); - deviceUI->m_deviceAPI->setSamplingDeviceDisplayName(samplingDevice->displayedName); - deviceUI->m_deviceAPI->setSamplingDevicePluginInterface(DeviceEnumerator::instance()->getMIMOPluginInterface(newDeviceIndex)); - qDebug() << "MainWindow::sampleMIMOChanged:" - << "newDeviceIndex:" << newDeviceIndex - << "hardwareId:" << samplingDevice->hardwareId - << "sequence:" << samplingDevice->sequence - << "id:" << samplingDevice->id - << "serial:" << samplingDevice->serial - << "displayedName:" << samplingDevice->displayedName; - - if (deviceUI->m_deviceAPI->getSamplingDeviceId().size() == 0) // non existent device => replace by default - { - qDebug("MainWindow::sampleMIMOChanged: non existent device replaced by Test MIMO"); - int testMIMODeviceIndex = DeviceEnumerator::instance()->getTestMIMODeviceIndex(); - const PluginInterface::SamplingDevice *samplingDevice = DeviceEnumerator::instance()->getMIMOSamplingDevice(testMIMODeviceIndex); - deviceUI->m_deviceAPI->setSamplingDeviceSequence(samplingDevice->sequence); - deviceUI->m_deviceAPI->setDeviceNbItems(samplingDevice->deviceNbItems); - deviceUI->m_deviceAPI->setDeviceItemIndex(samplingDevice->deviceItemIndex); - deviceUI->m_deviceAPI->setHardwareId(samplingDevice->hardwareId); - deviceUI->m_deviceAPI->setSamplingDeviceId(samplingDevice->id); - deviceUI->m_deviceAPI->setSamplingDeviceSerial(samplingDevice->serial); - deviceUI->m_deviceAPI->setSamplingDeviceDisplayName(samplingDevice->displayedName); - deviceUI->m_deviceAPI->setSamplingDevicePluginInterface(DeviceEnumerator::instance()->getMIMOPluginInterface(testMIMODeviceIndex)); - } - - QString userArgs = m_mainCore->m_settings.getDeviceUserArgs().findUserArgs(samplingDevice->hardwareId, samplingDevice->sequence); - - if (userArgs.size() > 0) { - deviceUI->m_deviceAPI->setHardwareUserArguments(userArgs); - } - - // constructs new GUI and MIMO object - DeviceSampleMIMO *mimo = deviceUI->m_deviceAPI->getPluginInterface()->createSampleMIMOPluginInstance( - deviceUI->m_deviceAPI->getSamplingDeviceId(), deviceUI->m_deviceAPI); - deviceUI->m_deviceAPI->setSampleMIMO(mimo); - QWidget *gui; - DeviceGUI *pluginGUI = deviceUI->m_deviceAPI->getPluginInterface()->createSampleMIMOPluginInstanceGUI( - deviceUI->m_deviceAPI->getSamplingDeviceId(), - &gui, - deviceUI); - deviceUI->m_deviceAPI->getSampleMIMO()->setMessageQueueToGUI(pluginGUI->getInputMessageQueue()); - deviceUI->m_deviceGUI = pluginGUI; - setDeviceGUI(tabIndex, gui, deviceUI->m_deviceAPI->getSamplingDeviceDisplayName(), 2); - deviceUI->m_deviceAPI->getSampleMIMO()->init(); - - deviceUI->m_deviceAPI->loadSamplingDeviceSettings(m_mainCore->m_settings.getWorkingPreset()); // load new API settings + sampleMIMOImplement(deviceSetIndex, deviceSetIndex, deviceUISet->m_deviceAPI, deviceUISet, workspace); } } @@ -2428,6 +2370,19 @@ void MainWindow::featureMove(FeatureGUI *gui, int wsIndexDestnation) m_workspaces[wsIndexDestnation]->addToMdiArea(gui); } +void MainWindow::deviceMove(DeviceGUI *gui, int wsIndexDestnation) +{ + int wsIndexOrigin = gui->getWorkspaceIndex(); + + if (wsIndexOrigin == wsIndexDestnation) { + return; + } + + m_workspaces[wsIndexOrigin]->removeFromMdiArea(gui); + gui->setWorkspaceIndex(wsIndexDestnation); + m_workspaces[wsIndexDestnation]->addToMdiArea(gui); +} + void MainWindow::openFeaturePresetsDialog(QPoint p, Workspace *workspace) { FeaturePresetsDialog dialog; @@ -2470,25 +2425,6 @@ void MainWindow::on_action_About_triggered() dlg.exec(); } -void MainWindow::on_action_addSourceDevice_triggered() -{ - addSourceDevice(-1); // create with file source device by default -} - -void MainWindow::on_action_addSinkDevice_triggered() -{ - addSinkDevice(); -} - -void MainWindow::on_action_addMIMODevice_triggered() -{ - if (m_dspEngine->getMIMOSupport()) { - addMIMODevice(); - } else { - QMessageBox::information(this, tr("Message"), tr("MIMO not supported in this version")); - } -} - void MainWindow::on_action_removeLastDevice_triggered() { if (m_deviceUIs.size() > 1) @@ -2563,12 +2499,6 @@ void MainWindow::tabChannelsIndexChanged() // } } -void MainWindow::tabFeaturesIndexChanged() -{ - // int featuresTabIndex = ui->tabFeatures->currentIndex(); - // ui->featureDock->setFeatureUISet(m_featureUIs[featuresTabIndex]); -} - void MainWindow::updateStatus() { m_dateTimeWidget->setText(QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss t")); @@ -2593,15 +2523,3 @@ void MainWindow::commandKeyPressed(Qt::Key key, Qt::KeyboardModifiers keyModifie } } } - -void MainWindow::restoreDeviceTabs() -{ - // ui->tabInputsView->clear(); - - // for (int i = 0; i < m_deviceWidgetTabs.size(); i++) - // { - // qDebug("MainWindow::restoreDeviceTabs: adding tab for %s", qPrintable(m_deviceWidgetTabs[i].displayName)); - // ui->tabInputsView->addTab(m_deviceWidgetTabs[i].gui, m_deviceWidgetTabs[i].tabName); - // ui->tabInputsView->setTabToolTip(i, m_deviceWidgetTabs[i].displayName); - // } -} diff --git a/sdrgui/mainwindow.h b/sdrgui/mainwindow.h index 0481bc8e0..65d2673b8 100644 --- a/sdrgui/mainwindow.h +++ b/sdrgui/mainwindow.h @@ -72,7 +72,6 @@ public: ~MainWindow(); static MainWindow *getInstance() { return m_instance; } // Main Window is de facto a singleton so this just returns its reference MessageQueue* getInputMessageQueue() { return &m_inputMessageQueue; } - void addViewAction(QAction* action); const PluginManager *getPluginManager() const { return m_pluginManager; } std::vector& getDeviceUISets() { return m_deviceUIs; } void commandKeysConnect(QObject *object, const char *slot); @@ -135,25 +134,41 @@ private: void closeEvent(QCloseEvent*); void updatePresetControls(); QTreeWidgetItem* addPresetToTree(const Preset* preset); - QTreeWidgetItem* addCommandToTree(const Command* command); void applySettings(); - void setDeviceGUI(int deviceTabIndex, QWidget* gui, const QString& deviceDisplayName, int deviceType = 0); - void addSourceDevice(int deviceIndex); - void addSinkDevice(); - void addMIMODevice(); void removeLastDevice(); void addFeatureSet(); void removeFeatureSet(unsigned int tabIndex); void removeAllFeatureSets(); void deleteChannel(int deviceSetIndex, int channelIndex); - void sampleSourceChanged(int tabIndex, int newDeviceIndex); - void sampleSinkChanged(int tabIndex, int newDeviceIndex); - void sampleMIMOChanged(int tabIndex, int newDeviceIndex); + void sampleDeviceChange(int deviceType, int deviceSetIndex, int newDeviceIndex, Workspace *workspace); + void sampleSourceChange(int deviceSetIndex, int newDeviceIndex, Workspace *workspace); + void sampleSinkChange(int deviceSetIndex, int newDeviceIndex, Workspace *workspace); + void sampleMIMOChange(int deviceSetIndex, int newDeviceIndex, Workspace *workspace); + void sampleSourceImplement( + int deviceSetIndex, + int deviceIndex, + DeviceAPI *deviceAPI, + DeviceUISet *deviceUISet, + Workspace *workspace + ); + void sampleSinkImplement( + int deviceSetIndex, + int deviceIndex, + DeviceAPI *deviceAPI, + DeviceUISet *deviceUISet, + Workspace *workspace + ); + void sampleMIMOImplement( + int deviceSetIndex, + int deviceIndex, + DeviceAPI *deviceAPI, + DeviceUISet *deviceUISet, + Workspace *workspace + ); void deleteFeature(int featureSetIndex, int featureIndex); bool handleMessage(const Message& cmd); - void restoreDeviceTabs(); private slots: void handleMessages(); @@ -164,6 +179,10 @@ private slots: void removeEmptyWorkspaces(); void loadConfiguration(const Configuration *configuration); void saveConfiguration(Configuration *configuration); + void sampleSourceAdd(Workspace *workspace, int deviceIndex); + void sampleSinkAdd(Workspace *workspace, int deviceIndex); + void sampleMIMOAdd(Workspace *workspace, int deviceIndex); + void sampleDeviceChangeHandler(DeviceGUI *deviceGUI, int newDeviceIndex); void on_action_View_Fullscreen_toggled(bool checked); void on_presetSave_clicked(); @@ -185,27 +204,22 @@ private slots: void on_action_My_Position_triggered(); void on_action_DeviceUserArguments_triggered(); void on_action_commands_triggered(); - void samplingDeviceChanged(int deviceType, int tabIndex, int newDeviceIndex); void channelAddClicked(int channelIndex); void featureAddClicked(Workspace *workspace, int featureIndex); void featureMove(FeatureGUI *gui, int wsIndexDestnation); void openFeaturePresetsDialog(QPoint p, Workspace *workspace); + void deviceMove(DeviceGUI *gui, int wsIndexDestnation); void on_action_Quick_Start_triggered(); void on_action_Main_Window_triggered(); void on_action_Loaded_Plugins_triggered(); void on_action_About_triggered(); - void on_action_addSourceDevice_triggered(); - void on_action_addSinkDevice_triggered(); - void on_action_addMIMODevice_triggered(); void on_action_removeLastDevice_triggered(); void on_action_addFeatureSet_triggered(); void on_action_removeLastFeatureSet_triggered(); void tabInputViewIndexChanged(); void tabChannelsIndexChanged(); - void tabFeaturesIndexChanged(); void commandKeyPressed(Qt::Key key, Qt::KeyboardModifiers keyModifiers, bool release); void fftWisdomProcessFinished(int exitCode, QProcess::ExitStatus exitStatus); - // void toggleSpectrumView(bool checked); }; #endif // INCLUDE_MAINWINDOW_H From 0ca0eb8f733d9cc739621c4b5612f253bb3a0155 Mon Sep 17 00:00:00 2001 From: f4exb Date: Sat, 9 Apr 2022 13:38:22 +0200 Subject: [PATCH 005/115] Massive UI revamping (v7): devices intermediate --- doc/img/channel.xcf | Bin 0 -> 36585 bytes doc/img/tool.xcf | Bin 14271 -> 27899 bytes .../bladerf2mimo/bladerf2mimogui.ui | 2 +- .../samplemimo/limesdrmimo/limesdrmimogui.ui | 2 +- plugins/samplemimo/metismiso/metismisogui.ui | 2 +- .../plutosdrmimo/plutosdrmimogui.ui | 2 +- plugins/samplemimo/testmi/testmigui.ui | 2 +- .../samplemimo/testmosync/testmosyncgui.ui | 2 +- plugins/samplemimo/xtrxmimo/xtrxmimogui.ui | 2 +- .../bladerf1output/bladerf1outputgui.ui | 2 +- .../bladerf2output/bladerf2outputgui.ui | 2 +- .../samplesink/fileoutput/fileoutputgui.ui | 2 +- .../hackrfoutput/hackrfoutputgui.ui | 2 +- .../limesdroutput/limesdroutputgui.ui | 2 +- .../plutosdroutput/plutosdroutputgui.ui | 2 +- .../soapysdroutput/soapysdroutputgui.ui | 2 +- plugins/samplesink/testsink/testsinkgui.ui | 2 +- .../samplesink/usrpoutput/usrpoutputgui.ui | 2 +- .../samplesink/xtrxoutput/xtrxoutputgui.ui | 2 +- plugins/samplesource/airspy/airspygui.ui | 2 +- plugins/samplesource/airspyhf/airspyhfgui.ui | 2 +- .../bladerf1input/bladerf1inputgui.ui | 2 +- .../bladerf2input/bladerf2inputgui.ui | 2 +- plugins/samplesource/fcdpro/fcdprogui.ui | 2 +- .../samplesource/fcdproplus/fcdproplusgui.ui | 2 +- .../hackrfinput/hackrfinputgui.ui | 29 +- plugins/samplesource/kiwisdr/kiwisdrgui.ui | 2 +- .../limesdrinput/limesdrinputgui.ui | 2 +- plugins/samplesource/perseus/perseusgui.ui | 2 +- .../plutosdrinput/plutosdrinputgui.ui | 2 +- plugins/samplesource/rtlsdr/rtlsdrgui.ui | 2 +- plugins/samplesource/sdrplay/sdrplaygui.ui | 2 +- .../samplesource/sdrplayv3/sdrplayv3gui.ui | 2 +- .../soapysdrinput/soapysdrinputgui.ui | 2 +- .../samplesource/testsource/testsourcegui.ui | 2 +- .../samplesource/usrpinput/usrpinputgui.ui | 2 +- .../samplesource/xtrxinput/xtrxinputgui.ui | 2 +- sdrbase/device/deviceenumerator.cpp | 89 +++++ sdrbase/device/deviceenumerator.h | 10 + sdrbase/settings/configuration.cpp | 28 +- sdrbase/settings/configuration.h | 8 +- sdrbase/settings/preset.cpp | 63 ++-- sdrbase/settings/preset.h | 33 +- sdrgui/CMakeLists.txt | 4 + sdrgui/device/devicegui.cpp | 36 +- sdrgui/device/devicegui.h | 3 + sdrgui/device/deviceuiset.cpp | 73 +++- sdrgui/device/deviceuiset.h | 33 +- sdrgui/feature/featuregui.cpp | 10 +- sdrgui/feature/featureuiset.cpp | 70 +--- sdrgui/feature/featureuiset.h | 9 +- sdrgui/gui/featurepresetsdialog.cpp | 4 +- sdrgui/gui/featurepresetsdialog.h | 6 +- sdrgui/gui/glspectrum.cpp | 7 +- sdrgui/gui/workspace.cpp | 4 +- sdrgui/mainspectrum/mainspectrumgui.cpp | 290 ++++++++++++++++ sdrgui/mainspectrum/mainspectrumgui.h | 110 ++++++ sdrgui/mainwindow.cpp | 317 ++++++++++++------ sdrgui/mainwindow.h | 21 +- sdrgui/resources/channels.png | Bin 0 -> 4926 bytes sdrgui/resources/channels_add.png | Bin 0 -> 5398 bytes sdrgui/resources/res.qrc | 4 + sdrgui/resources/tool_add.png | Bin 0 -> 5953 bytes sdrgui/resources/tool_star.png | Bin 0 -> 5865 bytes 64 files changed, 1039 insertions(+), 290 deletions(-) create mode 100644 doc/img/channel.xcf create mode 100644 sdrgui/mainspectrum/mainspectrumgui.cpp create mode 100644 sdrgui/mainspectrum/mainspectrumgui.h create mode 100644 sdrgui/resources/channels.png create mode 100644 sdrgui/resources/channels_add.png create mode 100644 sdrgui/resources/tool_add.png create mode 100644 sdrgui/resources/tool_star.png diff --git a/doc/img/channel.xcf b/doc/img/channel.xcf new file mode 100644 index 0000000000000000000000000000000000000000..61cbff5430bf3e8105778263b575fdae6730e9e6 GIT binary patch literal 36585 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 literal 0 HcmV?d00001 diff --git a/doc/img/tool.xcf b/doc/img/tool.xcf index 94adf6f710ea71d6fc15270333fc4d47a71e016a..5d181b4c1c47ab65ae6c29fb9171a0f48fac5cbe 100644 GIT binary patch delta 14000 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 delta 653 zcmXxgODIH97zglkZu2mCn`SZ?#vtR>y~8|AifA_Mu$XRa5Ji(IuLbEwSxM+FEHrrx zvA-*&Bx|LtX2ZrbYhfWN=g$B0)z|O-zjI&b{ABrE>R`wvF~;1YEuy(-7h}@nVIt3S z#sH6VYmcJHK|ReIs?@RBCHV^NTd>GOE|5#)I=M~WLPri3hhWJyEZZR8$?t+1D>t_- zszQgAAJBOQT{3h(!0K^wf}DY#Pgwf~>qek=4%XAUZy(urWU4XdJHen)oV{88JZ!pv z&9sx2EOP5P3~Z7+Bpr0%1-Wevws*pgFW8lVa#AS1&y*Mj${;yRj*==2(vNiiB8R47 dm|h&FdBlo&q?DwMMZA=6V04l( Liberation Mono - 20 + 16
diff --git a/plugins/samplemimo/limesdrmimo/limesdrmimogui.ui b/plugins/samplemimo/limesdrmimo/limesdrmimogui.ui index 818a5d5cd..bdc8329e5 100644 --- a/plugins/samplemimo/limesdrmimo/limesdrmimogui.ui +++ b/plugins/samplemimo/limesdrmimo/limesdrmimogui.ui @@ -336,7 +336,7 @@ Liberation Mono - 20 + 16 50 false diff --git a/plugins/samplemimo/metismiso/metismisogui.ui b/plugins/samplemimo/metismiso/metismisogui.ui index df0c87514..bf3a59e48 100644 --- a/plugins/samplemimo/metismiso/metismisogui.ui +++ b/plugins/samplemimo/metismiso/metismisogui.ui @@ -306,7 +306,7 @@ Liberation Mono - 20 + 16 50 false false diff --git a/plugins/samplemimo/plutosdrmimo/plutosdrmimogui.ui b/plugins/samplemimo/plutosdrmimo/plutosdrmimogui.ui index 5434ff697..2f0ef9104 100644 --- a/plugins/samplemimo/plutosdrmimo/plutosdrmimogui.ui +++ b/plugins/samplemimo/plutosdrmimo/plutosdrmimogui.ui @@ -313,7 +313,7 @@ Liberation Mono - 20 + 16 diff --git a/plugins/samplemimo/testmi/testmigui.ui b/plugins/samplemimo/testmi/testmigui.ui index 078537460..0baf16c66 100644 --- a/plugins/samplemimo/testmi/testmigui.ui +++ b/plugins/samplemimo/testmi/testmigui.ui @@ -223,7 +223,7 @@ Liberation Mono - 20 + 16 50 false false diff --git a/plugins/samplemimo/testmosync/testmosyncgui.ui b/plugins/samplemimo/testmosync/testmosyncgui.ui index 104df588a..e80a8714b 100644 --- a/plugins/samplemimo/testmosync/testmosyncgui.ui +++ b/plugins/samplemimo/testmosync/testmosyncgui.ui @@ -131,7 +131,7 @@ Liberation Mono - 20 + 16 diff --git a/plugins/samplemimo/xtrxmimo/xtrxmimogui.ui b/plugins/samplemimo/xtrxmimo/xtrxmimogui.ui index c59b43367..1cf55b59d 100644 --- a/plugins/samplemimo/xtrxmimo/xtrxmimogui.ui +++ b/plugins/samplemimo/xtrxmimo/xtrxmimogui.ui @@ -336,7 +336,7 @@ Liberation Mono - 20 + 16 50 false diff --git a/plugins/samplesink/bladerf1output/bladerf1outputgui.ui b/plugins/samplesink/bladerf1output/bladerf1outputgui.ui index d8260c1d4..2c2a63bc9 100644 --- a/plugins/samplesink/bladerf1output/bladerf1outputgui.ui +++ b/plugins/samplesink/bladerf1output/bladerf1outputgui.ui @@ -128,7 +128,7 @@ Liberation Mono - 20 + 16 diff --git a/plugins/samplesink/bladerf2output/bladerf2outputgui.ui b/plugins/samplesink/bladerf2output/bladerf2outputgui.ui index a589a1d9c..fe54b2964 100644 --- a/plugins/samplesink/bladerf2output/bladerf2outputgui.ui +++ b/plugins/samplesink/bladerf2output/bladerf2outputgui.ui @@ -128,7 +128,7 @@ Liberation Mono - 20 + 16 diff --git a/plugins/samplesink/fileoutput/fileoutputgui.ui b/plugins/samplesink/fileoutput/fileoutputgui.ui index 82c4c8094..a6c19111a 100644 --- a/plugins/samplesink/fileoutput/fileoutputgui.ui +++ b/plugins/samplesink/fileoutput/fileoutputgui.ui @@ -131,7 +131,7 @@ Liberation Mono - 20 + 16 diff --git a/plugins/samplesink/hackrfoutput/hackrfoutputgui.ui b/plugins/samplesink/hackrfoutput/hackrfoutputgui.ui index 8b9e1e455..b27ca6e16 100644 --- a/plugins/samplesink/hackrfoutput/hackrfoutputgui.ui +++ b/plugins/samplesink/hackrfoutput/hackrfoutputgui.ui @@ -131,7 +131,7 @@ Liberation Mono - 20 + 16 diff --git a/plugins/samplesink/limesdroutput/limesdroutputgui.ui b/plugins/samplesink/limesdroutput/limesdroutputgui.ui index 7fe2c2758..32b2efb93 100644 --- a/plugins/samplesink/limesdroutput/limesdroutputgui.ui +++ b/plugins/samplesink/limesdroutput/limesdroutputgui.ui @@ -128,7 +128,7 @@ Liberation Mono - 20 + 16 diff --git a/plugins/samplesink/plutosdroutput/plutosdroutputgui.ui b/plugins/samplesink/plutosdroutput/plutosdroutputgui.ui index 8dc5b657d..2220656be 100644 --- a/plugins/samplesink/plutosdroutput/plutosdroutputgui.ui +++ b/plugins/samplesink/plutosdroutput/plutosdroutputgui.ui @@ -128,7 +128,7 @@ Liberation Mono - 20 + 16 50 false diff --git a/plugins/samplesink/soapysdroutput/soapysdroutputgui.ui b/plugins/samplesink/soapysdroutput/soapysdroutputgui.ui index 985bb5c90..4ae211477 100644 --- a/plugins/samplesink/soapysdroutput/soapysdroutputgui.ui +++ b/plugins/samplesink/soapysdroutput/soapysdroutputgui.ui @@ -116,7 +116,7 @@ Liberation Mono - 20 + 16 diff --git a/plugins/samplesink/testsink/testsinkgui.ui b/plugins/samplesink/testsink/testsinkgui.ui index 1d491e993..65c8354e8 100644 --- a/plugins/samplesink/testsink/testsinkgui.ui +++ b/plugins/samplesink/testsink/testsinkgui.ui @@ -131,7 +131,7 @@ Liberation Mono - 20 + 16 diff --git a/plugins/samplesink/usrpoutput/usrpoutputgui.ui b/plugins/samplesink/usrpoutput/usrpoutputgui.ui index 3e0d3ba25..fb66c98d9 100644 --- a/plugins/samplesink/usrpoutput/usrpoutputgui.ui +++ b/plugins/samplesink/usrpoutput/usrpoutputgui.ui @@ -138,7 +138,7 @@ Liberation Mono - 20 + 16 diff --git a/plugins/samplesink/xtrxoutput/xtrxoutputgui.ui b/plugins/samplesink/xtrxoutput/xtrxoutputgui.ui index c04dd6267..349c33dda 100644 --- a/plugins/samplesink/xtrxoutput/xtrxoutputgui.ui +++ b/plugins/samplesink/xtrxoutput/xtrxoutputgui.ui @@ -128,7 +128,7 @@ Liberation Mono - 20 + 16 50 false diff --git a/plugins/samplesource/airspy/airspygui.ui b/plugins/samplesource/airspy/airspygui.ui index 1cf50415d..cd4ea6f06 100644 --- a/plugins/samplesource/airspy/airspygui.ui +++ b/plugins/samplesource/airspy/airspygui.ui @@ -122,7 +122,7 @@ Liberation Mono - 20 + 16 diff --git a/plugins/samplesource/airspyhf/airspyhfgui.ui b/plugins/samplesource/airspyhf/airspyhfgui.ui index 40463d73b..c19aae71c 100644 --- a/plugins/samplesource/airspyhf/airspyhfgui.ui +++ b/plugins/samplesource/airspyhf/airspyhfgui.ui @@ -122,7 +122,7 @@ Liberation Mono - 20 + 16 diff --git a/plugins/samplesource/bladerf1input/bladerf1inputgui.ui b/plugins/samplesource/bladerf1input/bladerf1inputgui.ui index c0321330a..531eb3311 100644 --- a/plugins/samplesource/bladerf1input/bladerf1inputgui.ui +++ b/plugins/samplesource/bladerf1input/bladerf1inputgui.ui @@ -122,7 +122,7 @@ Liberation Mono - 20 + 16 diff --git a/plugins/samplesource/bladerf2input/bladerf2inputgui.ui b/plugins/samplesource/bladerf2input/bladerf2inputgui.ui index 7ec663a32..03fd1f2e3 100644 --- a/plugins/samplesource/bladerf2input/bladerf2inputgui.ui +++ b/plugins/samplesource/bladerf2input/bladerf2inputgui.ui @@ -122,7 +122,7 @@ Liberation Mono - 20 + 16 diff --git a/plugins/samplesource/fcdpro/fcdprogui.ui b/plugins/samplesource/fcdpro/fcdprogui.ui index feae1eeb4..7af12e90b 100644 --- a/plugins/samplesource/fcdpro/fcdprogui.ui +++ b/plugins/samplesource/fcdpro/fcdprogui.ui @@ -122,7 +122,7 @@ Liberation Mono - 20 + 16 diff --git a/plugins/samplesource/fcdproplus/fcdproplusgui.ui b/plugins/samplesource/fcdproplus/fcdproplusgui.ui index 8c414c5ed..319ff8471 100644 --- a/plugins/samplesource/fcdproplus/fcdproplusgui.ui +++ b/plugins/samplesource/fcdproplus/fcdproplusgui.ui @@ -122,7 +122,7 @@ Liberation Mono - 20 + 16 diff --git a/plugins/samplesource/hackrfinput/hackrfinputgui.ui b/plugins/samplesource/hackrfinput/hackrfinputgui.ui index 65d063e37..d28afc6ba 100644 --- a/plugins/samplesource/hackrfinput/hackrfinputgui.ui +++ b/plugins/samplesource/hackrfinput/hackrfinputgui.ui @@ -6,8 +6,8 @@ 0 0 - 310 - 300 + 360 + 272 @@ -18,8 +18,8 @@ - 310 - 300 + 360 + 0 @@ -122,7 +122,7 @@ Liberation Mono - 20 + 16 @@ -243,7 +243,7 @@ - + Bandpass Filter auto select @@ -658,23 +658,6 @@ - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - diff --git a/plugins/samplesource/kiwisdr/kiwisdrgui.ui b/plugins/samplesource/kiwisdr/kiwisdrgui.ui index bbf6bec05..16a4c787d 100644 --- a/plugins/samplesource/kiwisdr/kiwisdrgui.ui +++ b/plugins/samplesource/kiwisdr/kiwisdrgui.ui @@ -131,7 +131,7 @@ Liberation Mono - 20 + 16 diff --git a/plugins/samplesource/limesdrinput/limesdrinputgui.ui b/plugins/samplesource/limesdrinput/limesdrinputgui.ui index 2d9d98b37..8bb19d189 100644 --- a/plugins/samplesource/limesdrinput/limesdrinputgui.ui +++ b/plugins/samplesource/limesdrinput/limesdrinputgui.ui @@ -128,7 +128,7 @@ Liberation Mono - 20 + 16 50 false diff --git a/plugins/samplesource/perseus/perseusgui.ui b/plugins/samplesource/perseus/perseusgui.ui index 1d457ba29..c06331a1d 100644 --- a/plugins/samplesource/perseus/perseusgui.ui +++ b/plugins/samplesource/perseus/perseusgui.ui @@ -122,7 +122,7 @@ Liberation Mono - 20 + 16 diff --git a/plugins/samplesource/plutosdrinput/plutosdrinputgui.ui b/plugins/samplesource/plutosdrinput/plutosdrinputgui.ui index 336794851..709d10937 100644 --- a/plugins/samplesource/plutosdrinput/plutosdrinputgui.ui +++ b/plugins/samplesource/plutosdrinput/plutosdrinputgui.ui @@ -128,7 +128,7 @@ Liberation Mono - 20 + 16 diff --git a/plugins/samplesource/rtlsdr/rtlsdrgui.ui b/plugins/samplesource/rtlsdr/rtlsdrgui.ui index d4a6d75af..df02e0cd5 100644 --- a/plugins/samplesource/rtlsdr/rtlsdrgui.ui +++ b/plugins/samplesource/rtlsdr/rtlsdrgui.ui @@ -122,7 +122,7 @@ Liberation Mono - 20 + 16 diff --git a/plugins/samplesource/sdrplay/sdrplaygui.ui b/plugins/samplesource/sdrplay/sdrplaygui.ui index e9d07e273..f22caa9a8 100644 --- a/plugins/samplesource/sdrplay/sdrplaygui.ui +++ b/plugins/samplesource/sdrplay/sdrplaygui.ui @@ -122,7 +122,7 @@ Liberation Mono - 20 + 16 diff --git a/plugins/samplesource/sdrplayv3/sdrplayv3gui.ui b/plugins/samplesource/sdrplayv3/sdrplayv3gui.ui index f4665f4f5..bbf4c43c0 100644 --- a/plugins/samplesource/sdrplayv3/sdrplayv3gui.ui +++ b/plugins/samplesource/sdrplayv3/sdrplayv3gui.ui @@ -122,7 +122,7 @@ Liberation Mono - 20 + 16 diff --git a/plugins/samplesource/soapysdrinput/soapysdrinputgui.ui b/plugins/samplesource/soapysdrinput/soapysdrinputgui.ui index ee59b8d69..f0781845a 100644 --- a/plugins/samplesource/soapysdrinput/soapysdrinputgui.ui +++ b/plugins/samplesource/soapysdrinput/soapysdrinputgui.ui @@ -116,7 +116,7 @@ Liberation Mono - 20 + 16 diff --git a/plugins/samplesource/testsource/testsourcegui.ui b/plugins/samplesource/testsource/testsourcegui.ui index d5768cdd8..ce3915af9 100644 --- a/plugins/samplesource/testsource/testsourcegui.ui +++ b/plugins/samplesource/testsource/testsourcegui.ui @@ -131,7 +131,7 @@ Liberation Mono - 20 + 16 diff --git a/plugins/samplesource/usrpinput/usrpinputgui.ui b/plugins/samplesource/usrpinput/usrpinputgui.ui index 80be6c42a..bbdafa362 100644 --- a/plugins/samplesource/usrpinput/usrpinputgui.ui +++ b/plugins/samplesource/usrpinput/usrpinputgui.ui @@ -138,7 +138,7 @@ Liberation Mono - 20 + 16 50 false diff --git a/plugins/samplesource/xtrxinput/xtrxinputgui.ui b/plugins/samplesource/xtrxinput/xtrxinputgui.ui index de4b9edce..8078a54e3 100644 --- a/plugins/samplesource/xtrxinput/xtrxinputgui.ui +++ b/plugins/samplesource/xtrxinput/xtrxinputgui.ui @@ -128,7 +128,7 @@ Liberation Mono - 20 + 16 50 false diff --git a/sdrbase/device/deviceenumerator.cpp b/sdrbase/device/deviceenumerator.cpp index c0ddec020..f4ecd56c9 100644 --- a/sdrbase/device/deviceenumerator.cpp +++ b/sdrbase/device/deviceenumerator.cpp @@ -506,3 +506,92 @@ int DeviceEnumerator::getMIMOSamplingDeviceIndex(const QString& deviceId, int se return -1; } + +int DeviceEnumerator::getBestRxSamplingDeviceIndex(const QString& deviceId, const QString& deviceSerial, int deviceSequence, int deviceItemIndex) +{ + return getBestSamplingDeviceIndex(m_rxEnumeration, deviceId, deviceSerial, deviceSequence, deviceItemIndex); +} + +int DeviceEnumerator::getBestTxSamplingDeviceIndex(const QString& deviceId, const QString& deviceSerial, int deviceSequence, int deviceItemIndex) +{ + return getBestSamplingDeviceIndex(m_txEnumeration, deviceId, deviceSerial, deviceSequence, deviceItemIndex); +} + +int DeviceEnumerator::getBestMIMOSamplingDeviceIndex(const QString& deviceId, const QString& deviceSerial, int deviceSequence) +{ + return getBestSamplingDeviceIndex(m_mimoEnumeration, deviceId, deviceSerial, deviceSequence, -1); +} + +int DeviceEnumerator::getBestSamplingDeviceIndex( + const DevicesEnumeration& devicesEnumeration, + const QString& deviceId, + const QString& deviceSerial, + int deviceSequence, + int deviceItemIndex +) +{ + DevicesEnumeration::const_iterator it = devicesEnumeration.begin(); + DevicesEnumeration::const_iterator itFirstOfKind = devicesEnumeration.end(); + DevicesEnumeration::const_iterator itMatchSequence = devicesEnumeration.end(); + + for (; it != devicesEnumeration.end(); ++it) + { + if ((it->m_samplingDevice.id == deviceId) && + ( + ((deviceItemIndex < 0) || (deviceItemIndex > it->m_samplingDevice.deviceNbItems)) || // take first if item index is negative or out of range + ((deviceItemIndex <= it->m_samplingDevice.deviceNbItems) && (deviceItemIndex == it->m_samplingDevice.deviceItemIndex)) // take exact item index if in range + ) + ) + { + if (itFirstOfKind == devicesEnumeration.end()) { + itFirstOfKind = it; + } + + if (deviceSerial.isNull() || deviceSerial.isEmpty()) + { + if (it->m_samplingDevice.sequence == deviceSequence) { + break; + } + } + else + { + if (it->m_samplingDevice.serial == deviceSerial) { + break; + } else if(it->m_samplingDevice.sequence == deviceSequence) { + itMatchSequence = it; + } + } + } + } + + if (it == devicesEnumeration.end()) // no exact match + { + if (itMatchSequence != devicesEnumeration.end()) // match sequence and device type ? + { + qDebug("DeviceEnumerator::getBestSamplingDeviceIndex: sequence matched: id: %s ser: %s seq: %d", + qPrintable(itMatchSequence->m_samplingDevice.id), + qPrintable(itMatchSequence->m_samplingDevice.serial), + itMatchSequence->m_samplingDevice.sequence); + return itMatchSequence - devicesEnumeration.begin(); + } + else if (itFirstOfKind != devicesEnumeration.end()) // match just device type ? + { + qDebug("DeviceEnumerator::getBestSamplingDeviceIndex: first of kind matched: id: %s ser: %s seq: %d", + qPrintable(itFirstOfKind->m_samplingDevice.id), + qPrintable(itFirstOfKind->m_samplingDevice.serial), + itFirstOfKind->m_samplingDevice.sequence); + return itFirstOfKind - devicesEnumeration.begin(); + } + else // definitely not found ! + { + qDebug("DeviceEnumerator::getBestSamplingDeviceIndex: no match"); + return -1; + } + } + else // exact match + { + qDebug("DeviceEnumerator::getBestSamplingDeviceIndex: serial matched (exact): id: %s ser: %s", + qPrintable(it->m_samplingDevice.id), qPrintable(it->m_samplingDevice.serial)); + return it - devicesEnumeration.begin(); + } +} diff --git a/sdrbase/device/deviceenumerator.h b/sdrbase/device/deviceenumerator.h index 3ef4f0489..3cd7e68d4 100644 --- a/sdrbase/device/deviceenumerator.h +++ b/sdrbase/device/deviceenumerator.h @@ -62,6 +62,9 @@ public: int getRxSamplingDeviceIndex(const QString& deviceId, int sequence, int deviceItemIndex); int getTxSamplingDeviceIndex(const QString& deviceId, int sequence, int deviceItemIndex); int getMIMOSamplingDeviceIndex(const QString& deviceId, int sequence); + int getBestRxSamplingDeviceIndex(const QString& deviceId, const QString& serial, int sequence, int deviceItemIndex); + int getBestTxSamplingDeviceIndex(const QString& deviceId, const QString& serial, int sequence, int deviceItemIndex); + int getBestMIMOSamplingDeviceIndex(const QString& deviceId, const QString& serial, int sequence); private: struct DeviceEnumeration @@ -91,6 +94,13 @@ private: bool isRxEnumerated(const QString& deviceHwId, int deviceSequence); bool isTxEnumerated(const QString& deviceHwId, int deviceSequence); bool isMIMOEnumerated(const QString& deviceHwId, int deviceSequence); + int getBestSamplingDeviceIndex( + const DevicesEnumeration& devicesEnumeration, + const QString& deviceId, + const QString& serial, + int sequence, + int deviceItemIndex + ); }; #endif /* SDRBASE_DEVICE_DEVICEENUMERATOR_H_ */ diff --git a/sdrbase/settings/configuration.cpp b/sdrbase/settings/configuration.cpp index 91210fa91..d2ef6ebcd 100644 --- a/sdrbase/settings/configuration.cpp +++ b/sdrbase/settings/configuration.cpp @@ -44,12 +44,20 @@ QByteArray Configuration::serialize() const QByteArray b = m_featureSetPreset.serialize(); s.writeBlob(3, b); - s.writeS32(100, m_workspaceGeometries.size()); + int nitems = m_workspaceGeometries.size() < 99 ? m_workspaceGeometries.size() : 99; + s.writeS32(100, nitems); - for(int i = 0; i < m_workspaceGeometries.size(); i++) { + for (int i = 0; i < nitems; i++) { s.writeBlob(101 + i, m_workspaceGeometries[i]); } + nitems = m_deviceSetPresets.size() < 99 ? m_deviceSetPresets.size() : 99; + s.writeS32(200, nitems); + + for (int i = 0; i < nitems; i++) { + s.writeBlob(201 + i, m_deviceSetPresets[i].serialize()); + } + return s.final(); } @@ -72,15 +80,24 @@ bool Configuration::deserialize(const QByteArray& data) d.readBlob(3, &b); m_featureSetPreset.deserialize(b); - int nbWorkspaces; - d.readS32(100, &nbWorkspaces, 0); + int nitems; + d.readS32(100, &nitems, 0); - for(int i = 0; i < nbWorkspaces; i++) + for(int i = 0; i < nitems; i++) { m_workspaceGeometries.push_back(QByteArray()); d.readBlob(101 + i, &m_workspaceGeometries.back()); } + d.readS32(200, &nitems, 0); + + for (int i = 0; i < nitems; i++) + { + d.readBlob(201 + i, &b); + m_deviceSetPresets.push_back(Preset()); + m_deviceSetPresets.back().deserialize(b); + } + return true; } else @@ -97,6 +114,7 @@ int Configuration::getNumberOfWorkspaces() const void Configuration::clearData() { + m_deviceSetPresets.clear(); m_featureSetPreset.clearFeatures(); m_workspaceGeometries.clear(); } diff --git a/sdrbase/settings/configuration.h b/sdrbase/settings/configuration.h index cbdbc37ed..d08d51371 100644 --- a/sdrbase/settings/configuration.h +++ b/sdrbase/settings/configuration.h @@ -26,6 +26,7 @@ #include #include "featuresetpreset.h" +#include "preset.h" #include "export.h" class SDRBASE_API WorkspaceConfiguration { @@ -47,10 +48,12 @@ public: const QString& getDescription() const { return m_description; } int getNumberOfWorkspaces() const; - FeatureSetPreset& getFeatureSetPreset() { return m_featureSetPreset; } - const FeatureSetPreset& getFeatureSetPreset() const { return m_featureSetPreset; } QList& getWorkspaceGeometries() { return m_workspaceGeometries; } const QList& getWorkspaceGeometries() const { return m_workspaceGeometries; } + FeatureSetPreset& getFeatureSetPreset() { return m_featureSetPreset; } + const FeatureSetPreset& getFeatureSetPreset() const { return m_featureSetPreset; } + QList& getDeviceSetPresets() { return m_deviceSetPresets; } + const QList& getDeviceSetPresets() const { return m_deviceSetPresets; } void clearData(); static bool configCompare(const Configuration *p1, Configuration *p2) @@ -70,6 +73,7 @@ private: QString m_description; QList m_workspaceGeometries; FeatureSetPreset m_featureSetPreset; + QList m_deviceSetPresets; }; Q_DECLARE_METATYPE(const Configuration*) diff --git a/sdrbase/settings/preset.cpp b/sdrbase/settings/preset.cpp index cc4e00f7c..4cb5b598a 100644 --- a/sdrbase/settings/preset.cpp +++ b/sdrbase/settings/preset.cpp @@ -72,6 +72,14 @@ QByteArray Preset::serialize() const s.writeBool(6, m_presetType == PresetSource); s.writeS32(7, (int) m_presetType); s.writeBool(8, m_showSpectrum); + s.writeBlob(9, m_spectrumGeometry); + s.writeS32(10, m_spectrumWorkspaceIndex); + s.writeBlob(11, m_deviceGeometry); + s.writeS32(12, m_deviceWorkspaceIndex); + s.writeString(13, m_selectedDevice.m_deviceId); + s.writeString(14, m_selectedDevice.m_deviceSerial); + s.writeS32(15, m_selectedDevice.m_deviceSequence); + s.writeS32(16, m_selectedDevice.m_deviceItemIndex); s.writeS32(20, m_deviceConfigs.size()); @@ -129,13 +137,22 @@ bool Preset::deserialize(const QByteArray& data) d.readBlob(5, &m_spectrumConfig); d.readBool(6, &tmpBool, true); d.readS32(7, &tmp, PresetSource); - d.readBool(8, &m_showSpectrum, true); m_presetType = tmp < (int) PresetSource ? PresetSource : tmp > (int) PresetMIMO ? PresetMIMO : (PresetType) tmp; if (m_presetType != PresetMIMO) { m_presetType = tmpBool ? PresetSource : PresetSink; } + d.readBool(8, &m_showSpectrum, true); + d.readBlob(9, &m_spectrumGeometry); + d.readS32(10, &m_spectrumWorkspaceIndex, 0); + d.readBlob(11, &m_deviceGeometry); + d.readS32(12, &m_deviceWorkspaceIndex, 0); + d.readString(13, &m_selectedDevice.m_deviceId); + d.readString(14, &m_selectedDevice.m_deviceSerial); + d.readS32(15, &m_selectedDevice.m_deviceSequence); + d.readS32(16, &m_selectedDevice.m_deviceItemIndex); + // qDebug("Preset::deserialize: m_group: %s mode: %s m_description: %s m_centerFrequency: %llu", // qPrintable(m_group), // m_sourcePreset ? "Rx" : "Tx", @@ -205,7 +222,7 @@ void Preset::addOrUpdateDeviceConfig(const QString& sourceId, int sourceSequence, const QByteArray& config) { - DeviceeConfigs::iterator it = m_deviceConfigs.begin(); + DeviceConfigs::iterator it = m_deviceConfigs.begin(); for (; it != m_deviceConfigs.end(); ++it) { @@ -243,7 +260,7 @@ const QByteArray* Preset::findDeviceConfig( const QString& deviceSerial, int deviceSequence) const { - DeviceeConfigs::const_iterator it = m_deviceConfigs.begin(); + DeviceConfigs::const_iterator it = m_deviceConfigs.begin(); for (; it != m_deviceConfigs.end(); ++it) { @@ -254,46 +271,46 @@ const QByteArray* Preset::findDeviceConfig( return nullptr; } - +//_samplingDeviceId, m_samplingDeviceSerial, m_samplingDeviceSequence const QByteArray* Preset::findBestDeviceConfig( - const QString& sourceId, - const QString& sourceSerial, - int sourceSequence) const + const QString& deviceId, + const QString& deviceSerial, + int deviceSequence) const { // Special case for SoapySDR based on serial (driver name) - if (sourceId == "sdrangel.samplesource.soapysdrinput") { - return findBestDeviceConfigSoapy(sourceId, sourceSerial); - } else if (sourceId == "sdrangel.samplesource.soapysdroutput") { - return findBestDeviceConfigSoapy(sourceId, sourceSerial); + if (deviceId == "sdrangel.samplesource.soapysdrinput") { + return findBestDeviceConfigSoapy(deviceId, deviceSerial); + } else if (deviceId == "sdrangel.samplesource.soapysdroutput") { + return findBestDeviceConfigSoapy(deviceId, deviceSerial); } - DeviceeConfigs::const_iterator it = m_deviceConfigs.begin(); - DeviceeConfigs::const_iterator itFirstOfKind = m_deviceConfigs.end(); - DeviceeConfigs::const_iterator itMatchSequence = m_deviceConfigs.end(); + DeviceConfigs::const_iterator it = m_deviceConfigs.begin(); + DeviceConfigs::const_iterator itFirstOfKind = m_deviceConfigs.end(); + DeviceConfigs::const_iterator itMatchSequence = m_deviceConfigs.end(); for (; it != m_deviceConfigs.end(); ++it) { - if (it->m_deviceId == sourceId) + if (it->m_deviceId == deviceId) { if (itFirstOfKind == m_deviceConfigs.end()) { itFirstOfKind = it; } - if (sourceSerial.isNull() || sourceSerial.isEmpty()) + if (deviceSerial.isNull() || deviceSerial.isEmpty()) { - if (it->m_deviceSequence == sourceSequence) + if (it->m_deviceSequence == deviceSequence) { break; } } else { - if (it->m_deviceSerial == sourceSerial) + if (it->m_deviceSerial == deviceSerial) { break; } - else if(it->m_deviceSequence == sourceSequence) + else if(it->m_deviceSequence == deviceSequence) { itMatchSequence = it; } @@ -303,13 +320,13 @@ const QByteArray* Preset::findBestDeviceConfig( if (it == m_deviceConfigs.end()) // no exact match { - if (itMatchSequence != m_deviceConfigs.end()) // match sequence ? + if (itMatchSequence != m_deviceConfigs.end()) // match device type and sequence ? { qDebug("Preset::findBestDeviceConfig: sequence matched: id: %s ser: %s seq: %d", qPrintable(itMatchSequence->m_deviceId), qPrintable(itMatchSequence->m_deviceSerial), itMatchSequence->m_deviceSequence); return &(itMatchSequence->m_config); } - else if (itFirstOfKind != m_deviceConfigs.end()) // match source type ? + else if (itFirstOfKind != m_deviceConfigs.end()) // match just device type ? { qDebug("Preset::findBestDeviceConfig: first of kind matched: id: %s ser: %s seq: %d", qPrintable(itFirstOfKind->m_deviceId), qPrintable(itFirstOfKind->m_deviceSerial), itFirstOfKind->m_deviceSequence); @@ -337,8 +354,8 @@ const QByteArray* Preset::findBestDeviceConfigSoapy(const QString& sourceId, con return 0; // unable to process } - DeviceeConfigs::const_iterator it = m_deviceConfigs.begin(); - DeviceeConfigs::const_iterator itFirstOfKind = m_deviceConfigs.end(); + DeviceConfigs::const_iterator it = m_deviceConfigs.begin(); + DeviceConfigs::const_iterator itFirstOfKind = m_deviceConfigs.end(); for (; it != m_deviceConfigs.end(); ++it) { diff --git a/sdrbase/settings/preset.h b/sdrbase/settings/preset.h index 148fd74d8..edf306d7e 100644 --- a/sdrbase/settings/preset.h +++ b/sdrbase/settings/preset.h @@ -55,7 +55,19 @@ public: m_config(config) { } }; - typedef QList DeviceeConfigs; + typedef QList DeviceConfigs; + + struct SelectedDevice + { + QString m_deviceId; + QString m_deviceSerial; + int m_deviceSequence; + int m_deviceItemIndex; + + SelectedDevice() = default; + SelectedDevice(const SelectedDevice&) = default; + SelectedDevice& operator=(const SelectedDevice&) = default; + }; enum PresetType { @@ -90,6 +102,16 @@ public: void setSpectrumConfig(const QByteArray& data) { m_spectrumConfig = data; } const QByteArray& getSpectrumConfig() const { return m_spectrumConfig; } + void setSpectrumGeometry(const QByteArray& data) { m_spectrumGeometry = data; } + const QByteArray& getSpectrumGeometry() const { return m_spectrumGeometry; } + void setSpectrumWorkspaceIndex(int workspaceIndex) { m_spectrumWorkspaceIndex = workspaceIndex; } + int getSpectrumWorkspaceIndex() const { return m_spectrumWorkspaceIndex; } + void setSelectedDevice(const SelectedDevice& selectedDevice) { m_selectedDevice = selectedDevice; } + SelectedDevice getSelectedDevice() const { return m_selectedDevice; } + void setDeviceGeometry(const QByteArray& data) { m_deviceGeometry = data; } + const QByteArray& getDeviceGeometry() const { return m_deviceGeometry; } + void setDeviceWorkspaceIndex(int workspaceIndex) { m_deviceWorkspaceIndex = workspaceIndex; } + int getDeviceWorkspaceIndex() const { return m_deviceWorkspaceIndex; } bool hasDCOffsetCorrection() const { return m_dcOffsetCorrection; } void setDCOffsetCorrection(bool dcOffsetCorrection) { m_dcOffsetCorrection = dcOffsetCorrection; } @@ -154,6 +176,11 @@ protected: // general configuration QByteArray m_spectrumConfig; + QByteArray m_spectrumGeometry; + int m_spectrumWorkspaceIndex; + QByteArray m_deviceGeometry; + int m_deviceWorkspaceIndex; + SelectedDevice m_selectedDevice; // dc offset and i/q imbalance correction TODO: move it into the source data bool m_dcOffsetCorrection; @@ -163,14 +190,14 @@ protected: ChannelConfigs m_channelConfigs; // devices and configurations - DeviceeConfigs m_deviceConfigs; + DeviceConfigs m_deviceConfigs; // screen and dock layout bool m_showSpectrum; QByteArray m_layout; private: - const QByteArray* findBestDeviceConfigSoapy(const QString& sourceId, const QString& deviceSerial) const; + const QByteArray* findBestDeviceConfigSoapy(const QString& deviceId, const QString& deviceSerial) const; }; Q_DECLARE_METATYPE(const Preset*) diff --git a/sdrgui/CMakeLists.txt b/sdrgui/CMakeLists.txt index 357850af4..57a90f961 100644 --- a/sdrgui/CMakeLists.txt +++ b/sdrgui/CMakeLists.txt @@ -91,6 +91,8 @@ set(sdrgui_SOURCES feature/featuregui.cpp feature/featureuiset.cpp + mainspectrum/mainspectrumgui.cpp + soapygui/discreterangegui.cpp soapygui/intervalrangegui.cpp soapygui/itemsettinggui.cpp @@ -192,6 +194,8 @@ set(sdrgui_HEADERS feature/featuregui.h feature/featureuiset.h + mainspectrum/mainspectrumgui.h + soapygui/discreterangegui.h soapygui/intervalrangegui.h soapygui/itemsettinggui.h diff --git a/sdrgui/device/devicegui.cpp b/sdrgui/device/devicegui.cpp index c1c1b1727..28715039c 100644 --- a/sdrgui/device/devicegui.cpp +++ b/sdrgui/device/devicegui.cpp @@ -62,14 +62,22 @@ DeviceGUI::DeviceGUI(QWidget *parent) : m_addChannelsButton = new QPushButton(); m_addChannelsButton->setFixedSize(20, 20); - QIcon addChannelsIcon(":/create.png"); + QIcon addChannelsIcon(":/channels_add.png"); m_addChannelsButton->setIcon(addChannelsIcon); m_addChannelsButton->setToolTip("Add channels"); + m_deviceSetPresetsButton = new QPushButton(); + m_deviceSetPresetsButton->setFixedSize(20, 20); + QIcon deviceSetPresetsIcon(":/star.png"); + m_deviceSetPresetsButton->setIcon(deviceSetPresetsIcon); + m_deviceSetPresetsButton->setToolTip("Device set presets"); + m_titleLabel = new QLabel(); m_titleLabel->setText("Device"); m_titleLabel->setToolTip("Device identification"); m_titleLabel->setFixedHeight(20); + m_titleLabel->setMinimumWidth(20); + m_titleLabel->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Fixed); m_helpButton = new QPushButton(); m_helpButton->setFixedSize(20, 20); @@ -98,8 +106,22 @@ DeviceGUI::DeviceGUI(QWidget *parent) : m_statusLabel = new QLabel(); // m_statusLabel->setText("OK"); // for future use m_statusLabel->setFixedHeight(20); + m_statusLabel->setMinimumWidth(20); + m_statusLabel->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Fixed); m_statusLabel->setToolTip("Device status"); + m_showSpectrumButton = new QPushButton(); + m_showSpectrumButton->setFixedSize(20, 20); + QIcon showSpectrumIcon(":/dsb.png"); + m_showSpectrumButton->setIcon(showSpectrumIcon); + m_showSpectrumButton->setToolTip("Show main spectrum"); + + m_showAllChannelsButton = new QPushButton(); + m_showAllChannelsButton->setFixedSize(20, 20); + QIcon showAllChannelsIcon(":/channels.png"); + m_showAllChannelsButton->setIcon(showAllChannelsIcon); + m_showAllChannelsButton->setToolTip("Show all channels"); + m_layouts = new QVBoxLayout(); m_layouts->setContentsMargins(0, 4, 0, 4); m_layouts->setSpacing(2); @@ -110,14 +132,16 @@ DeviceGUI::DeviceGUI(QWidget *parent) : m_topLayout->addWidget(m_changeDeviceButton); m_topLayout->addWidget(m_reloadDeviceButton); m_topLayout->addWidget(m_addChannelsButton); + m_topLayout->addWidget(m_deviceSetPresetsButton); m_topLayout->addWidget(m_titleLabel); - m_topLayout->addStretch(1); + // m_topLayout->addStretch(1); m_topLayout->addWidget(m_helpButton); m_topLayout->addWidget(m_moveButton); m_topLayout->addWidget(m_shrinkButton); m_topLayout->addWidget(m_closeButton); m_sizeGripTopRight = new QSizeGrip(this); m_sizeGripTopRight->setStyleSheet("QSizeGrip { background-color: rgb(128, 128, 128); width: 10px; height: 10px; }"); + m_sizeGripTopRight->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); m_topLayout->addWidget(m_sizeGripTopRight, 0, Qt::AlignTop | Qt::AlignRight); m_centerLayout = new QHBoxLayout(); @@ -126,10 +150,13 @@ DeviceGUI::DeviceGUI(QWidget *parent) : m_bottomLayout = new QHBoxLayout(); m_bottomLayout->setContentsMargins(0, 0, 0, 0); + m_bottomLayout->addWidget(m_showSpectrumButton); + m_bottomLayout->addWidget(m_showAllChannelsButton); m_bottomLayout->addWidget(m_statusLabel); m_sizeGripBottomRight = new QSizeGrip(this); m_sizeGripBottomRight->setStyleSheet("QSizeGrip { background-color: rgb(128, 128, 128); width: 10px; height: 10px; }"); - m_bottomLayout->addStretch(1); + m_sizeGripBottomRight->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + // m_bottomLayout->addStretch(1); m_bottomLayout->addWidget(m_sizeGripBottomRight, 0, Qt::AlignBottom | Qt::AlignRight); m_layouts->addLayout(m_topLayout); @@ -158,12 +185,15 @@ DeviceGUI::~DeviceGUI() delete m_sizeGripTopRight; delete m_topLayout; delete m_layouts; + delete m_showAllChannelsButton; + delete m_showSpectrumButton; delete m_statusLabel; delete m_closeButton; delete m_shrinkButton; delete m_moveButton; delete m_helpButton; delete m_titleLabel; + delete m_deviceSetPresetsButton; delete m_addChannelsButton; delete m_reloadDeviceButton; delete m_changeDeviceButton; diff --git a/sdrgui/device/devicegui.h b/sdrgui/device/devicegui.h index e6a947c93..a4167d704 100644 --- a/sdrgui/device/devicegui.h +++ b/sdrgui/device/devicegui.h @@ -101,11 +101,14 @@ private: QPushButton *m_changeDeviceButton; QPushButton *m_reloadDeviceButton; QPushButton *m_addChannelsButton; + QPushButton *m_deviceSetPresetsButton; QLabel *m_titleLabel; QPushButton *m_helpButton; QPushButton *m_moveButton; QPushButton *m_shrinkButton; QPushButton *m_closeButton; + QPushButton *m_showSpectrumButton; + QPushButton *m_showAllChannelsButton; QLabel *m_statusLabel; QVBoxLayout *m_layouts; QHBoxLayout *m_topLayout; diff --git a/sdrgui/device/deviceuiset.cpp b/sdrgui/device/deviceuiset.cpp index cb628124c..3570517df 100644 --- a/sdrgui/device/deviceuiset.cpp +++ b/sdrgui/device/deviceuiset.cpp @@ -25,31 +25,35 @@ #include "gui/glspectrum.h" #include "gui/glspectrumgui.h" #include "gui/channelwindow.h" +#include "gui/workspace.h" #include "device/devicegui.h" #include "device/deviceset.h" +#include "device/deviceapi.h" #include "plugin/pluginapi.h" #include "plugin/plugininterface.h" #include "channel/channelutils.h" #include "channel/channelapi.h" #include "channel/channelgui.h" +#include "mainspectrum/mainspectrumgui.h" #include "settings/preset.h" #include "deviceuiset.h" -DeviceUISet::DeviceUISet(int tabIndex, DeviceSet *deviceSet) +DeviceUISet::DeviceUISet(int deviceSetIndex, DeviceSet *deviceSet) { m_spectrum = new GLSpectrum; m_spectrumVis = deviceSet->m_spectrumVis; m_spectrumVis->setGLSpectrum(m_spectrum); m_spectrumGUI = new GLSpectrumGUI; m_spectrumGUI->setBuddies(m_spectrumVis, m_spectrum); + m_mainSpectrumGUI = new MainSpectrumGUI(m_spectrum, m_spectrumGUI); m_channelWindow = new ChannelWindow; m_deviceAPI = nullptr; m_deviceGUI = nullptr; m_deviceSourceEngine = nullptr; m_deviceSinkEngine = nullptr; m_deviceMIMOEngine = nullptr; - m_deviceTabIndex = tabIndex; + m_deviceSetIndex = deviceSetIndex; m_deviceSet = deviceSet; m_nbAvailableRxChannels = 0; // updated at enumeration for UI selector m_nbAvailableTxChannels = 0; // updated at enumeration for UI selector @@ -65,8 +69,9 @@ DeviceUISet::DeviceUISet(int tabIndex, DeviceSet *deviceSet) DeviceUISet::~DeviceUISet() { delete m_channelWindow; - delete m_spectrumGUI; - delete m_spectrum; + delete m_mainSpectrumGUI; + // delete m_spectrumGUI; // done above + // delete m_spectrum; } void DeviceUISet::setSpectrumScalingFactor(float scalef) @@ -156,6 +161,60 @@ ChannelAPI *DeviceUISet::getChannelAt(int channelIndex) return m_deviceSet->getChannelAt(channelIndex); } +void DeviceUISet::loadDeviceSetSettings( + const Preset* preset, + PluginAPI *pluginAPI, + QList *workspaces, + Workspace *currentWorkspace +) +{ + (void) workspaces; // TODO: use for channels + (void) currentWorkspace; // TODO: use for channels + + m_spectrumGUI->deserialize(preset->getSpectrumConfig()); + m_deviceAPI->loadSamplingDeviceSettings(preset); + + if (m_deviceSourceEngine) { // source device + loadRxChannelSettings(preset, pluginAPI); + } else if (m_deviceSinkEngine) { // sink device + loadTxChannelSettings(preset, pluginAPI); + } else if (m_deviceMIMOEngine) { // MIMO device + loadMIMOChannelSettings(preset, pluginAPI); + } +} + +void DeviceUISet::saveDeviceSetSettings(Preset* preset) const +{ + preset->setSpectrumConfig(m_spectrumGUI->serialize()); + preset->setSpectrumWorkspaceIndex(m_mainSpectrumGUI->getWorkspaceIndex()); + preset->setSpectrumGeometry(m_mainSpectrumGUI->saveGeometry()); + preset->setSelectedDevice(Preset::SelectedDevice{ + m_deviceAPI->getSamplingDeviceId(), + m_deviceAPI->getSamplingDeviceSerial(), + (int) m_deviceAPI->getSamplingDeviceSequence(), + (int) m_deviceAPI->getDeviceItemIndex() + }); + preset->clearChannels(); + + if (m_deviceSourceEngine) // source device + { + preset->setSourcePreset(); + saveRxChannelSettings(preset); + } + else if (m_deviceSinkEngine) // sink device + { + preset->setSinkPreset(); + saveTxChannelSettings(preset); + } + else if (m_deviceMIMOEngine) // MIMO device + { + preset->setMIMOPreset(); + saveMIMOChannelSettings(preset); + } + + m_deviceAPI->saveSamplingDeviceSettings(preset); +} + void DeviceUISet::loadRxChannelSettings(const Preset *preset, PluginAPI *pluginAPI) { if (preset->isSourcePreset()) @@ -224,7 +283,7 @@ void DeviceUISet::loadRxChannelSettings(const Preset *preset, PluginAPI *pluginA } } -void DeviceUISet::saveRxChannelSettings(Preset *preset) +void DeviceUISet::saveRxChannelSettings(Preset *preset) const { if (preset->isSourcePreset()) { @@ -308,7 +367,7 @@ void DeviceUISet::loadTxChannelSettings(const Preset *preset, PluginAPI *pluginA } -void DeviceUISet::saveTxChannelSettings(Preset *preset) +void DeviceUISet::saveTxChannelSettings(Preset *preset) const { if (preset->isSinkPreset()) { @@ -392,7 +451,7 @@ void DeviceUISet::loadMIMOChannelSettings(const Preset *preset, PluginAPI *plugi } } -void DeviceUISet::saveMIMOChannelSettings(Preset *preset) +void DeviceUISet::saveMIMOChannelSettings(Preset *preset) const { if (preset->isMIMOPreset()) { diff --git a/sdrgui/device/deviceuiset.h b/sdrgui/device/deviceuiset.h index 4965b431b..5d133d4b7 100644 --- a/sdrgui/device/deviceuiset.h +++ b/sdrgui/device/deviceuiset.h @@ -27,6 +27,7 @@ class SpectrumVis; class GLSpectrum; class GLSpectrumGUI; +class MainSpectrumGUI; class ChannelWindow; class DeviceAPI; class DeviceSet; @@ -39,6 +40,7 @@ class DeviceGUI; class ChannelAPI; class ChannelGUI; class Preset; +class Workspace; namespace SWGSDRangel { class SWGGLSpectrum; @@ -53,6 +55,7 @@ public: SpectrumVis *m_spectrumVis; GLSpectrum *m_spectrum; GLSpectrumGUI *m_spectrumGUI; + MainSpectrumGUI *m_mainSpectrumGUI; ChannelWindow *m_channelWindow; DeviceAPI *m_deviceAPI; DeviceGUI *m_deviceGUI; @@ -60,8 +63,12 @@ public: DSPDeviceSinkEngine *m_deviceSinkEngine; DSPDeviceMIMOEngine *m_deviceMIMOEngine; QByteArray m_mainWindowState; + QString m_selectedDeviceId; + QString m_selectedDeviceSerial; + int m_selectedDeviceSequence; + int m_selectedDeviceItemImdex; - DeviceUISet(int tabIndex, DeviceSet *deviceSet); + DeviceUISet(int deviceSetIndex, DeviceSet *deviceSet); ~DeviceUISet(); GLSpectrum *getSpectrum() { return m_spectrum; } //!< Direct spectrum getter @@ -73,12 +80,15 @@ public: void freeChannels(); void deleteChannel(int channelIndex); ChannelAPI *getChannelAt(int channelIndex); - void loadRxChannelSettings(const Preset* preset, PluginAPI *pluginAPI); - void saveRxChannelSettings(Preset* preset); - void loadTxChannelSettings(const Preset* preset, PluginAPI *pluginAPI); - void saveTxChannelSettings(Preset* preset); - void loadMIMOChannelSettings(const Preset* preset, PluginAPI *pluginAPI); - void saveMIMOChannelSettings(Preset* preset); + + void loadDeviceSetSettings( + const Preset* preset, + PluginAPI *pluginAPI, + QList *workspaces, + Workspace *currentWorkspace + ); + void saveDeviceSetSettings(Preset* preset) const; + void registerRxChannelInstance(ChannelAPI *channelAPI, ChannelGUI* channelGUI); void registerTxChannelInstance(ChannelAPI *channelAPI, ChannelGUI* channelGUI); void registerChannelInstance(ChannelAPI *channelAPI, ChannelGUI* channelGUI); @@ -129,12 +139,19 @@ private: // ChannelInstanceRegistrations m_rxChannelInstanceRegistrations; // ChannelInstanceRegistrations m_txChannelInstanceRegistrations; ChannelInstanceRegistrations m_channelInstanceRegistrations; - int m_deviceTabIndex; + int m_deviceSetIndex; DeviceSet *m_deviceSet; int m_nbAvailableRxChannels; //!< Number of Rx channels available for selection int m_nbAvailableTxChannels; //!< Number of Tx channels available for selection int m_nbAvailableMIMOChannels; //!< Number of MIMO channels available for selection + void loadRxChannelSettings(const Preset* preset, PluginAPI *pluginAPI); + void loadTxChannelSettings(const Preset* preset, PluginAPI *pluginAPI); + void loadMIMOChannelSettings(const Preset* preset, PluginAPI *pluginAPI); + void saveRxChannelSettings(Preset* preset) const; + void saveTxChannelSettings(Preset* preset) const; + void saveMIMOChannelSettings(Preset* preset) const; + private slots: void handleChannelGUIClosing(ChannelGUI* channelGUI); }; diff --git a/sdrgui/feature/featuregui.cpp b/sdrgui/feature/featuregui.cpp index 41638097e..248be215b 100644 --- a/sdrgui/feature/featuregui.cpp +++ b/sdrgui/feature/featuregui.cpp @@ -54,6 +54,8 @@ FeatureGUI::FeatureGUI(QWidget *parent) : m_titleLabel->setText("Feature"); m_titleLabel->setToolTip("Feature name"); m_titleLabel->setFixedHeight(20); + m_titleLabel->setMinimumWidth(20); + m_titleLabel->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Fixed); m_helpButton = new QPushButton(); m_helpButton->setFixedSize(20, 20); @@ -82,6 +84,8 @@ FeatureGUI::FeatureGUI(QWidget *parent) : m_statusLabel = new QLabel(); // m_statusLabel->setText("OK"); // for future use m_statusLabel->setFixedHeight(20); + m_statusLabel->setMinimumWidth(20); + m_statusLabel->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Fixed); m_statusLabel->setToolTip("Feature status"); m_layouts = new QVBoxLayout(); @@ -93,13 +97,14 @@ FeatureGUI::FeatureGUI(QWidget *parent) : m_topLayout->addWidget(m_indexLabel); m_topLayout->addWidget(m_settingsButton); m_topLayout->addWidget(m_titleLabel); - m_topLayout->addStretch(1); + // m_topLayout->addStretch(1); m_topLayout->addWidget(m_helpButton); m_topLayout->addWidget(m_moveButton); m_topLayout->addWidget(m_shrinkButton); m_topLayout->addWidget(m_closeButton); m_sizeGripTopRight = new QSizeGrip(this); m_sizeGripTopRight->setStyleSheet("QSizeGrip { background-color: rgb(128, 128, 128); width: 10px; height: 10px; }"); + m_sizeGripTopRight->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); m_topLayout->addWidget(m_sizeGripTopRight, 0, Qt::AlignTop | Qt::AlignRight); m_centerLayout = new QHBoxLayout(); @@ -110,7 +115,8 @@ FeatureGUI::FeatureGUI(QWidget *parent) : m_bottomLayout->addWidget(m_statusLabel); m_sizeGripBottomRight = new QSizeGrip(this); m_sizeGripBottomRight->setStyleSheet("QSizeGrip { background-color: rgb(128, 128, 128); width: 10px; height: 10px; }"); - m_bottomLayout->addStretch(1); + m_sizeGripBottomRight->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + // m_bottomLayout->addStretch(1); m_bottomLayout->addWidget(m_sizeGripBottomRight, 0, Qt::AlignBottom | Qt::AlignRight); m_layouts->addLayout(m_topLayout); diff --git a/sdrgui/feature/featureuiset.cpp b/sdrgui/feature/featureuiset.cpp index e09d2d0fb..3bf8c7453 100644 --- a/sdrgui/feature/featureuiset.cpp +++ b/sdrgui/feature/featureuiset.cpp @@ -134,7 +134,8 @@ void FeatureUISet::loadFeatureSetSettings( const FeatureSetPreset *preset, PluginAPI *pluginAPI, WebAPIAdapterInterface *apiAdapter, - Workspace *workspace + QList *workspaces, + Workspace *currentWorkspace ) { qDebug("FeatureUISet::loadFeatureSetSettings: Loading preset [%s | %s]", @@ -161,58 +162,6 @@ void FeatureUISet::loadFeatureSetSettings( qDebug("FeatureUISet::loadFeatureSetSettings: %d feature(s) in preset", preset->getFeatureCount()); - for (int i = 0; i < preset->getFeatureCount(); i++) - { - const FeatureSetPreset::FeatureConfig& featureConfig = preset->getFeatureConfig(i); - FeatureGUI *featureGUI = nullptr; - Feature *feature = nullptr; - - // create feature instance - - for(int i = 0; i < featureRegistrations->count(); i++) - { - if (FeatureUtils::compareFeatureURIs((*featureRegistrations)[i].m_featureIdURI, featureConfig.m_featureIdURI)) - { - qDebug("FeatureUISet::loadFeatureSetSettings: creating new feature [%s] from config [%s]", - qPrintable((*featureRegistrations)[i].m_featureIdURI), - qPrintable(featureConfig.m_featureIdURI) - ); - feature = (*featureRegistrations)[i].m_plugin->createFeature(apiAdapter); - featureGUI = (*featureRegistrations)[i].m_plugin->createFeatureGUI(this, feature); - registerFeatureInstance(featureGUI, feature); - break; - } - } - - if (featureGUI) - { - qDebug("FeatureUISet::loadFeatureSetSettings: deserializing feature [%s]", - qPrintable(featureConfig.m_featureIdURI)); - featureGUI->deserialize(featureConfig.m_config); - - if (workspace) // restore in current workspace - { - featureGUI->setIndex(feature->getIndexInFeatureSet()); - featureGUI->setWorkspaceIndex(workspace->getIndex()); - workspace->addToMdiArea((QMdiSubWindow*) featureGUI); - } - } - } -} - -void FeatureUISet::loadFeatureSetSettings( - const FeatureSetPreset* preset, - PluginAPI *pluginAPI, - WebAPIAdapterInterface *apiAdapter, - QList& workspaces -) -{ - // This method loads from scratch - load from configuration - qDebug("FeatureUISet::loadFeatureSetSettings: %d feature(s) in preset", preset->getFeatureCount()); - - // Available feature plugins - PluginAPI::FeatureRegistrations *featureRegistrations = pluginAPI->getFeatureRegistrations(); - for (int i = 0; i < preset->getFeatureCount(); i++) { const FeatureSetPreset::FeatureConfig& featureConfig = preset->getFeatureConfig(i); @@ -242,8 +191,19 @@ void FeatureUISet::loadFeatureSetSettings( qPrintable(featureConfig.m_featureIdURI)); featureGUI->deserialize(featureConfig.m_config); featureGUI->setIndex(feature->getIndexInFeatureSet()); - workspaces[featureGUI->getWorkspaceIndex()]->addToMdiArea((QMdiSubWindow*) featureGUI); - featureGUI->restoreGeometry(featureGUI->getGeometryBytes()); + int originalWorkspaceIndex = featureGUI->getWorkspaceIndex(); + + if (workspaces && (workspaces->size() > 0) && (originalWorkspaceIndex < workspaces->size())) // restore in original workspace + { + (*workspaces)[originalWorkspaceIndex]->addToMdiArea((QMdiSubWindow*) featureGUI); + featureGUI->restoreGeometry(featureGUI->getGeometryBytes()); + } + else if (currentWorkspace) // restore in current workspace + { + featureGUI->setWorkspaceIndex(currentWorkspace->getIndex()); + currentWorkspace->addToMdiArea((QMdiSubWindow*) featureGUI); + featureGUI->restoreGeometry(featureGUI->getGeometryBytes()); + } } } } diff --git a/sdrgui/feature/featureuiset.h b/sdrgui/feature/featureuiset.h index 372a3cb3e..e557527cf 100644 --- a/sdrgui/feature/featureuiset.h +++ b/sdrgui/feature/featureuiset.h @@ -52,13 +52,8 @@ public: const FeatureSetPreset* preset, PluginAPI *pluginAPI, WebAPIAdapterInterface *apiAdapter, - Workspace *workspace - ); - void loadFeatureSetSettings( - const FeatureSetPreset* preset, - PluginAPI *pluginAPI, - WebAPIAdapterInterface *apiAdapter, - QList& workspaces + QList *workspaces, + Workspace *currentWorkspace ); void saveFeatureSetSettings(FeatureSetPreset* preset); void freeFeatures(); diff --git a/sdrgui/gui/featurepresetsdialog.cpp b/sdrgui/gui/featurepresetsdialog.cpp index 6458a43e1..acd902766 100644 --- a/sdrgui/gui/featurepresetsdialog.cpp +++ b/sdrgui/gui/featurepresetsdialog.cpp @@ -34,6 +34,8 @@ FeaturePresetsDialog::FeaturePresetsDialog(QWidget* parent) : m_featureUISet(nullptr), m_pluginAPI(nullptr), m_apiAdapter(nullptr), + m_currentWorkspace(nullptr), + m_workspaces(nullptr), m_presetLoaded(false) { ui->setupUi(this); @@ -374,7 +376,7 @@ void FeaturePresetsDialog::loadPresetSettings(const FeatureSetPreset* preset) qPrintable(preset->getGroup()), qPrintable(preset->getDescription())); - m_featureUISet->loadFeatureSetSettings(preset, m_pluginAPI, m_apiAdapter, m_workspace); + m_featureUISet->loadFeatureSetSettings(preset, m_pluginAPI, m_apiAdapter, m_workspaces, m_currentWorkspace); m_presetLoaded = true; } diff --git a/sdrgui/gui/featurepresetsdialog.h b/sdrgui/gui/featurepresetsdialog.h index 1addbf404..c5b9c3d2b 100644 --- a/sdrgui/gui/featurepresetsdialog.h +++ b/sdrgui/gui/featurepresetsdialog.h @@ -44,7 +44,8 @@ public: void setFeatureUISet(FeatureUISet *featureUISet) { m_featureUISet = featureUISet; } void setPluginAPI(PluginAPI *pluginAPI) { m_pluginAPI = pluginAPI; } void setWebAPIAdapter(WebAPIAdapterInterface *apiAdapter) { m_apiAdapter = apiAdapter; } - void setWorkspace(Workspace *workspace) { m_workspace = workspace; } + void setCurrentWorkspace(Workspace *workspace) { m_currentWorkspace = workspace; } + void setWorkspaces(QList *workspaces) { m_workspaces = workspaces; } void populateTree(); bool wasPresetLoaded() const { return m_presetLoaded; } @@ -59,7 +60,8 @@ private: FeatureUISet *m_featureUISet; PluginAPI *m_pluginAPI; WebAPIAdapterInterface *m_apiAdapter; - Workspace *m_workspace; + Workspace *m_currentWorkspace; + QList *m_workspaces; bool m_presetLoaded; QTreeWidgetItem* addPresetToTree(const FeatureSetPreset* preset); diff --git a/sdrgui/gui/glspectrum.cpp b/sdrgui/gui/glspectrum.cpp index d9b846398..2c9c1513f 100644 --- a/sdrgui/gui/glspectrum.cpp +++ b/sdrgui/gui/glspectrum.cpp @@ -61,7 +61,7 @@ GLSpectrum::GLSpectrum(QWidget* parent) : m_displayGrid(true), m_displayGridIntensity(5), m_displayTraceIntensity(50), - m_invertedWaterfall(false), + m_invertedWaterfall(true), m_displayMaxHold(false), m_currentSpectrum(nullptr), m_displayCurrent(false), @@ -69,7 +69,7 @@ GLSpectrum::GLSpectrum(QWidget* parent) : m_rightMargin(0), m_topMargin(0), m_frequencyScaleHeight(0), - m_histogramHeight(20), + m_histogramHeight(80), m_waterfallHeight(0), m_bottomMargin(0), m_waterfallBuffer(nullptr), @@ -93,6 +93,7 @@ GLSpectrum::GLSpectrum(QWidget* parent) : m_calibrationInterpMode(SpectrumSettings::CalibInterpLinear), m_messageQueueToGUI(nullptr) { + setObjectName("GLSpectrum"); setAutoFillBackground(false); setAttribute(Qt::WA_OpaquePaintEvent, true); setAttribute(Qt::WA_NoSystemBackground, true); @@ -100,7 +101,7 @@ GLSpectrum::GLSpectrum(QWidget* parent) : setMinimumSize(200, 200); - m_waterfallShare = 0.66; + m_waterfallShare = 0.5; for (int i = 0; i <= 239; i++) { diff --git a/sdrgui/gui/workspace.cpp b/sdrgui/gui/workspace.cpp index 724f10116..a40b82bee 100644 --- a/sdrgui/gui/workspace.cpp +++ b/sdrgui/gui/workspace.cpp @@ -73,13 +73,13 @@ Workspace::Workspace(int index, QWidget *parent, Qt::WindowFlags flags) : m_vline1->setFrameShadow(QFrame::Sunken); m_addFeatureButton = new QPushButton(); - QIcon addFeatureIcon(":/tool.png"); + QIcon addFeatureIcon(":/tool_add.png"); m_addFeatureButton->setIcon(addFeatureIcon); m_addFeatureButton->setToolTip("Add features"); m_addFeatureButton->setFixedSize(20, 20); m_featurePresetsButton = new QPushButton(); - QIcon presetsIcon(":/star.png"); + QIcon presetsIcon(":/tool_star.png"); m_featurePresetsButton->setIcon(presetsIcon); m_featurePresetsButton->setToolTip("Feature presets"); m_featurePresetsButton->setFixedSize(20, 20); diff --git a/sdrgui/mainspectrum/mainspectrumgui.cpp b/sdrgui/mainspectrum/mainspectrumgui.cpp new file mode 100644 index 000000000..04349036b --- /dev/null +++ b/sdrgui/mainspectrum/mainspectrumgui.cpp @@ -0,0 +1,290 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2022 Edouard Griffiths, F4EXB // +// // +// This program 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 as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program 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 V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mainwindow.h" +#include "gui/glspectrum.h" +#include "gui/glspectrumgui.h" +#include "gui/workspaceselectiondialog.h" +#include "mainspectrumgui.h" + +MainSpectrumGUI::MainSpectrumGUI(GLSpectrum *spectrum, GLSpectrumGUI *spectrumGUI, QWidget *parent) : + QMdiSubWindow(parent), + m_spectrum(spectrum), + m_spectrumGUI(spectrumGUI), + m_deviceType(DeviceRx), + m_deviceSetIndex(0), + m_drag(false) +{ + qDebug("MainSpectrumGUI::MainSpectrumGUI: %p", parent); + setWindowFlags(windowFlags() | Qt::FramelessWindowHint); + + m_indexLabel = new QLabel(); + m_indexLabel->setFixedSize(32, 16); + m_indexLabel->setStyleSheet("QLabel { background-color: rgb(128, 128, 128); qproperty-alignment: AlignCenter; }"); + m_indexLabel->setText(tr("X:%1").arg(m_deviceSetIndex)); + m_indexLabel->setToolTip("Device type and set index"); + + m_spacerLabel = new QLabel(); + m_spacerLabel->setFixedWidth(5); + + m_titleLabel = new QLabel(); + m_titleLabel->setText("Device"); + m_titleLabel->setToolTip("Device identification"); + m_titleLabel->setFixedHeight(20); + m_titleLabel->setMinimumWidth(20); + m_titleLabel->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Fixed); + + m_helpButton = new QPushButton(); + m_helpButton->setFixedSize(20, 20); + QIcon helpIcon(":/help.png"); + m_helpButton->setIcon(helpIcon); + m_helpButton->setToolTip("Show spectrum window documentation in browser"); + + m_moveButton = new QPushButton(); + m_moveButton->setFixedSize(20, 20); + QIcon moveIcon(":/exit.png"); + m_moveButton->setIcon(moveIcon); + m_moveButton->setToolTip("Move to workspace"); + + m_shrinkButton = new QPushButton(); + m_shrinkButton->setFixedSize(20, 20); + QIcon shrinkIcon(":/shrink.png"); + m_shrinkButton->setIcon(shrinkIcon); + m_shrinkButton->setToolTip("Adjust window to minimum size"); + + m_hideButton = new QPushButton(); + m_hideButton->setFixedSize(20, 20); + QIcon hideIcon(":/hide.png"); + m_hideButton->setIcon(hideIcon); + m_hideButton->setToolTip("Hide device"); + + m_statusLabel = new QLabel(); + // m_statusLabel->setText("OK"); // for future use + m_statusLabel->setFixedHeight(10); + m_statusLabel->setMinimumWidth(10); + m_statusLabel->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Fixed); + // m_statusLabel->setToolTip("Spectrum status"); + + m_layouts = new QVBoxLayout(); + m_layouts->setContentsMargins(0, 4, 0, 4); + m_layouts->setSpacing(0); + + m_topLayout = new QHBoxLayout(); + m_topLayout->setContentsMargins(0, 0, 0, 0); + m_topLayout->addWidget(m_indexLabel); + m_topLayout->addWidget(m_spacerLabel); + m_topLayout->addWidget(m_titleLabel); + // m_topLayout->addStretch(1); + m_topLayout->addWidget(m_helpButton); + m_topLayout->addWidget(m_moveButton); + m_topLayout->addWidget(m_shrinkButton); + m_topLayout->addWidget(m_hideButton); + m_sizeGripTopRight = new QSizeGrip(this); + m_sizeGripTopRight->setStyleSheet("QSizeGrip { background-color: rgb(128, 128, 128); width: 10px; height: 10px; }"); + m_sizeGripTopRight->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + m_topLayout->addWidget(m_sizeGripTopRight, 0, Qt::AlignTop | Qt::AlignRight); + + m_spectrumLayout = new QHBoxLayout(); + m_spectrumLayout->addWidget(spectrum); + spectrum->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding); + m_spectrumGUILayout = new QHBoxLayout(); + m_spectrumGUILayout->addWidget(spectrumGUI); + + m_bottomLayout = new QHBoxLayout(); + m_bottomLayout->setContentsMargins(0, 0, 0, 0); + m_bottomLayout->addWidget(m_statusLabel); + m_sizeGripBottomRight = new QSizeGrip(this); + m_sizeGripBottomRight->setStyleSheet("QSizeGrip { background-color: rgb(128, 128, 128); width: 10px; height: 10px; }"); + m_sizeGripBottomRight->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + //m_bottomLayout->addStretch(1); + m_bottomLayout->addWidget(m_sizeGripBottomRight, 0, Qt::AlignBottom | Qt::AlignRight); + + m_layouts->addLayout(m_topLayout); + m_layouts->addLayout(m_spectrumLayout); + m_layouts->addLayout(m_spectrumGUILayout); + m_layouts->addLayout(m_bottomLayout); + + QObjectCleanupHandler().add(layout()); + setLayout(m_layouts); + + connect(m_helpButton, SIGNAL(clicked()), this, SLOT(showHelp())); + connect(m_moveButton, SIGNAL(clicked()), this, SLOT(openMoveToWorkspaceDialog())); + connect(m_shrinkButton, SIGNAL(clicked()), this, SLOT(shrinkWindow())); + connect(this, SIGNAL(forceShrink()), this, SLOT(shrinkWindow())); + connect(m_hideButton, SIGNAL(clicked()), this, SLOT(hide())); + connect(this, SIGNAL(forceClose()), this, SLOT(close())); + + shrinkWindow(); +} + +MainSpectrumGUI::~MainSpectrumGUI() +{ + qDebug("MainSpectrumGUI::~MainSpectrumGUI"); + m_spectrumLayout->removeWidget(m_spectrum); + m_spectrumGUILayout->removeWidget(m_spectrumGUI); + delete m_sizeGripBottomRight; + delete m_bottomLayout; + delete m_spectrumGUILayout; + delete m_spectrumLayout; + delete m_sizeGripTopRight; + delete m_topLayout; + delete m_layouts; + delete m_statusLabel; + delete m_hideButton; + delete m_shrinkButton; + delete m_moveButton; + delete m_helpButton; + delete m_titleLabel; + delete m_spacerLabel; + delete m_indexLabel; + qDebug("MainSpectrumGUI::~MainSpectrumGUI: end"); +} + +void MainSpectrumGUI::closeEvent(QCloseEvent *event) +{ + qDebug("MainSpectrumGUI::closeEvent"); + emit closing(); + event->accept(); +} + +void MainSpectrumGUI::mousePressEvent(QMouseEvent* event) +{ + if ((event->button() == Qt::LeftButton) && isOnMovingPad()) + { + m_drag = true; + m_DragPosition = event->globalPos() - pos(); + event->accept(); + } +} + +void MainSpectrumGUI::mouseMoveEvent(QMouseEvent* event) +{ + if ((event->buttons() & Qt::LeftButton) && isOnMovingPad()) + { + move(event->globalPos() - m_DragPosition); + event->accept(); + } +} + +void MainSpectrumGUI::showHelp() +{ + if (m_helpURL.isEmpty()) { + return; + } + + QString url; + + if (m_helpURL.startsWith("http")) { + url = m_helpURL; + } else { + url = QString("https://github.com/f4exb/sdrangel/blob/master/%1").arg(m_helpURL); // Something like "plugins/channelrx/chanalyzer/readme.md" + } + + QDesktopServices::openUrl(QUrl(url)); +} + +void MainSpectrumGUI::openMoveToWorkspaceDialog() +{ + int numberOfWorkspaces = MainWindow::getInstance()->getNumberOfWorkspaces(); + WorkspaceSelectionDialog dialog(numberOfWorkspaces, this); + dialog.exec(); + + if (dialog.hasChanged()) { + emit moveToWorkspace(dialog.getSelectedIndex()); + } +} + +void MainSpectrumGUI::shrinkWindow() +{ + qDebug("MainSpectrumGUI::shrinkWindow"); + adjustSize(); + resize(width(), 360); +} + +void MainSpectrumGUI::setTitle(const QString& title) +{ + m_titleLabel->setText(title); +} + +QString MainSpectrumGUI::getTitle() const +{ + return m_titleLabel->text(); +} + +bool MainSpectrumGUI::isOnMovingPad() +{ + return m_indexLabel->underMouse() || + m_spacerLabel->underMouse() || + m_titleLabel->underMouse() || + m_statusLabel->underMouse(); +} + +void MainSpectrumGUI::setIndex(int index) +{ + m_deviceSetIndex = index; + m_indexLabel->setText(tr("%1:%2").arg(getDeviceTypeTag()).arg(m_deviceSetIndex)); +} + +void MainSpectrumGUI::setDeviceType(DeviceType type) +{ + m_deviceType = type; + m_indexLabel->setStyleSheet(tr("QLabel { background-color: %1; qproperty-alignment: AlignCenter; }").arg(getDeviceTypeColor())); +} + +void MainSpectrumGUI::setToolTip(const QString& tooltip) +{ + m_titleLabel->setToolTip(tooltip); +} + +QString MainSpectrumGUI::getDeviceTypeColor() +{ + switch(m_deviceType) + { + case DeviceRx: + return "rgb(0, 128, 0)"; + case DeviceTx: + return "rgb(204, 0, 0)"; + case DeviceMIMO: + return "rgb(0, 0, 192)"; + default: + return "rgb(128, 128, 128)"; + } +} + +QString MainSpectrumGUI::getDeviceTypeTag() +{ + switch(m_deviceType) + { + case DeviceRx: + return "R"; + case DeviceTx: + return "T"; + case DeviceMIMO: + return "M"; + default: + return "X"; + } +} diff --git a/sdrgui/mainspectrum/mainspectrumgui.h b/sdrgui/mainspectrum/mainspectrumgui.h new file mode 100644 index 000000000..4a03ba983 --- /dev/null +++ b/sdrgui/mainspectrum/mainspectrumgui.h @@ -0,0 +1,110 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2022 Edouard Griffiths, F4EXB // +// // +// This program 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 as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program 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 V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef SDRGUI_MAINSPECTRUM_MAINSPECTRUMGUIGUI_H_ +#define SDRGUI_MAINSPECTRUM_MAINSPECTRUMGUIGUI_H_ + +#include +#include + +#include "util/messagequeue.h" +#include "export.h" + +class GLSpectrum; +class GLSpectrumGUI; +class QLabel; +class QPushButton; +class QVBoxLayout; +class QHBoxLayout; +class QSizeGrip; + +class SDRGUI_API MainSpectrumGUI : public QMdiSubWindow +{ + Q_OBJECT +public: + enum DeviceType + { + DeviceRx, + DeviceTx, + DeviceMIMO + }; + + MainSpectrumGUI(GLSpectrum *spectrum, GLSpectrumGUI *spectrumGUI, QWidget *parent = nullptr); + virtual ~MainSpectrumGUI(); + + void setDeviceType(DeviceType type); + DeviceType getDeviceType() const { return m_deviceType; } + void setTitle(const QString& title); + QString getTitle() const; + void setToolTip(const QString& tooltip); + void setIndex(int index); + int getIndex() const { return m_deviceSetIndex; } + void setWorkspaceIndex(int index) { m_workspaceIndex = index; } + int getWorkspaceIndex() const { return m_workspaceIndex; } + void setGeometryBytes(const QByteArray& blob) { m_geometryBytes = blob; } + const QByteArray& getGeometryBytes() const { return m_geometryBytes; } + +private: + GLSpectrum *m_spectrum; + GLSpectrumGUI *m_spectrumGUI; + int m_workspaceIndex; + QByteArray m_geometryBytes; + DeviceType m_deviceType; + int m_deviceSetIndex; + QString m_deviceTitle; + QString m_deviceTooltip; + QString m_helpURL; + + QLabel *m_indexLabel; + QLabel *m_spacerLabel; + QLabel *m_titleLabel; + QPushButton *m_helpButton; + QPushButton *m_moveButton; + QPushButton *m_shrinkButton; + QPushButton *m_hideButton; + QLabel *m_statusLabel; + QVBoxLayout *m_layouts; + QHBoxLayout *m_topLayout; + QHBoxLayout *m_spectrumLayout; + QHBoxLayout *m_spectrumGUILayout; + QHBoxLayout *m_bottomLayout; + QSizeGrip *m_sizeGripTopRight; + QSizeGrip *m_sizeGripBottomRight; + bool m_drag; + QPoint m_DragPosition; + + void closeEvent(QCloseEvent *event); + void mousePressEvent(QMouseEvent* event); + void mouseMoveEvent(QMouseEvent* event); + bool isOnMovingPad(); + QString getDeviceTypeColor(); + QString getDeviceTypeTag(); + +private slots: + void showHelp(); + void openMoveToWorkspaceDialog(); + void shrinkWindow(); + +signals: + void forceClose(); + void closing(); + void moveToWorkspace(int workspaceIndex); + void forceShrink(); +}; + + +#endif // SDRGUI_MAINSPECTRUM_MAINSPECTRUMGUIGUI_H_ diff --git a/sdrgui/mainwindow.cpp b/sdrgui/mainwindow.cpp index 76d0881ee..3b9d3f654 100644 --- a/sdrgui/mainwindow.cpp +++ b/sdrgui/mainwindow.cpp @@ -47,6 +47,7 @@ #include "feature/featureset.h" #include "feature/feature.h" #include "feature/featuregui.h" +#include "mainspectrum/mainspectrumgui.h" #include "commands/commandkeyreceiver.h" #include "gui/indicator.h" #include "gui/presetitem.h" @@ -261,7 +262,7 @@ MainWindow::MainWindow(qtwebapp::LoggerWithFile *logger, const MainParser& parse splash->showStatusMessage("load current configuration...", Qt::white); qDebug() << "MainWindow::MainWindow: load current configuration..."; - // loadPresetSettings(m_mainCore->m_settings.getWorkingPreset(), 0); + // loadDeviceSetPresetSettings(m_mainCore->m_settings.getWorkingPreset(), 0); m_apiAdapter = new WebAPIAdapter(); // loadFeatureSetPresetSettings(m_mainCore->m_settings.getWorkingFeatureSetPreset(), 0); loadConfiguration(m_mainCore->m_settings.getWorkingConfiguration()); @@ -389,16 +390,27 @@ void MainWindow::sampleSourceAdd(Workspace *workspace, int deviceIndex) deviceIndex = DeviceEnumerator::instance()->getFileInputDeviceIndex(); } - sampleSourceImplement(deviceSetIndex, deviceIndex, deviceAPI, m_deviceUIs.back(), workspace); + sampleSourceCreate(deviceSetIndex, deviceIndex, deviceAPI, m_deviceUIs.back()); + m_deviceUIs.back()->m_deviceGUI->setWorkspaceIndex(workspace->getIndex()); + m_deviceUIs.back()->m_mainSpectrumGUI->setWorkspaceIndex(workspace->getIndex()); + + QObject::connect( + m_deviceUIs.back()->m_mainSpectrumGUI, + &MainSpectrumGUI::moveToWorkspace, + this, + [=](int wsIndexDest){ this->mainSpectrumMove(m_deviceUIs.back()->m_mainSpectrumGUI, wsIndexDest); } + ); + + workspace->addToMdiArea(m_deviceUIs.back()->m_deviceGUI); + workspace->addToMdiArea(m_deviceUIs.back()->m_mainSpectrumGUI); emit m_mainCore->deviceSetAdded(deviceSetIndex, deviceAPI); } -void MainWindow::sampleSourceImplement( +void MainWindow::sampleSourceCreate( int deviceSetIndex, int deviceIndex, DeviceAPI *deviceAPI, - DeviceUISet *deviceUISet, - Workspace *workspace + DeviceUISet *deviceUISet ) { int selectedDeviceIndex = deviceIndex; @@ -506,15 +518,23 @@ void MainWindow::sampleSourceImplement( ); deviceAPI->getSampleSource()->setMessageQueueToGUI(deviceGUI->getInputMessageQueue()); deviceUISet->m_deviceGUI = deviceGUI; + const PluginInterface::SamplingDevice *selectedDevice = DeviceEnumerator::instance()->getRxSamplingDevice(selectedDeviceIndex); + deviceUISet->m_selectedDeviceId = selectedDevice->id; + deviceUISet->m_selectedDeviceSerial = selectedDevice->serial; + deviceUISet->m_selectedDeviceSequence = selectedDevice->sequence; + deviceUISet->m_selectedDeviceItemImdex = selectedDevice->deviceItemIndex; deviceUISet->m_deviceAPI->getSampleSource()->init(); // Finalize GUI setup and add it to workspace MDI deviceGUI->setDeviceType(DeviceGUI::DeviceRx); deviceGUI->setIndex(deviceSetIndex); - deviceGUI->setWorkspaceIndex(workspace->getIndex()); deviceGUI->setToolTip(samplingDevice->displayedName); deviceGUI->setTitle(samplingDevice->displayedName.split(" ")[0]); deviceGUI->setCurrentDeviceIndex(selectedDeviceIndex); - workspace->addToMdiArea((QMdiSubWindow*) deviceGUI); + MainSpectrumGUI *mainSpectrumGUI = deviceUISet->m_mainSpectrumGUI; + mainSpectrumGUI->setDeviceType(MainSpectrumGUI::DeviceRx); + mainSpectrumGUI->setIndex(deviceSetIndex); + mainSpectrumGUI->setToolTip(samplingDevice->displayedName); + mainSpectrumGUI->setTitle(samplingDevice->displayedName.split(" ")[0]); } void MainWindow::sampleSinkAdd(Workspace *workspace, int deviceIndex) @@ -552,16 +572,27 @@ void MainWindow::sampleSinkAdd(Workspace *workspace, int deviceIndex) deviceIndex = DeviceEnumerator::instance()->getFileOutputDeviceIndex(); // create a file output by default } - sampleSinkImplement(deviceSetIndex, deviceIndex, deviceAPI, m_deviceUIs.back(), workspace); + sampleSinkCreate(deviceSetIndex, deviceIndex, deviceAPI, m_deviceUIs.back()); + m_deviceUIs.back()->m_deviceGUI->setWorkspaceIndex(workspace->getIndex()); + m_deviceUIs.back()->m_mainSpectrumGUI->setWorkspaceIndex(workspace->getIndex()); + + QObject::connect( + m_deviceUIs.back()->m_mainSpectrumGUI, + &MainSpectrumGUI::moveToWorkspace, + this, + [=](int wsIndexDest){ this->mainSpectrumMove(m_deviceUIs.back()->m_mainSpectrumGUI, wsIndexDest); } + ); + + workspace->addToMdiArea(m_deviceUIs.back()->m_deviceGUI); + workspace->addToMdiArea(m_deviceUIs.back()->m_mainSpectrumGUI); emit m_mainCore->deviceSetAdded(deviceSetIndex, deviceAPI); } -void MainWindow::sampleSinkImplement( +void MainWindow::sampleSinkCreate( int deviceSetIndex, int deviceIndex, DeviceAPI *deviceAPI, - DeviceUISet *deviceUISet, - Workspace *workspace + DeviceUISet *deviceUISet ) { int selectedDeviceIndex = deviceIndex; @@ -669,15 +700,23 @@ void MainWindow::sampleSinkImplement( ); deviceAPI->getSampleSink()->setMessageQueueToGUI(deviceGUI->getInputMessageQueue()); deviceUISet->m_deviceGUI = deviceGUI; + const PluginInterface::SamplingDevice *selectedDevice = DeviceEnumerator::instance()->getRxSamplingDevice(selectedDeviceIndex); + deviceUISet->m_selectedDeviceId = selectedDevice->id; + deviceUISet->m_selectedDeviceSerial = selectedDevice->serial; + deviceUISet->m_selectedDeviceSequence = selectedDevice->sequence; + deviceUISet->m_selectedDeviceItemImdex = selectedDevice->deviceItemIndex; deviceUISet->m_deviceAPI->getSampleSink()->init(); // Finalize GUI setup and add it to workspace MDI deviceGUI->setDeviceType(DeviceGUI::DeviceTx); deviceGUI->setIndex(deviceSetIndex); - deviceGUI->setWorkspaceIndex(workspace->getIndex()); deviceGUI->setToolTip(samplingDevice->displayedName); deviceGUI->setTitle(samplingDevice->displayedName.split(" ")[0]); deviceGUI->setCurrentDeviceIndex(selectedDeviceIndex); - workspace->addToMdiArea((QMdiSubWindow*) deviceGUI); + MainSpectrumGUI *spectrumGUI = deviceUISet->m_mainSpectrumGUI; + spectrumGUI->setDeviceType(MainSpectrumGUI::DeviceTx); + spectrumGUI->setIndex(deviceSetIndex); + spectrumGUI->setToolTip(samplingDevice->displayedName); + spectrumGUI->setTitle(samplingDevice->displayedName.split(" ")[0]); } void MainWindow::sampleMIMOAdd(Workspace *workspace, int deviceIndex) @@ -723,16 +762,27 @@ void MainWindow::sampleMIMOAdd(Workspace *workspace, int deviceIndex) deviceIndex = DeviceEnumerator::instance()->getTestMIMODeviceIndex(); // create a test MIMO by default } - sampleMIMOImplement(deviceSetIndex, deviceIndex, deviceAPI, m_deviceUIs.back(), workspace); + sampleMIMOCreate(deviceSetIndex, deviceIndex, deviceAPI, m_deviceUIs.back()); + m_deviceUIs.back()->m_deviceGUI->setWorkspaceIndex(workspace->getIndex()); + m_deviceUIs.back()->m_mainSpectrumGUI->setWorkspaceIndex(workspace->getIndex()); + + QObject::connect( + m_deviceUIs.back()->m_mainSpectrumGUI, + &MainSpectrumGUI::moveToWorkspace, + this, + [=](int wsIndexDest){ this->mainSpectrumMove(m_deviceUIs.back()->m_mainSpectrumGUI, wsIndexDest); } + ); + + workspace->addToMdiArea(m_deviceUIs.back()->m_deviceGUI); + workspace->addToMdiArea(m_deviceUIs.back()->m_mainSpectrumGUI); emit m_mainCore->deviceSetAdded(deviceSetIndex, deviceAPI); } -void MainWindow::sampleMIMOImplement( +void MainWindow::sampleMIMOCreate( int deviceSetIndex, int deviceIndex, DeviceAPI *deviceAPI, - DeviceUISet *deviceUISet, - Workspace *workspace + DeviceUISet *deviceUISet ) { int selectedDeviceIndex = deviceIndex; @@ -806,20 +856,28 @@ void MainWindow::sampleMIMOImplement( ); deviceAPI->getSampleMIMO()->setMessageQueueToGUI(deviceGUI->getInputMessageQueue()); deviceUISet->m_deviceGUI = deviceGUI; + const PluginInterface::SamplingDevice *selectedDevice = DeviceEnumerator::instance()->getRxSamplingDevice(selectedDeviceIndex); + deviceUISet->m_selectedDeviceId = selectedDevice->id; + deviceUISet->m_selectedDeviceSerial = selectedDevice->serial; + deviceUISet->m_selectedDeviceSequence = selectedDevice->sequence; + deviceUISet->m_selectedDeviceItemImdex = selectedDevice->deviceItemIndex; deviceUISet->m_deviceAPI->getSampleMIMO()->init(); // Finalize GUI setup and add it to workspace MDI deviceGUI->setDeviceType(DeviceGUI::DeviceMIMO); deviceGUI->setIndex(deviceSetIndex); - deviceGUI->setWorkspaceIndex(workspace->getIndex()); deviceGUI->setToolTip(samplingDevice->displayedName); deviceGUI->setTitle(samplingDevice->displayedName.split(" ")[0]); deviceGUI->setCurrentDeviceIndex(selectedDeviceIndex); - workspace->addToMdiArea((QMdiSubWindow*) deviceGUI); + MainSpectrumGUI *spectrumGUI = deviceUISet->m_mainSpectrumGUI; + spectrumGUI->setDeviceType(MainSpectrumGUI::DeviceMIMO); + spectrumGUI->setIndex(deviceSetIndex); + spectrumGUI->setToolTip(samplingDevice->displayedName); + spectrumGUI->setTitle(samplingDevice->displayedName.split(" ")[0]); } void MainWindow::removeLastDevice() { - int removedTabIndex = m_deviceUIs.size() - 1; + int removedDeviceSetIndex = m_deviceUIs.size() - 1; if (m_deviceUIs.back()->m_deviceSourceEngine) // source tab { @@ -837,12 +895,12 @@ void MainWindow::removeLastDevice() m_deviceUIs.back()->m_deviceGUI->destroy(); m_deviceUIs.back()->m_deviceAPI->resetSamplingDeviceId(); m_deviceUIs.back()->m_deviceAPI->getPluginInterface()->deleteSampleSourcePluginInstanceInput( - m_deviceUIs.back()->m_deviceAPI->getSampleSource()); + m_deviceUIs.back()->m_deviceAPI->getSampleSource()); m_deviceUIs.back()->m_deviceAPI->clearBuddiesLists(); // clear old API buddies lists // ui->tabChannels->removeTab(ui->tabChannels->count() - 1); - m_deviceWidgetTabs.removeLast(); + // m_deviceWidgetTabs.removeLast(); // restoreDeviceTabs(); DeviceAPI *sourceAPI = m_deviceUIs.back()->m_deviceAPI; @@ -869,12 +927,12 @@ void MainWindow::removeLastDevice() m_deviceUIs.back()->m_deviceGUI->destroy(); m_deviceUIs.back()->m_deviceAPI->resetSamplingDeviceId(); m_deviceUIs.back()->m_deviceAPI->getPluginInterface()->deleteSampleSinkPluginInstanceOutput( - m_deviceUIs.back()->m_deviceAPI->getSampleSink()); + m_deviceUIs.back()->m_deviceAPI->getSampleSink()); m_deviceUIs.back()->m_deviceAPI->clearBuddiesLists(); // clear old API buddies lists // ui->tabChannels->removeTab(ui->tabChannels->count() - 1); - m_deviceWidgetTabs.removeLast(); + // m_deviceWidgetTabs.removeLast(); // restoreDeviceTabs(); DeviceAPI *sinkAPI = m_deviceUIs.back()->m_deviceAPI; @@ -902,11 +960,11 @@ void MainWindow::removeLastDevice() m_deviceUIs.back()->m_deviceGUI->destroy(); m_deviceUIs.back()->m_deviceAPI->resetSamplingDeviceId(); m_deviceUIs.back()->m_deviceAPI->getPluginInterface()->deleteSampleMIMOPluginInstanceMIMO( - m_deviceUIs.back()->m_deviceAPI->getSampleMIMO()); + m_deviceUIs.back()->m_deviceAPI->getSampleMIMO()); // ui->tabChannels->removeTab(ui->tabChannels->count() - 1); - m_deviceWidgetTabs.removeLast(); + // m_deviceWidgetTabs.removeLast(); // restoreDeviceTabs(); DeviceAPI *mimoAPI = m_deviceUIs.back()->m_deviceAPI; @@ -920,7 +978,7 @@ void MainWindow::removeLastDevice() m_deviceUIs.pop_back(); m_mainCore->removeLastDeviceSet(); - emit m_mainCore->deviceSetRemoved(removedTabIndex); + emit m_mainCore->deviceSetRemoved(removedDeviceSetIndex); } void MainWindow::addFeatureSet() @@ -990,75 +1048,41 @@ void MainWindow::loadSettings() m_mainCore->setLoggingOptions(); } -void MainWindow::loadPresetSettings(const Preset* preset, int tabIndex) +void MainWindow::loadDeviceSetPresetSettings(const Preset* preset, int deviceSetIndex) { - qDebug("MainWindow::loadPresetSettings: preset [%s | %s]", + qDebug("MainWindow::loadDeviceSetPresetSettings: preset [%s | %s]", qPrintable(preset->getGroup()), qPrintable(preset->getDescription())); - if (tabIndex >= 0) + if (deviceSetIndex >= 0) { - DeviceUISet *deviceUI = m_deviceUIs[tabIndex]; - deviceUI->m_spectrumGUI->deserialize(preset->getSpectrumConfig()); - deviceUI->m_deviceAPI->loadSamplingDeviceSettings(preset); - - if (deviceUI->m_deviceSourceEngine) { // source device - deviceUI->loadRxChannelSettings(preset, m_pluginManager->getPluginAPI()); - } else if (deviceUI->m_deviceSinkEngine) { // sink device - deviceUI->loadTxChannelSettings(preset, m_pluginManager->getPluginAPI()); - } else if (deviceUI->m_deviceMIMOEngine) { // MIMO device - deviceUI->loadMIMOChannelSettings(preset, m_pluginManager->getPluginAPI()); - } + DeviceUISet *deviceUISet = m_deviceUIs[deviceSetIndex]; + deviceUISet->loadDeviceSetSettings(preset, m_pluginManager->getPluginAPI(), &m_workspaces, nullptr); } - m_spectrumToggleViewAction->setChecked(preset->getShowSpectrum()); + // m_spectrumToggleViewAction->setChecked(preset->getShowSpectrum()); - // has to be last step - if (!preset->getLayout().isEmpty()) { - restoreState(preset->getLayout()); - } + // // has to be last step + // if (!preset->getLayout().isEmpty()) { + // restoreState(preset->getLayout()); + // } // tabifyDockWidget(ui->presetDock, ui->commandsDock); // override this setting // ui->presetDock->raise(); } -void MainWindow::savePresetSettings(Preset* preset, int tabIndex) +void MainWindow::saveDeviceSetPresetSettings(Preset* preset, int deviceSetIndex) { - qDebug("MainWindow::savePresetSettings: preset [%s | %s]", + qDebug("MainWindow::saveDeviceSetPresetSettings: preset [%s | %s]", qPrintable(preset->getGroup()), qPrintable(preset->getDescription())); // Save from currently selected source tab //int currentSourceTabIndex = ui->tabInputsView->currentIndex(); - DeviceUISet *deviceUI = m_deviceUIs[tabIndex]; - - if (deviceUI->m_deviceSourceEngine) // source device - { - preset->setSpectrumConfig(deviceUI->m_spectrumGUI->serialize()); - preset->clearChannels(); - preset->setSourcePreset(); - deviceUI->saveRxChannelSettings(preset); - deviceUI->m_deviceAPI->saveSamplingDeviceSettings(preset); - } - else if (deviceUI->m_deviceSinkEngine) // sink device - { - preset->setSpectrumConfig(deviceUI->m_spectrumGUI->serialize()); - preset->clearChannels(); - preset->setSinkPreset(); - deviceUI->saveTxChannelSettings(preset); - deviceUI->m_deviceAPI->saveSamplingDeviceSettings(preset); - } - else if (deviceUI->m_deviceMIMOEngine) // MIMO device - { - preset->setSpectrumConfig(deviceUI->m_spectrumGUI->serialize()); - preset->clearChannels(); - preset->setMIMOPreset(); - deviceUI->saveMIMOChannelSettings(preset); - deviceUI->m_deviceAPI->saveSamplingDeviceSettings(preset); - } - - preset->setShowSpectrum(m_spectrumToggleViewAction->isChecked()); - preset->setLayout(saveState()); + DeviceUISet *deviceUISet = m_deviceUIs[deviceSetIndex]; + deviceUISet->saveDeviceSetSettings(preset); + // preset->setShowSpectrum(m_spectrumToggleViewAction->isChecked()); + // preset->setLayout(saveState()); } void MainWindow::loadFeatureSetPresetSettings(const FeatureSetPreset* preset, int featureSetIndex, Workspace *workspace) @@ -1071,7 +1095,7 @@ void MainWindow::loadFeatureSetPresetSettings(const FeatureSetPreset* preset, in { FeatureUISet *featureSetUI = m_featureUIs[featureSetIndex]; qDebug("MainWindow::loadFeatureSetPresetSettings: m_apiAdapter: %p", m_apiAdapter); - featureSetUI->loadFeatureSetSettings(preset, m_pluginManager->getPluginAPI(), m_apiAdapter, workspace); + featureSetUI->loadFeatureSetSettings(preset, m_pluginManager->getPluginAPI(), m_apiAdapter, &m_workspaces, workspace); } } @@ -1091,14 +1115,20 @@ void MainWindow::saveFeatureSetPresetSettings(FeatureSetPreset* preset, int feat void MainWindow::loadConfiguration(const Configuration *configuration) { - qDebug("MainWindow::loadConfiguration: configuration [%s | %s] %d workspaces", + qDebug("MainWindow::loadConfiguration: configuration [%s | %s] %d workspace(s) - %d device set(s) - %d feature(s)", qPrintable(configuration->getGroup()), qPrintable(configuration->getDescription()), - configuration->getNumberOfWorkspaces() + configuration->getNumberOfWorkspaces(), + configuration->getDeviceSetPresets().size(), + configuration->getFeatureSetPreset().getFeatureCount() ); // Wipe out everything first + // Device sets + while (m_deviceUIs.size() > 0) { + removeLastDevice(); + } // Features m_featureUIs[0]->freeFeatures(); // Workspaces @@ -1113,8 +1143,66 @@ void MainWindow::loadConfiguration(const Configuration *configuration) for (int i = 0; i < configuration->getNumberOfWorkspaces(); i++) { addWorkspace(); } + + if (m_workspaces.size() <= 0) { // cannot go further if there are no workspaces + return; + } + + // Device sets + const QList& deviceSetPresets = configuration->getDeviceSetPresets(); + + for (const auto& deviceSetPreset : deviceSetPresets) + { + if (deviceSetPreset.isSourcePreset()) + { + int bestDeviceIndex = DeviceEnumerator::instance()->getBestRxSamplingDeviceIndex( + deviceSetPreset.getSelectedDevice().m_deviceId, + deviceSetPreset.getSelectedDevice().m_deviceSerial, + deviceSetPreset.getSelectedDevice().m_deviceSequence, + deviceSetPreset.getSelectedDevice().m_deviceItemIndex + ); + int workspaceIndex = deviceSetPreset.getDeviceWorkspaceIndex() < m_workspaces.size() ? + deviceSetPreset.getDeviceWorkspaceIndex() : + 0; + sampleSourceAdd(m_workspaces[workspaceIndex], bestDeviceIndex); + } + else if (deviceSetPreset.isSinkPreset()) + { + int bestDeviceIndex = DeviceEnumerator::instance()->getBestTxSamplingDeviceIndex( + deviceSetPreset.getSelectedDevice().m_deviceId, + deviceSetPreset.getSelectedDevice().m_deviceSerial, + deviceSetPreset.getSelectedDevice().m_deviceSequence, + deviceSetPreset.getSelectedDevice().m_deviceItemIndex + ); + int workspaceIndex = deviceSetPreset.getDeviceWorkspaceIndex() < m_workspaces.size() ? + deviceSetPreset.getDeviceWorkspaceIndex() : + 0; + sampleSinkAdd(m_workspaces[workspaceIndex], bestDeviceIndex); + } else if (deviceSetPreset.isMIMOPreset()) + { + int bestDeviceIndex = DeviceEnumerator::instance()->getBestMIMOSamplingDeviceIndex( + deviceSetPreset.getSelectedDevice().m_deviceId, + deviceSetPreset.getSelectedDevice().m_deviceSerial, + deviceSetPreset.getSelectedDevice().m_deviceSequence + ); + int workspaceIndex = deviceSetPreset.getDeviceWorkspaceIndex() < m_workspaces.size() ? + deviceSetPreset.getDeviceWorkspaceIndex() : + 0; + sampleMIMOAdd(m_workspaces[workspaceIndex], bestDeviceIndex); + } + + m_deviceUIs.back()->m_deviceGUI->restoreGeometry(deviceSetPreset.getDeviceGeometry()); + m_deviceUIs.back()->m_mainSpectrumGUI->restoreGeometry(deviceSetPreset.getSpectrumGeometry()); + m_deviceUIs.back()->loadDeviceSetSettings(&deviceSetPreset, m_pluginManager->getPluginAPI(), &m_workspaces, nullptr); + } // Features - m_featureUIs[0]->loadFeatureSetSettings(&configuration->getFeatureSetPreset(), m_pluginManager->getPluginAPI(), m_apiAdapter, m_workspaces); + m_featureUIs[0]->loadFeatureSetSettings( + &configuration->getFeatureSetPreset(), + m_pluginManager->getPluginAPI(), + m_apiAdapter, + &m_workspaces, + nullptr + ); for (int i = 0; i < m_featureUIs[0]->getNumberOfFeatures(); i++) { @@ -1142,6 +1230,18 @@ void MainWindow::saveConfiguration(Configuration *configuration) ); configuration->clearData(); + QList& deviceSetPresets = configuration->getDeviceSetPresets(); + + for (const auto& deviceUISet : m_deviceUIs) + { + deviceSetPresets.push_back(Preset()); + deviceUISet->saveDeviceSetSettings(&deviceSetPresets.back()); + deviceSetPresets.back().setSpectrumGeometry(deviceUISet->m_mainSpectrumGUI->saveGeometry()); + deviceSetPresets.back().setSpectrumWorkspaceIndex(deviceUISet->m_mainSpectrumGUI->getWorkspaceIndex()); + deviceSetPresets.back().setDeviceGeometry(deviceUISet->m_deviceGUI->saveGeometry()); + deviceSetPresets.back().setDeviceWorkspaceIndex(deviceUISet->m_deviceGUI->getWorkspaceIndex()); + } + m_featureUIs[0]->saveFeatureSetSettings(&configuration->getFeatureSetPreset()); for (const auto& workspace : m_workspaces) { @@ -1279,15 +1379,14 @@ void MainWindow::closeEvent(QCloseEvent *closeEvent) s.setValue("mainWindowGeometry", qCompress(saveGeometry()).toBase64()); s.setValue("mainWindowState", qCompress(saveState()).toBase64()); - // savePresetSettings(m_mainCore->m_settings.getWorkingPreset(), 0); + // saveDeviceSetPresetSettings(m_mainCore->m_settings.getWorkingPreset(), 0); // saveFeatureSetPresetSettings(m_mainCore->m_settings.getWorkingFeatureSetPreset(), 0); saveConfiguration(m_mainCore->m_settings.getWorkingConfiguration()); m_mainCore->m_settings.save(); - // while (m_deviceUIs.size() > 0) - // { - // removeLastDevice(); - // } + while (m_deviceUIs.size() > 0) { + removeLastDevice(); + } closeEvent->accept(); } @@ -1348,13 +1447,13 @@ QTreeWidgetItem* MainWindow::addPresetToTree(const Preset* preset) void MainWindow::applySettings() { - // loadPresetSettings(m_mainCore->m_settings.getWorkingPreset(), 0); + // loadDeviceSetPresetSettings(m_mainCore->m_settings.getWorkingPreset(), 0); // loadFeatureSetPresetSettings(m_mainCore->m_settings.getWorkingFeatureSetPreset(), 0); loadConfiguration(m_mainCore->m_settings.getWorkingConfiguration()); m_mainCore->m_settings.sortPresets(); - int middleIndex = m_mainCore->m_settings.getPresetCount() / 2; - QTreeWidgetItem *treeItem; + // int middleIndex = m_mainCore->m_settings.getPresetCount() / 2; + // QTreeWidgetItem *treeItem; // ui->presetTree->clear(); // for (int i = 0; i < m_mainCore->m_settings.getPresetCount(); ++i) @@ -1381,13 +1480,13 @@ bool MainWindow::handleMessage(const Message& cmd) if (MainCore::MsgLoadPreset::match(cmd)) { MainCore::MsgLoadPreset& notif = (MainCore::MsgLoadPreset&) cmd; - loadPresetSettings(notif.getPreset(), notif.getDeviceSetIndex()); + loadDeviceSetPresetSettings(notif.getPreset(), notif.getDeviceSetIndex()); return true; } else if (MainCore::MsgSavePreset::match(cmd)) { // MainCore::MsgSavePreset& notif = (MainCore::MsgSavePreset&) cmd; - // savePresetSettings(notif.getPreset(), notif.getDeviceSetIndex()); + // saveDeviceSetPresetSettings(notif.getPreset(), notif.getDeviceSetIndex()); // if (notif.isNewPreset()) { ui->presetTree->setCurrentItem(addPresetToTree(notif.getPreset())); } // m_mainCore->m_settings.sortPresets(); // m_mainCore->m_settings.save(); @@ -1747,7 +1846,7 @@ void MainWindow::on_presetSave_clicked() // if(dlg.exec() == QDialog::Accepted) { // Preset* preset = m_mainCore->m_settings.newPreset(dlg.group(), dlg.description()); - // savePresetSettings(preset, ui->tabInputsView->currentIndex()); + // saveDeviceSetPresetSettings(preset, ui->tabInputsView->currentIndex()); // ui->presetTree->setCurrentItem(addPresetToTree(preset)); // } @@ -1769,7 +1868,7 @@ void MainWindow::on_presetUpdate_clicked() // if (preset != 0) // { // Preset* preset_mod = const_cast(preset); - // savePresetSettings(preset_mod, ui->tabInputsView->currentIndex()); + // saveDeviceSetPresetSettings(preset_mod, ui->tabInputsView->currentIndex()); // changedPreset = preset; // } // } @@ -1943,7 +2042,7 @@ void MainWindow::on_presetImport_clicked() void MainWindow::on_action_saveAll_triggered() { - // savePresetSettings(m_mainCore->m_settings.getWorkingPreset(), ui->tabInputsView->currentIndex()); + // saveDeviceSetPresetSettings(m_mainCore->m_settings.getWorkingPreset(), ui->tabInputsView->currentIndex()); // saveFeatureSetPresetSettings(m_mainCore->m_settings.getWorkingFeatureSetPreset(), ui->tabFeatures->currentIndex()); saveConfiguration(m_mainCore->m_settings.getWorkingConfiguration()); m_mainCore->m_settings.save(); @@ -1971,7 +2070,7 @@ void MainWindow::on_presetLoad_clicked() // return; // } - // loadPresetSettings(preset, ui->tabInputsView->currentIndex()); + // loadDeviceSetPresetSettings(preset, ui->tabInputsView->currentIndex()); } void MainWindow::on_presetDelete_clicked() @@ -2215,7 +2314,9 @@ void MainWindow::sampleSourceChange(int deviceSetIndex, int newDeviceIndex, Work deviceUISet->m_deviceAPI->getPluginInterface()->deleteSampleSourcePluginInstanceInput(deviceUISet->m_deviceAPI->getSampleSource()); deviceUISet->m_deviceAPI->clearBuddiesLists(); // clear old API buddies lists - sampleSourceImplement(deviceSetIndex, newDeviceIndex, deviceUISet->m_deviceAPI, deviceUISet, workspace); + sampleSourceCreate(deviceSetIndex, newDeviceIndex, deviceUISet->m_deviceAPI, deviceUISet); + deviceUISet->m_deviceGUI->setWorkspaceIndex(workspace->getIndex()); + workspace->addToMdiArea(deviceUISet->m_deviceGUI); } } @@ -2235,7 +2336,9 @@ void MainWindow::sampleSinkChange(int deviceSetIndex, int newDeviceIndex, Worksp deviceUISet->m_deviceAPI->getPluginInterface()->deleteSampleSinkPluginInstanceOutput(deviceUISet->m_deviceAPI->getSampleSink()); deviceUISet->m_deviceAPI->clearBuddiesLists(); // clear old API buddies lists - sampleSinkImplement(deviceSetIndex, newDeviceIndex, deviceUISet->m_deviceAPI, deviceUISet, workspace); + sampleSourceCreate(deviceSetIndex, newDeviceIndex, deviceUISet->m_deviceAPI, deviceUISet); + deviceUISet->m_deviceGUI->setWorkspaceIndex(workspace->getIndex()); + workspace->addToMdiArea(deviceUISet->m_deviceGUI); } } @@ -2254,7 +2357,9 @@ void MainWindow::sampleMIMOChange(int deviceSetIndex, int newDeviceIndex, Worksp deviceUISet->m_deviceAPI->resetSamplingDeviceId(); deviceUISet->m_deviceAPI->getPluginInterface()->deleteSampleMIMOPluginInstanceMIMO(deviceUISet->m_deviceAPI->getSampleMIMO()); - sampleMIMOImplement(deviceSetIndex, deviceSetIndex, deviceUISet->m_deviceAPI, deviceUISet, workspace); + sampleSourceCreate(deviceSetIndex, newDeviceIndex, deviceUISet->m_deviceAPI, deviceUISet); + deviceUISet->m_deviceGUI->setWorkspaceIndex(workspace->getIndex()); + workspace->addToMdiArea(deviceUISet->m_deviceGUI); } } @@ -2383,6 +2488,19 @@ void MainWindow::deviceMove(DeviceGUI *gui, int wsIndexDestnation) m_workspaces[wsIndexDestnation]->addToMdiArea(gui); } +void MainWindow::mainSpectrumMove(MainSpectrumGUI *gui, int wsIndexDestnation) +{ + int wsIndexOrigin = gui->getWorkspaceIndex(); + + if (wsIndexOrigin == wsIndexDestnation) { + return; + } + + m_workspaces[wsIndexOrigin]->removeFromMdiArea(gui); + gui->setWorkspaceIndex(wsIndexDestnation); + m_workspaces[wsIndexDestnation]->addToMdiArea(gui); +} + void MainWindow::openFeaturePresetsDialog(QPoint p, Workspace *workspace) { FeaturePresetsDialog dialog; @@ -2390,7 +2508,8 @@ void MainWindow::openFeaturePresetsDialog(QPoint p, Workspace *workspace) dialog.setPresets(m_mainCore->m_settings.getFeatureSetPresets()); dialog.setPluginAPI(m_pluginManager->getPluginAPI()); dialog.setWebAPIAdapter(m_apiAdapter); - dialog.setWorkspace(workspace); + dialog.setCurrentWorkspace(workspace); + dialog.setWorkspaces(&m_workspaces); dialog.populateTree(); dialog.move(p); dialog.exec(); diff --git a/sdrgui/mainwindow.h b/sdrgui/mainwindow.h index 65d2673b8..1a5151365 100644 --- a/sdrgui/mainwindow.h +++ b/sdrgui/mainwindow.h @@ -40,6 +40,7 @@ class DSPDeviceSourceEngine; class DSPDeviceSinkEngine; class Indicator; class GLSpectrumGUI; +class MainSpectrumGUI; class PluginAPI; class ChannelGUI; class ChannelMarker; @@ -123,8 +124,8 @@ private: QProcess *m_fftWisdomProcess; void loadSettings(); - void loadPresetSettings(const Preset* preset, int tabIndex); - void savePresetSettings(Preset* preset, int tabIndex); + void loadDeviceSetPresetSettings(const Preset* preset, int deviceSetIndex); + void saveDeviceSetPresetSettings(Preset* preset, int deviceSetIndex); void loadFeatureSetPresetSettings(const FeatureSetPreset* preset, int featureSetIndex, Workspace *workspace); void saveFeatureSetPresetSettings(FeatureSetPreset* preset, int featureSetIndex); @@ -145,26 +146,23 @@ private: void sampleSourceChange(int deviceSetIndex, int newDeviceIndex, Workspace *workspace); void sampleSinkChange(int deviceSetIndex, int newDeviceIndex, Workspace *workspace); void sampleMIMOChange(int deviceSetIndex, int newDeviceIndex, Workspace *workspace); - void sampleSourceImplement( + void sampleSourceCreate( int deviceSetIndex, int deviceIndex, DeviceAPI *deviceAPI, - DeviceUISet *deviceUISet, - Workspace *workspace + DeviceUISet *deviceUISet ); - void sampleSinkImplement( + void sampleSinkCreate( int deviceSetIndex, int deviceIndex, DeviceAPI *deviceAPI, - DeviceUISet *deviceUISet, - Workspace *workspace + DeviceUISet *deviceUISet ); - void sampleMIMOImplement( + void sampleMIMOCreate( int deviceSetIndex, int deviceIndex, DeviceAPI *deviceAPI, - DeviceUISet *deviceUISet, - Workspace *workspace + DeviceUISet *deviceUISet ); void deleteFeature(int featureSetIndex, int featureIndex); @@ -209,6 +207,7 @@ private slots: void featureMove(FeatureGUI *gui, int wsIndexDestnation); void openFeaturePresetsDialog(QPoint p, Workspace *workspace); void deviceMove(DeviceGUI *gui, int wsIndexDestnation); + void mainSpectrumMove(MainSpectrumGUI *gui, int wsIndexDestnation); void on_action_Quick_Start_triggered(); void on_action_Main_Window_triggered(); void on_action_Loaded_Plugins_triggered(); diff --git a/sdrgui/resources/channels.png b/sdrgui/resources/channels.png new file mode 100644 index 0000000000000000000000000000000000000000..14c223fe418d78989d434b87769bc43355be17d6 GIT binary patch literal 4926 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 JlrN7={V#bgKNtW2 literal 0 HcmV?d00001 diff --git a/sdrgui/resources/res.qrc b/sdrgui/resources/res.qrc index bb851fc62..4b39bd761 100644 --- a/sdrgui/resources/res.qrc +++ b/sdrgui/resources/res.qrc @@ -117,6 +117,8 @@ tx.png mimo.png tool.png + tool_add.png + tool_star.png gear.png corner_topleft.png corner_topright.png @@ -130,6 +132,8 @@ shrink.png exit.png hide.png + channels.png + channels_add.png LiberationMono-Regular.ttf LiberationSans-Regular.ttf diff --git a/sdrgui/resources/tool_add.png b/sdrgui/resources/tool_add.png new file mode 100644 index 0000000000000000000000000000000000000000..479974212c63469605517813abb90f8d1a4d789d GIT binary patch literal 5953 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 GWB(0JOmzML literal 0 HcmV?d00001 diff --git a/sdrgui/resources/tool_star.png b/sdrgui/resources/tool_star.png new file mode 100644 index 0000000000000000000000000000000000000000..530c6a6b11b589f68650f98f87daf15544e0a74e GIT binary patch literal 5865 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 cll9@3Z7D})xM682&?v#I% Date: Sat, 9 Apr 2022 22:05:02 +0200 Subject: [PATCH 006/115] Update spectrum GUI layout --- sdrgui/gui/glspectrumgui.cpp | 4 +- sdrgui/gui/glspectrumgui.ui | 217 ++++++++++++++++++++++------------- 2 files changed, 138 insertions(+), 83 deletions(-) diff --git a/sdrgui/gui/glspectrumgui.cpp b/sdrgui/gui/glspectrumgui.cpp index fd0d9390d..8ac0916c8 100644 --- a/sdrgui/gui/glspectrumgui.cpp +++ b/sdrgui/gui/glspectrumgui.cpp @@ -48,13 +48,15 @@ GLSpectrumGUI::GLSpectrumGUI(QWidget* parent) : ui->setupUi(this); // Use the custom flow layout for the 3 main horizontal layouts (lines) + ui->verticalLayout->removeItem(ui->Line4Layout); ui->verticalLayout->removeItem(ui->Line3Layout); ui->verticalLayout->removeItem(ui->Line2Layout); ui->verticalLayout->removeItem(ui->Line1Layout); FlowLayout *flowLayout = new FlowLayout(nullptr, 1, 1, 1); + flowLayout->addItem(ui->Line3Layout); flowLayout->addItem(ui->Line1Layout); flowLayout->addItem(ui->Line2Layout); - flowLayout->addItem(ui->Line3Layout); + flowLayout->addItem(ui->Line4Layout); ui->verticalLayout->addItem(flowLayout); on_linscale_toggled(false); diff --git a/sdrgui/gui/glspectrumgui.ui b/sdrgui/gui/glspectrumgui.ui index 28b87aa85..68fa381d2 100644 --- a/sdrgui/gui/glspectrumgui.ui +++ b/sdrgui/gui/glspectrumgui.ui @@ -6,8 +6,8 @@ 0 0 - 359 - 106 + 364 + 137 @@ -357,32 +357,23 @@ - - - Play/Pause spectrum display + + + + 0 + 0 + - - - :/pause.png - :/play.png:/pause.png - - - true - - - - - - - Qt::Horizontal - - + - 40 - 20 + 5 + 22 - + + + + @@ -539,61 +530,23 @@ - - - Left: toggle websocket spectrum - Right: websocket spectrum parameters + + + + 0 + 0 + - - - :/stream.png - - - - true - - - - - - - Open spectrum markers dialog - - - - :/gridpolar.png - - - - false - - - - - - - Left: toggle relative / calibrated power - Right: open calibration dialog - - - - :/ruler.png:/ruler.png - - - true - - - - - - - Qt::Horizontal - - + - 40 - 20 + 5 + 22 - + + + + @@ -604,6 +557,12 @@ + + + 0 + 0 + + Clear spectrum histogram @@ -874,17 +833,111 @@ - - - Qt::Horizontal + + + + 0 + 0 + - + - 40 - 20 + 5 + 22 - + + + + + + + + + + + + + Play/Pause spectrum display + + + + :/pause.png + :/play.png:/pause.png + + + true + + + + + + + Left: toggle websocket spectrum - Right: websocket spectrum parameters + + + + :/stream.png + + + + true + + + + + + + + 0 + 0 + + + + Open spectrum markers dialog + + + + :/gridpolar.png + + + + false + + + + + + + Left: toggle relative / calibrated power - Right: open calibration dialog + + + + :/ruler.png:/ruler.png + + + true + + + + + + + + 0 + 0 + + + + + 5 + 22 + + + + + + From cfde845b5af71d366ee6fc643bd10e40ad9e4450 Mon Sep 17 00:00:00 2001 From: f4exb Date: Sat, 9 Apr 2022 22:05:55 +0200 Subject: [PATCH 007/115] Massive UI revamping (v7): devices UIs update --- .../bladerf2mimo/bladerf2mimogui.ui | 32 ++---------- .../samplemimo/limesdrmimo/limesdrmimogui.ui | 41 ++++------------ plugins/samplemimo/metismiso/metismisogui.ui | 29 +++-------- .../plutosdrmimo/plutosdrmimogui.ui | 25 ++-------- .../samplemimo/testmosync/testmosyncgui.ui | 25 ++-------- plugins/samplemimo/xtrxmimo/xtrxmimogui.ui | 41 ++++------------ .../samplesink/audiooutput/audiooutputgui.ui | 25 ++-------- .../bladerf1output/bladerf1outputgui.ui | 32 ++---------- .../bladerf2output/bladerf2outputgui.ui | 32 ++---------- .../hackrfoutput/hackrfoutputgui.ui | 25 ++-------- .../limesdroutput/limesdroutputgui.ui | 41 ++++------------ .../samplesink/localoutput/localoutputgui.ui | 28 +---------- .../plutosdroutput/plutosdroutputgui.ui | 25 ++-------- .../remoteoutput/remoteoutputgui.ui | 21 +------- .../soapysdroutput/soapysdroutputgui.ui | 12 ++--- plugins/samplesink/testsink/testsinkgui.ui | 25 ++-------- .../samplesink/usrpoutput/usrpoutputgui.ui | 46 +++++------------ .../samplesink/xtrxoutput/xtrxoutputgui.ui | 41 ++++------------ plugins/samplesource/airspy/airspygui.ui | 32 ++---------- plugins/samplesource/airspyhf/airspyhfgui.ui | 35 ++++--------- .../samplesource/audioinput/audioinputgui.ui | 25 ++-------- .../bladerf1input/bladerf1inputgui.ui | 32 ++---------- .../bladerf2input/bladerf2inputgui.ui | 32 ++---------- plugins/samplesource/fcdpro/fcdprogui.ui | 25 ++-------- .../samplesource/fcdproplus/fcdproplusgui.ui | 25 ++-------- plugins/samplesource/kiwisdr/kiwisdrgui.ui | 20 ++------ .../limesdrinput/limesdrinputgui.ui | 41 ++++------------ .../samplesource/localinput/localinputgui.ui | 21 +------- plugins/samplesource/perseus/perseusgui.ui | 25 ++-------- .../plutosdrinput/plutosdrinputgui.ui | 25 ++-------- .../remoteinput/remoteinputgui.ui | 33 +++---------- plugins/samplesource/sdrplay/sdrplaygui.ui | 32 ++---------- .../samplesource/sdrplayv3/sdrplayv3gui.ui | 49 ++++--------------- .../sigmffileinput/sigmffileinputgui.ui | 21 +------- .../soapysdrinput/soapysdrinputgui.ui | 12 ++--- .../samplesource/testsource/testsourcegui.ui | 16 ++++-- .../samplesource/usrpinput/usrpinputgui.ui | 46 +++++------------ .../samplesource/xtrxinput/xtrxinputgui.ui | 41 ++++------------ 38 files changed, 218 insertions(+), 916 deletions(-) diff --git a/plugins/samplemimo/bladerf2mimo/bladerf2mimogui.ui b/plugins/samplemimo/bladerf2mimo/bladerf2mimogui.ui index 30750ed28..12fc5c79a 100644 --- a/plugins/samplemimo/bladerf2mimo/bladerf2mimogui.ui +++ b/plugins/samplemimo/bladerf2mimo/bladerf2mimogui.ui @@ -6,8 +6,8 @@ 0 0 - 366 - 228 + 360 + 220 @@ -18,8 +18,8 @@ - 350 - 220 + 360 + 0 @@ -761,30 +761,6 @@ - - - - Qt::Horizontal - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - diff --git a/plugins/samplemimo/limesdrmimo/limesdrmimogui.ui b/plugins/samplemimo/limesdrmimo/limesdrmimogui.ui index bdc8329e5..f5e2166b0 100644 --- a/plugins/samplemimo/limesdrmimo/limesdrmimogui.ui +++ b/plugins/samplemimo/limesdrmimo/limesdrmimogui.ui @@ -7,7 +7,7 @@ 0 0 360 - 290 + 286 @@ -19,7 +19,7 @@ 360 - 290 + 0 @@ -1246,6 +1246,7 @@ + Ubuntu 8 @@ -1319,30 +1320,6 @@ QToolTip{background-color: white; color: black;} - - - - Qt::Horizontal - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - @@ -1358,9 +1335,9 @@ QToolTip{background-color: white; color: black;}

gui/buttonswitch.h
- ExternalClockButton - QToolButton -
gui/externalclockbutton.h
+ TransverterButton + QPushButton +
gui/transverterbutton.h
ValueDialZ @@ -1369,9 +1346,9 @@ QToolTip{background-color: white; color: black;} 1 - TransverterButton - QPushButton -
gui/transverterbutton.h
+ ExternalClockButton + QToolButton +
gui/externalclockbutton.h
diff --git a/plugins/samplemimo/metismiso/metismisogui.ui b/plugins/samplemimo/metismiso/metismisogui.ui index bf3a59e48..c04ddc532 100644 --- a/plugins/samplemimo/metismiso/metismisogui.ui +++ b/plugins/samplemimo/metismiso/metismisogui.ui @@ -6,8 +6,8 @@ 0 0 - 483 - 228 + 360 + 234
@@ -19,7 +19,7 @@ 360 - 200 + 0 @@ -807,33 +807,20 @@ - - - - Qt::Vertical - - - - 20 - 40 - - - - - - ButtonSwitch - QToolButton -
gui/buttonswitch.h
-
ValueDial QWidget
gui/valuedial.h
1
+ + ButtonSwitch + QToolButton +
gui/buttonswitch.h
+
TransverterButton QPushButton diff --git a/plugins/samplemimo/plutosdrmimo/plutosdrmimogui.ui b/plugins/samplemimo/plutosdrmimo/plutosdrmimogui.ui index 2f0ef9104..ff49f0cbc 100644 --- a/plugins/samplemimo/plutosdrmimo/plutosdrmimogui.ui +++ b/plugins/samplemimo/plutosdrmimo/plutosdrmimogui.ui @@ -6,8 +6,8 @@ 0 0 - 344 - 320 + 360 + 319
@@ -18,8 +18,8 @@ - 344 - 320 + 360 + 0 @@ -1257,23 +1257,6 @@ - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - diff --git a/plugins/samplemimo/testmosync/testmosyncgui.ui b/plugins/samplemimo/testmosync/testmosyncgui.ui index e80a8714b..8573a1ee6 100644 --- a/plugins/samplemimo/testmosync/testmosyncgui.ui +++ b/plugins/samplemimo/testmosync/testmosyncgui.ui @@ -6,8 +6,8 @@ 0 0 - 350 - 400 + 360 + 320 @@ -18,8 +18,8 @@ - 350 - 400 + 360 + 0 @@ -345,23 +345,6 @@ - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - diff --git a/plugins/samplemimo/xtrxmimo/xtrxmimogui.ui b/plugins/samplemimo/xtrxmimo/xtrxmimogui.ui index 1cf55b59d..6fcea580a 100644 --- a/plugins/samplemimo/xtrxmimo/xtrxmimogui.ui +++ b/plugins/samplemimo/xtrxmimo/xtrxmimogui.ui @@ -6,8 +6,8 @@ 0 0 - 370 - 290 + 360 + 284 @@ -19,7 +19,7 @@ 360 - 290 + 0 @@ -1185,6 +1185,7 @@ + Liberation Sans 8 @@ -1274,30 +1275,6 @@ QToolTip{background-color: white; color: black;} - - - - Qt::Horizontal - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - @@ -1312,17 +1289,17 @@ QToolTip{background-color: white; color: black;} QToolButton
gui/buttonswitch.h
- - ExternalClockButton - QToolButton -
gui/externalclockbutton.h
-
ValueDialZ QWidget
gui/valuedialz.h
1
+ + ExternalClockButton + QToolButton +
gui/externalclockbutton.h
+
diff --git a/plugins/samplesink/audiooutput/audiooutputgui.ui b/plugins/samplesink/audiooutput/audiooutputgui.ui index 291a3b123..9baec9db9 100644 --- a/plugins/samplesink/audiooutput/audiooutputgui.ui +++ b/plugins/samplesink/audiooutput/audiooutputgui.ui @@ -6,8 +6,8 @@ 0 0 - 320 - 200 + 360 + 149
@@ -18,8 +18,8 @@ - 320 - 200 + 360 + 0 @@ -257,23 +257,6 @@ - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - diff --git a/plugins/samplesink/bladerf1output/bladerf1outputgui.ui b/plugins/samplesink/bladerf1output/bladerf1outputgui.ui index 2c2a63bc9..a63f40f7b 100644 --- a/plugins/samplesink/bladerf1output/bladerf1outputgui.ui +++ b/plugins/samplesink/bladerf1output/bladerf1outputgui.ui @@ -6,8 +6,8 @@ 0 0 - 310 - 250 + 360 + 214 @@ -18,8 +18,8 @@ - 310 - 250 + 360 + 0 @@ -548,30 +548,6 @@ - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - - Qt::Horizontal - - - diff --git a/plugins/samplesink/bladerf2output/bladerf2outputgui.ui b/plugins/samplesink/bladerf2output/bladerf2outputgui.ui index fe54b2964..3a5e17a28 100644 --- a/plugins/samplesink/bladerf2output/bladerf2outputgui.ui +++ b/plugins/samplesink/bladerf2output/bladerf2outputgui.ui @@ -6,8 +6,8 @@ 0 0 - 350 - 200 + 360 + 202 @@ -18,8 +18,8 @@ - 350 - 200 + 360 + 0 @@ -487,30 +487,6 @@ - - - - Qt::Horizontal - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - diff --git a/plugins/samplesink/hackrfoutput/hackrfoutputgui.ui b/plugins/samplesink/hackrfoutput/hackrfoutputgui.ui index b27ca6e16..da73a6cf2 100644 --- a/plugins/samplesink/hackrfoutput/hackrfoutputgui.ui +++ b/plugins/samplesink/hackrfoutput/hackrfoutputgui.ui @@ -6,8 +6,8 @@ 0 0 - 320 - 200 + 360 + 207 @@ -18,8 +18,8 @@ - 320 - 200 + 360 + 0 @@ -537,23 +537,6 @@ - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - diff --git a/plugins/samplesink/limesdroutput/limesdroutputgui.ui b/plugins/samplesink/limesdroutput/limesdroutputgui.ui index 32b2efb93..faaf8f492 100644 --- a/plugins/samplesink/limesdroutput/limesdroutputgui.ui +++ b/plugins/samplesink/limesdroutput/limesdroutputgui.ui @@ -7,7 +7,7 @@ 0 0 360 - 290 + 264 @@ -19,7 +19,7 @@ 360 - 290 + 0 @@ -854,6 +854,7 @@ + Ubuntu 8 @@ -924,30 +925,6 @@ QToolTip{background-color: white; color: black;} - - - - Qt::Horizontal - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - @@ -963,9 +940,9 @@ QToolTip{background-color: white; color: black;}
gui/buttonswitch.h
- ExternalClockButton - QToolButton -
gui/externalclockbutton.h
+ TransverterButton + QPushButton +
gui/transverterbutton.h
ValueDialZ @@ -974,9 +951,9 @@ QToolTip{background-color: white; color: black;} 1 - TransverterButton - QPushButton -
gui/transverterbutton.h
+ ExternalClockButton + QToolButton +
gui/externalclockbutton.h
diff --git a/plugins/samplesink/localoutput/localoutputgui.ui b/plugins/samplesink/localoutput/localoutputgui.ui index 66467b17d..7e66d09c9 100644 --- a/plugins/samplesink/localoutput/localoutputgui.ui +++ b/plugins/samplesink/localoutput/localoutputgui.ui @@ -7,13 +7,13 @@ 0 0 360 - 80 + 68
360 - 80 + 0 @@ -152,30 +152,6 @@ - - - - Qt::Horizontal - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - diff --git a/plugins/samplesink/plutosdroutput/plutosdroutputgui.ui b/plugins/samplesink/plutosdroutput/plutosdroutputgui.ui index 2220656be..2133d8be2 100644 --- a/plugins/samplesink/plutosdroutput/plutosdroutputgui.ui +++ b/plugins/samplesink/plutosdroutput/plutosdroutputgui.ui @@ -6,8 +6,8 @@ 0 0 - 350 - 260 + 360 + 250 @@ -18,8 +18,8 @@ - 350 - 260 + 360 + 0 @@ -766,23 +766,6 @@ - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - diff --git a/plugins/samplesink/remoteoutput/remoteoutputgui.ui b/plugins/samplesink/remoteoutput/remoteoutputgui.ui index 84a293ac8..b6b7a7a51 100644 --- a/plugins/samplesink/remoteoutput/remoteoutputgui.ui +++ b/plugins/samplesink/remoteoutput/remoteoutputgui.ui @@ -10,7 +10,7 @@ 0 0 360 - 270 + 284 @@ -22,7 +22,7 @@ 360 - 270 + 0 @@ -719,23 +719,6 @@ - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - diff --git a/plugins/samplesink/soapysdroutput/soapysdroutputgui.ui b/plugins/samplesink/soapysdroutput/soapysdroutputgui.ui index 4ae211477..615c6e736 100644 --- a/plugins/samplesink/soapysdroutput/soapysdroutputgui.ui +++ b/plugins/samplesink/soapysdroutput/soapysdroutputgui.ui @@ -6,14 +6,14 @@ 0 0 - 324 - 176 + 360 + 236 - 320 - 132 + 360 + 0 @@ -309,8 +309,8 @@ 0 0 - 318 - 49 + 352 + 83 diff --git a/plugins/samplesink/testsink/testsinkgui.ui b/plugins/samplesink/testsink/testsinkgui.ui index 65c8354e8..545630c60 100644 --- a/plugins/samplesink/testsink/testsinkgui.ui +++ b/plugins/samplesink/testsink/testsinkgui.ui @@ -6,8 +6,8 @@ 0 0 - 350 - 400 + 360 + 320 @@ -18,8 +18,8 @@ - 350 - 400 + 360 + 0 @@ -312,23 +312,6 @@ - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - diff --git a/plugins/samplesink/usrpoutput/usrpoutputgui.ui b/plugins/samplesink/usrpoutput/usrpoutputgui.ui index fb66c98d9..090f2b32a 100644 --- a/plugins/samplesink/usrpoutput/usrpoutputgui.ui +++ b/plugins/samplesink/usrpoutput/usrpoutputgui.ui @@ -7,7 +7,7 @@ 0 0 360 - 290 + 214 @@ -19,7 +19,7 @@ 360 - 290 + 0 @@ -705,38 +705,9 @@ - - - - Qt::Horizontal - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - ButtonSwitch - QToolButton -
gui/buttonswitch.h
-
ValueDial QWidget @@ -744,16 +715,21 @@ 1 - ValueDialZ - QWidget -
gui/valuedialz.h
- 1 + ButtonSwitch + QToolButton +
gui/buttonswitch.h
TransverterButton QPushButton
gui/transverterbutton.h
+ + ValueDialZ + QWidget +
gui/valuedialz.h
+ 1 +
diff --git a/plugins/samplesink/xtrxoutput/xtrxoutputgui.ui b/plugins/samplesink/xtrxoutput/xtrxoutputgui.ui index 349c33dda..7e661afcf 100644 --- a/plugins/samplesink/xtrxoutput/xtrxoutputgui.ui +++ b/plugins/samplesink/xtrxoutput/xtrxoutputgui.ui @@ -6,8 +6,8 @@ 0 0 - 370 - 290 + 360 + 264
@@ -19,7 +19,7 @@ 360 - 290 + 0 @@ -820,6 +820,7 @@ + Liberation Sans 8 @@ -876,30 +877,6 @@ QToolTip{background-color: white; color: black;} - - - - Qt::Horizontal - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - @@ -914,17 +891,17 @@ QToolTip{background-color: white; color: black;} QToolButton
gui/buttonswitch.h
- - ExternalClockButton - QToolButton -
gui/externalclockbutton.h
-
ValueDialZ QWidget
gui/valuedialz.h
1
+ + ExternalClockButton + QToolButton +
gui/externalclockbutton.h
+
diff --git a/plugins/samplesource/airspy/airspygui.ui b/plugins/samplesource/airspy/airspygui.ui index cd4ea6f06..e59c34ce1 100644 --- a/plugins/samplesource/airspy/airspygui.ui +++ b/plugins/samplesource/airspy/airspygui.ui @@ -6,8 +6,8 @@ 0 0 - 280 - 220 + 360 + 244
@@ -18,8 +18,8 @@ - 280 - 220 + 360 + 0 @@ -427,13 +427,6 @@ - - - - Qt::Horizontal - - - @@ -616,23 +609,6 @@ - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - diff --git a/plugins/samplesource/airspyhf/airspyhfgui.ui b/plugins/samplesource/airspyhf/airspyhfgui.ui index c19aae71c..efd5e525b 100644 --- a/plugins/samplesource/airspyhf/airspyhfgui.ui +++ b/plugins/samplesource/airspyhf/airspyhfgui.ui @@ -6,8 +6,8 @@ 0 0 - 320 - 160 + 360 + 180 @@ -18,8 +18,8 @@ - 320 - 160 + 360 + 0 @@ -581,37 +581,20 @@ - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - ButtonSwitch - QToolButton -
gui/buttonswitch.h
-
ValueDial QWidget
gui/valuedial.h
1
+ + ButtonSwitch + QToolButton +
gui/buttonswitch.h
+
TransverterButton QPushButton diff --git a/plugins/samplesource/audioinput/audioinputgui.ui b/plugins/samplesource/audioinput/audioinputgui.ui index f2081a25e..1d5ff0178 100644 --- a/plugins/samplesource/audioinput/audioinputgui.ui +++ b/plugins/samplesource/audioinput/audioinputgui.ui @@ -6,8 +6,8 @@ 0 0 - 320 - 350 + 360 + 191
@@ -18,8 +18,8 @@ - 320 - 350 + 360 + 0 @@ -358,23 +358,6 @@ - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - diff --git a/plugins/samplesource/bladerf1input/bladerf1inputgui.ui b/plugins/samplesource/bladerf1input/bladerf1inputgui.ui index 531eb3311..1ab849e8a 100644 --- a/plugins/samplesource/bladerf1input/bladerf1inputgui.ui +++ b/plugins/samplesource/bladerf1input/bladerf1inputgui.ui @@ -6,8 +6,8 @@ 0 0 - 310 - 265 + 360 + 250 @@ -18,8 +18,8 @@ - 310 - 250 + 360 + 0 @@ -681,30 +681,6 @@ - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - - Qt::Horizontal - - - diff --git a/plugins/samplesource/bladerf2input/bladerf2inputgui.ui b/plugins/samplesource/bladerf2input/bladerf2inputgui.ui index 03fd1f2e3..fd648fe31 100644 --- a/plugins/samplesource/bladerf2input/bladerf2inputgui.ui +++ b/plugins/samplesource/bladerf2input/bladerf2inputgui.ui @@ -6,8 +6,8 @@ 0 0 - 350 - 220 + 360 + 209 @@ -18,8 +18,8 @@ - 350 - 220 + 360 + 0 @@ -549,30 +549,6 @@ - - - - Qt::Horizontal - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - diff --git a/plugins/samplesource/fcdpro/fcdprogui.ui b/plugins/samplesource/fcdpro/fcdprogui.ui index 7af12e90b..3e75bb1d6 100644 --- a/plugins/samplesource/fcdpro/fcdprogui.ui +++ b/plugins/samplesource/fcdpro/fcdprogui.ui @@ -6,8 +6,8 @@ 0 0 - 320 - 350 + 360 + 443 @@ -18,8 +18,8 @@ - 320 - 350 + 360 + 0 @@ -610,23 +610,6 @@ - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - diff --git a/plugins/samplesource/fcdproplus/fcdproplusgui.ui b/plugins/samplesource/fcdproplus/fcdproplusgui.ui index 319ff8471..982210b30 100644 --- a/plugins/samplesource/fcdproplus/fcdproplusgui.ui +++ b/plugins/samplesource/fcdproplus/fcdproplusgui.ui @@ -6,8 +6,8 @@ 0 0 - 320 - 180 + 360 + 219 @@ -18,8 +18,8 @@ - 320 - 180 + 360 + 0 @@ -452,23 +452,6 @@ - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - diff --git a/plugins/samplesource/kiwisdr/kiwisdrgui.ui b/plugins/samplesource/kiwisdr/kiwisdrgui.ui index 16a4c787d..1a843a4d3 100644 --- a/plugins/samplesource/kiwisdr/kiwisdrgui.ui +++ b/plugins/samplesource/kiwisdr/kiwisdrgui.ui @@ -7,7 +7,7 @@ 0 0 360 - 120 + 146 @@ -19,7 +19,7 @@ 360 - 120 + 0 @@ -132,6 +132,9 @@ Liberation Mono 16 + 50 + false + false @@ -308,19 +311,6 @@ - - - - Qt::Vertical - - - - 20 - 40 - - - - diff --git a/plugins/samplesource/limesdrinput/limesdrinputgui.ui b/plugins/samplesource/limesdrinput/limesdrinputgui.ui index 8bb19d189..b69695c8c 100644 --- a/plugins/samplesource/limesdrinput/limesdrinputgui.ui +++ b/plugins/samplesource/limesdrinput/limesdrinputgui.ui @@ -7,7 +7,7 @@ 0 0 360 - 290 + 266 @@ -19,7 +19,7 @@ 360 - 290 + 0 @@ -1080,6 +1080,7 @@ + Ubuntu 8 @@ -1153,30 +1154,6 @@ QToolTip{background-color: white; color: black;} - - - - Qt::Horizontal - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - @@ -1192,9 +1169,9 @@ QToolTip{background-color: white; color: black;}
gui/buttonswitch.h
- ExternalClockButton - QToolButton -
gui/externalclockbutton.h
+ TransverterButton + QPushButton +
gui/transverterbutton.h
ValueDialZ @@ -1203,9 +1180,9 @@ QToolTip{background-color: white; color: black;} 1 - TransverterButton - QPushButton -
gui/transverterbutton.h
+ ExternalClockButton + QToolButton +
gui/externalclockbutton.h
diff --git a/plugins/samplesource/localinput/localinputgui.ui b/plugins/samplesource/localinput/localinputgui.ui index 39b4ca876..d2d8398d4 100644 --- a/plugins/samplesource/localinput/localinputgui.ui +++ b/plugins/samplesource/localinput/localinputgui.ui @@ -7,13 +7,13 @@ 0 0 360 - 100 + 110
360 - 100 + 0 @@ -199,23 +199,6 @@ - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - diff --git a/plugins/samplesource/perseus/perseusgui.ui b/plugins/samplesource/perseus/perseusgui.ui index c06331a1d..7630ced73 100644 --- a/plugins/samplesource/perseus/perseusgui.ui +++ b/plugins/samplesource/perseus/perseusgui.ui @@ -6,8 +6,8 @@ 0 0 - 324 - 132 + 360 + 182 @@ -18,8 +18,8 @@ - 320 - 132 + 360 + 0 @@ -455,23 +455,6 @@ - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - diff --git a/plugins/samplesource/plutosdrinput/plutosdrinputgui.ui b/plugins/samplesource/plutosdrinput/plutosdrinputgui.ui index 709d10937..7d8a8df4c 100644 --- a/plugins/samplesource/plutosdrinput/plutosdrinputgui.ui +++ b/plugins/samplesource/plutosdrinput/plutosdrinputgui.ui @@ -6,8 +6,8 @@ 0 0 - 350 - 260 + 360 + 291 @@ -18,8 +18,8 @@ - 350 - 260 + 360 + 0 @@ -1007,23 +1007,6 @@ - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - diff --git a/plugins/samplesource/remoteinput/remoteinputgui.ui b/plugins/samplesource/remoteinput/remoteinputgui.ui index ca3982d22..bc766560e 100644 --- a/plugins/samplesource/remoteinput/remoteinputgui.ui +++ b/plugins/samplesource/remoteinput/remoteinputgui.ui @@ -6,14 +6,14 @@ 0 0 - 400 - 480 + 360 + 400 360 - 480 + 0 @@ -1062,37 +1062,20 @@ - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - ButtonSwitch - QToolButton -
gui/buttonswitch.h
-
ValueDial QWidget
gui/valuedial.h
1
+ + ButtonSwitch + QToolButton +
gui/buttonswitch.h
+
diff --git a/plugins/samplesource/sdrplay/sdrplaygui.ui b/plugins/samplesource/sdrplay/sdrplaygui.ui index f22caa9a8..7afef29c8 100644 --- a/plugins/samplesource/sdrplay/sdrplaygui.ui +++ b/plugins/samplesource/sdrplay/sdrplaygui.ui @@ -6,8 +6,8 @@ 0 0 - 307 - 210 + 360 + 257
@@ -18,8 +18,8 @@ - 0 - 210 + 360 + 0 @@ -649,30 +649,6 @@ - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - - Qt::Horizontal - - - diff --git a/plugins/samplesource/sdrplayv3/sdrplayv3gui.ui b/plugins/samplesource/sdrplayv3/sdrplayv3gui.ui index bbf4c43c0..fea992488 100644 --- a/plugins/samplesource/sdrplayv3/sdrplayv3gui.ui +++ b/plugins/samplesource/sdrplayv3/sdrplayv3gui.ui @@ -6,8 +6,8 @@ 0 0 - 409 - 260 + 360 + 303
@@ -18,8 +18,8 @@ - 0 - 260 + 360 + 0 @@ -765,51 +765,20 @@ - - - - Qt::Horizontal - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - - Qt::Horizontal - - - - - ButtonSwitch - QToolButton -
gui/buttonswitch.h
-
ValueDial QWidget
gui/valuedial.h
1
+ + ButtonSwitch + QToolButton +
gui/buttonswitch.h
+
TransverterButton QPushButton diff --git a/plugins/samplesource/sigmffileinput/sigmffileinputgui.ui b/plugins/samplesource/sigmffileinput/sigmffileinputgui.ui index 8db2c990b..3cc1946bd 100644 --- a/plugins/samplesource/sigmffileinput/sigmffileinputgui.ui +++ b/plugins/samplesource/sigmffileinput/sigmffileinputgui.ui @@ -7,7 +7,7 @@ 0 0 360 - 420 + 391
@@ -19,7 +19,7 @@ 360 - 420 + 0 @@ -823,23 +823,6 @@ - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - diff --git a/plugins/samplesource/soapysdrinput/soapysdrinputgui.ui b/plugins/samplesource/soapysdrinput/soapysdrinputgui.ui index f0781845a..8e1720a2e 100644 --- a/plugins/samplesource/soapysdrinput/soapysdrinputgui.ui +++ b/plugins/samplesource/soapysdrinput/soapysdrinputgui.ui @@ -6,14 +6,14 @@ 0 0 - 324 - 176 + 360 + 238 - 320 - 132 + 360 + 0 @@ -374,8 +374,8 @@ 0 0 - 318 - 49 + 352 + 83 diff --git a/plugins/samplesource/testsource/testsourcegui.ui b/plugins/samplesource/testsource/testsourcegui.ui index ce3915af9..889037318 100644 --- a/plugins/samplesource/testsource/testsourcegui.ui +++ b/plugins/samplesource/testsource/testsourcegui.ui @@ -7,7 +7,7 @@ 0 0 360 - 300 + 337 @@ -19,7 +19,7 @@ 360 - 300 + 0 @@ -132,6 +132,9 @@ Liberation Mono 16 + 50 + false + false @@ -403,6 +406,9 @@ Liberation Mono 12 + 50 + false + false @@ -559,6 +565,9 @@ Liberation Mono 12 + 50 + false + false @@ -967,9 +976,6 @@ - - - diff --git a/plugins/samplesource/usrpinput/usrpinputgui.ui b/plugins/samplesource/usrpinput/usrpinputgui.ui index bbdafa362..d31bd2bdd 100644 --- a/plugins/samplesource/usrpinput/usrpinputgui.ui +++ b/plugins/samplesource/usrpinput/usrpinputgui.ui @@ -7,7 +7,7 @@ 0 0 360 - 290 + 221 @@ -19,7 +19,7 @@ 360 - 290 + 0 @@ -771,38 +771,9 @@ - - - - Qt::Horizontal - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - ButtonSwitch - QToolButton -
gui/buttonswitch.h
-
ValueDial QWidget @@ -810,16 +781,21 @@ 1 - ValueDialZ - QWidget -
gui/valuedialz.h
- 1 + ButtonSwitch + QToolButton +
gui/buttonswitch.h
TransverterButton QPushButton
gui/transverterbutton.h
+ + ValueDialZ + QWidget +
gui/valuedialz.h
+ 1 +
diff --git a/plugins/samplesource/xtrxinput/xtrxinputgui.ui b/plugins/samplesource/xtrxinput/xtrxinputgui.ui index 8078a54e3..55b4aa3c2 100644 --- a/plugins/samplesource/xtrxinput/xtrxinputgui.ui +++ b/plugins/samplesource/xtrxinput/xtrxinputgui.ui @@ -6,8 +6,8 @@ 0 0 - 370 - 290 + 360 + 264
@@ -19,7 +19,7 @@ 360 - 290 + 0 @@ -1012,6 +1012,7 @@ + Liberation Sans 8 @@ -1068,30 +1069,6 @@ QToolTip{background-color: white; color: black;} - - - - Qt::Horizontal - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - @@ -1106,17 +1083,17 @@ QToolTip{background-color: white; color: black;} QToolButton
gui/buttonswitch.h
- - ExternalClockButton - QToolButton -
gui/externalclockbutton.h
-
ValueDialZ QWidget
gui/valuedialz.h
1
+ + ExternalClockButton + QToolButton +
gui/externalclockbutton.h
+
From 4ff5d241c805826ddfbf05e35520139bdfedf046 Mon Sep 17 00:00:00 2001 From: f4exb Date: Sun, 10 Apr 2022 00:11:23 +0200 Subject: [PATCH 008/115] Massive UI revamping (v7): main spectrum show/hide and minimum width set to 360 --- sdrgui/device/devicegui.cpp | 6 ++++++ sdrgui/device/devicegui.h | 2 ++ sdrgui/device/deviceuiset.cpp | 5 +++++ sdrgui/gui/glspectrum.cpp | 2 +- sdrgui/mainwindow.cpp | 11 +++++++++++ sdrgui/mainwindow.h | 1 + 6 files changed, 26 insertions(+), 1 deletion(-) diff --git a/sdrgui/device/devicegui.cpp b/sdrgui/device/devicegui.cpp index 28715039c..bd76017cf 100644 --- a/sdrgui/device/devicegui.cpp +++ b/sdrgui/device/devicegui.cpp @@ -174,6 +174,7 @@ DeviceGUI::DeviceGUI(QWidget *parent) : connect(this, SIGNAL(forceShrink()), this, SLOT(shrinkWindow())); connect(m_closeButton, SIGNAL(clicked()), this, SLOT(close())); connect(this, SIGNAL(forceClose()), this, SLOT(close())); + connect(m_showSpectrumButton, SIGNAL(clicked()), this, SLOT(showSpectrumHandler())); } DeviceGUI::~DeviceGUI() @@ -274,6 +275,11 @@ void DeviceGUI::openMoveToWorkspaceDialog() } } +void DeviceGUI::showSpectrumHandler() +{ + emit showSpectrum(m_deviceSetIndex); +} + void DeviceGUI::shrinkWindow() { qDebug("DeviceGUI::shrinkWindow"); diff --git a/sdrgui/device/devicegui.h b/sdrgui/device/devicegui.h index a4167d704..5ba14ccad 100644 --- a/sdrgui/device/devicegui.h +++ b/sdrgui/device/devicegui.h @@ -125,6 +125,7 @@ private slots: void deviceReload(); void showHelp(); void openMoveToWorkspaceDialog(); + void showSpectrumHandler(); signals: void forceClose(); @@ -133,6 +134,7 @@ signals: void forceShrink(); void deviceAdd(int deviceType, int deviceIndex); void deviceChange(int newDeviceIndex); + void showSpectrum(int deviceSetIndex); }; #endif // INCLUDE_DEVICEGUI_H diff --git a/sdrgui/device/deviceuiset.cpp b/sdrgui/device/deviceuiset.cpp index 3570517df..2a8bff4f9 100644 --- a/sdrgui/device/deviceuiset.cpp +++ b/sdrgui/device/deviceuiset.cpp @@ -174,6 +174,10 @@ void DeviceUISet::loadDeviceSetSettings( m_spectrumGUI->deserialize(preset->getSpectrumConfig()); m_deviceAPI->loadSamplingDeviceSettings(preset); + if (!preset->getShowSpectrum()) { + m_mainSpectrumGUI->hide(); + } + if (m_deviceSourceEngine) { // source device loadRxChannelSettings(preset, pluginAPI); } else if (m_deviceSinkEngine) { // sink device @@ -188,6 +192,7 @@ void DeviceUISet::saveDeviceSetSettings(Preset* preset) const preset->setSpectrumConfig(m_spectrumGUI->serialize()); preset->setSpectrumWorkspaceIndex(m_mainSpectrumGUI->getWorkspaceIndex()); preset->setSpectrumGeometry(m_mainSpectrumGUI->saveGeometry()); + preset->setShowSpectrum(m_spectrumGUI->isVisible()); preset->setSelectedDevice(Preset::SelectedDevice{ m_deviceAPI->getSamplingDeviceId(), m_deviceAPI->getSamplingDeviceSerial(), diff --git a/sdrgui/gui/glspectrum.cpp b/sdrgui/gui/glspectrum.cpp index 2c9c1513f..f6a6485e4 100644 --- a/sdrgui/gui/glspectrum.cpp +++ b/sdrgui/gui/glspectrum.cpp @@ -99,7 +99,7 @@ GLSpectrum::GLSpectrum(QWidget* parent) : setAttribute(Qt::WA_NoSystemBackground, true); setMouseTracking(true); - setMinimumSize(200, 200); + setMinimumSize(360, 200); m_waterfallShare = 0.5; diff --git a/sdrgui/mainwindow.cpp b/sdrgui/mainwindow.cpp index 3b9d3f654..7c9a4b462 100644 --- a/sdrgui/mainwindow.cpp +++ b/sdrgui/mainwindow.cpp @@ -516,6 +516,12 @@ void MainWindow::sampleSourceCreate( this, [=](int newDeviceIndex){ this->sampleDeviceChangeHandler(deviceGUI, newDeviceIndex); } ); + QObject::connect( + deviceGUI, + &DeviceGUI::showSpectrum, + this, + [=](int deviceSetIndex){ this->mainSpectrumShow(this->m_deviceUIs[deviceSetIndex]->m_mainSpectrumGUI); } + ); deviceAPI->getSampleSource()->setMessageQueueToGUI(deviceGUI->getInputMessageQueue()); deviceUISet->m_deviceGUI = deviceGUI; const PluginInterface::SamplingDevice *selectedDevice = DeviceEnumerator::instance()->getRxSamplingDevice(selectedDeviceIndex); @@ -2501,6 +2507,11 @@ void MainWindow::mainSpectrumMove(MainSpectrumGUI *gui, int wsIndexDestnation) m_workspaces[wsIndexDestnation]->addToMdiArea(gui); } +void MainWindow::mainSpectrumShow(MainSpectrumGUI *gui) +{ + gui->show(); +} + void MainWindow::openFeaturePresetsDialog(QPoint p, Workspace *workspace) { FeaturePresetsDialog dialog; diff --git a/sdrgui/mainwindow.h b/sdrgui/mainwindow.h index 1a5151365..2b799cf3d 100644 --- a/sdrgui/mainwindow.h +++ b/sdrgui/mainwindow.h @@ -208,6 +208,7 @@ private slots: void openFeaturePresetsDialog(QPoint p, Workspace *workspace); void deviceMove(DeviceGUI *gui, int wsIndexDestnation); void mainSpectrumMove(MainSpectrumGUI *gui, int wsIndexDestnation); + void mainSpectrumShow(MainSpectrumGUI *gui); void on_action_Quick_Start_triggered(); void on_action_Main_Window_triggered(); void on_action_Loaded_Plugins_triggered(); From 32973132d921c7e9e72c223a3845a8bd6ff84a7f Mon Sep 17 00:00:00 2001 From: f4exb Date: Sun, 10 Apr 2022 02:19:26 +0200 Subject: [PATCH 009/115] Massive UI revamping (v7): unify custom widget borders --- .../bladerf2mimo/bladerf2mimogui.cpp | 3 +- .../samplemimo/limesdrmimo/limesdrmimogui.cpp | 3 +- plugins/samplemimo/metismiso/metismisogui.cpp | 3 +- .../plutosdrmimo/plutosdrmimogui.cpp | 3 +- plugins/samplemimo/testmi/testmigui.cpp | 3 +- .../samplemimo/testmosync/testmosyncgui.cpp | 3 +- plugins/samplemimo/xtrxmimo/xtrxmimogui.cpp | 3 +- .../samplesink/audiooutput/audiooutputgui.cpp | 3 +- .../bladerf1output/bladerf1outputgui.cpp | 3 +- .../bladerf2output/bladerf2outputgui.cpp | 3 +- .../samplesink/fileoutput/fileoutputgui.cpp | 3 +- .../hackrfoutput/hackrfoutputgui.cpp | 3 +- .../limesdroutput/limesdroutputgui.cpp | 3 +- .../samplesink/localoutput/localoutputgui.cpp | 3 +- .../plutosdroutput/plutosdroutputgui.cpp | 3 +- .../remoteoutput/remoteoutputgui.cpp | 3 +- .../soapysdroutput/soapysdroutputgui.cpp | 3 +- plugins/samplesink/testsink/testsinkgui.cpp | 3 +- .../samplesink/usrpoutput/usrpoutputgui.cpp | 3 +- .../samplesink/xtrxoutput/xtrxoutputgui.cpp | 3 +- plugins/samplesource/airspy/airspygui.cpp | 3 +- plugins/samplesource/airspyhf/airspyhfgui.cpp | 3 +- .../samplesource/audioinput/audioinputgui.cpp | 3 +- .../bladerf1input/bladerf1inputgui.cpp | 3 +- .../bladerf2input/bladerf2inputgui.cpp | 3 +- plugins/samplesource/fcdpro/fcdprogui.cpp | 3 +- .../samplesource/fcdproplus/fcdproplusgui.cpp | 3 +- .../samplesource/fileinput/fileinputgui.cpp | 3 +- .../hackrfinput/hackrfinputgui.cpp | 3 +- plugins/samplesource/kiwisdr/kiwisdrgui.cpp | 3 +- .../limesdrinput/limesdrinputgui.cpp | 3 +- .../samplesource/localinput/localinputgui.cpp | 3 +- plugins/samplesource/perseus/perseusgui.cpp | 3 +- .../plutosdrinput/plutosdrinputgui.cpp | 3 +- .../remoteinput/remoteinputgui.cpp | 3 +- plugins/samplesource/rtlsdr/rtlsdrgui.cpp | 4 +- plugins/samplesource/sdrplay/sdrplaygui.cpp | 3 +- .../samplesource/sdrplayv3/sdrplayv3gui.cpp | 3 +- .../sigmffileinput/sigmffileinputgui.cpp | 3 +- .../soapysdrinput/soapysdrinputgui.cpp | 3 +- .../samplesource/testsource/testsourcegui.cpp | 3 +- .../samplesource/usrpinput/usrpinputgui.cpp | 3 +- .../samplesource/xtrxinput/xtrxinputgui.cpp | 3 +- sdrgui/gui/rollupcontents.cpp | 224 ++---------------- sdrgui/gui/rollupcontents.h | 5 +- 45 files changed, 102 insertions(+), 257 deletions(-) diff --git a/plugins/samplemimo/bladerf2mimo/bladerf2mimogui.cpp b/plugins/samplemimo/bladerf2mimo/bladerf2mimogui.cpp index 608e7b388..6639a8b06 100644 --- a/plugins/samplemimo/bladerf2mimo/bladerf2mimogui.cpp +++ b/plugins/samplemimo/bladerf2mimo/bladerf2mimogui.cpp @@ -68,7 +68,8 @@ BladeRF2MIMOGui::BladeRF2MIMOGui(DeviceUISet *deviceUISet, QWidget* parent) : qDebug("BladeRF2MIMOGui::BladeRF2MIMOGui"); setAttribute(Qt::WA_DeleteOnClose, true); ui->setupUi(getContents()); - getContents()->setStyleSheet("#BladeRF2MIMOGui { border: 1px solid #C06900 }"); + getContents()->setStyleSheet(QString(tr("#BladeRF2MIMOGui { border: 1px solid %1 }") + .arg(palette().highlight().color().darker(115).name()))); m_helpURL = "plugins/samplemimo/bladerf2mimo/readme.md"; m_sampleMIMO = (BladeRF2MIMO*) m_deviceUISet->m_deviceAPI->getSampleMIMO(); diff --git a/plugins/samplemimo/limesdrmimo/limesdrmimogui.cpp b/plugins/samplemimo/limesdrmimo/limesdrmimogui.cpp index 2022ce44e..ceeb25951 100644 --- a/plugins/samplemimo/limesdrmimo/limesdrmimogui.cpp +++ b/plugins/samplemimo/limesdrmimo/limesdrmimogui.cpp @@ -70,7 +70,8 @@ LimeSDRMIMOGUI::LimeSDRMIMOGUI(DeviceUISet *deviceUISet, QWidget* parent) : qDebug("LimeSDRMIMOGUI::LimeSDRMIMOGUI"); setAttribute(Qt::WA_DeleteOnClose, true); ui->setupUi(getContents()); - getContents()->setStyleSheet("#LimeSDRMIMOGUI { border: 1px solid #C06900 }"); + getContents()->setStyleSheet(QString(tr("#LimeSDRMIMOGUI { border: 1px solid %1 }") + .arg(palette().highlight().color().darker(115).name()))); m_helpURL = "plugins/samplemimo/limesdrmimo/readme.md"; m_limeSDRMIMO = (LimeSDRMIMO*) m_deviceUISet->m_deviceAPI->getSampleMIMO(); diff --git a/plugins/samplemimo/metismiso/metismisogui.cpp b/plugins/samplemimo/metismiso/metismisogui.cpp index 98e91cd69..47b6b5b60 100644 --- a/plugins/samplemimo/metismiso/metismisogui.cpp +++ b/plugins/samplemimo/metismiso/metismisogui.cpp @@ -58,7 +58,8 @@ MetisMISOGui::MetisMISOGui(DeviceUISet *deviceUISet, QWidget* parent) : m_txSampleRate = 48000; ui->setupUi(getContents()); - getContents()->setStyleSheet("#MetisMISOGui { border: 1px solid #C06900 }"); + getContents()->setStyleSheet(QString(tr("#MetisMISOGui { border: 1px solid %1 }") + .arg(palette().highlight().color().darker(115).name()))); m_helpURL = "plugins/samplemimo/metismiso/readme.md"; ui->centerFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold)); ui->centerFrequency->setValueRange(7, 0, m_absMaxFreq); diff --git a/plugins/samplemimo/plutosdrmimo/plutosdrmimogui.cpp b/plugins/samplemimo/plutosdrmimo/plutosdrmimogui.cpp index 6b741d057..412b34629 100644 --- a/plugins/samplemimo/plutosdrmimo/plutosdrmimogui.cpp +++ b/plugins/samplemimo/plutosdrmimo/plutosdrmimogui.cpp @@ -70,7 +70,8 @@ PlutoSDRMIMOGUI::PlutoSDRMIMOGUI(DeviceUISet *deviceUISet, QWidget* parent) : qDebug("PlutoSDRMIMOGui::PlutoSDRMIMOGui"); setAttribute(Qt::WA_DeleteOnClose, true); ui->setupUi(getContents()); - getContents()->setStyleSheet("#PlutoSDRMIMOGUI { border: 1px solid #C06900 }"); + getContents()->setStyleSheet(QString(tr("#PlutoSDRMIMOGUI { border: 1px solid %1 }") + .arg(palette().highlight().color().darker(115).name()))); m_helpURL = "plugins/samplemimo/plutosdrmimo/readme.md"; m_sampleMIMO = (PlutoSDRMIMO*) m_deviceUISet->m_deviceAPI->getSampleMIMO(); diff --git a/plugins/samplemimo/testmi/testmigui.cpp b/plugins/samplemimo/testmi/testmigui.cpp index 49751eabf..41fc673c1 100644 --- a/plugins/samplemimo/testmi/testmigui.cpp +++ b/plugins/samplemimo/testmi/testmigui.cpp @@ -61,7 +61,8 @@ TestMIGui::TestMIGui(DeviceUISet *deviceUISet, QWidget* parent) : m_deviceSampleRates.push_back(m_settings.m_streams[1].m_sampleRate / (1<setupUi(getContents()); - getContents()->setStyleSheet("#TestMIGui { border: 1px solid #C06900 }"); + getContents()->setStyleSheet(QString(tr("#TestMIGui { border: 1px solid %1 }") + .arg(palette().highlight().color().darker(115).name()))); m_helpURL = "plugins/samplemimo/testmi/readme.md"; ui->spectrumSource->addItem("0"); ui->spectrumSource->addItem("1"); diff --git a/plugins/samplemimo/testmosync/testmosyncgui.cpp b/plugins/samplemimo/testmosync/testmosyncgui.cpp index b9c3b8404..845844ac8 100644 --- a/plugins/samplemimo/testmosync/testmosyncgui.cpp +++ b/plugins/samplemimo/testmosync/testmosyncgui.cpp @@ -51,7 +51,8 @@ TestMOSyncGui::TestMOSyncGui(DeviceUISet *deviceUISet, QWidget* parent) : { setAttribute(Qt::WA_DeleteOnClose, true); ui->setupUi(getContents()); - getContents()->setStyleSheet("#TestMOSyncGui { border: 1px solid #C06900 }"); + getContents()->setStyleSheet(QString(tr("#TestMOSyncGui { border: 1px solid %1 }") + .arg(palette().highlight().color().darker(115).name()))); m_helpURL = "plugins/samplemimo/testmosync/readme.md"; m_sampleMIMO = (TestMOSync*) m_deviceUISet->m_deviceAPI->getSampleMIMO(); diff --git a/plugins/samplemimo/xtrxmimo/xtrxmimogui.cpp b/plugins/samplemimo/xtrxmimo/xtrxmimogui.cpp index 98fc27e4f..b054ff5df 100644 --- a/plugins/samplemimo/xtrxmimo/xtrxmimogui.cpp +++ b/plugins/samplemimo/xtrxmimo/xtrxmimogui.cpp @@ -67,7 +67,8 @@ XTRXMIMOGUI::XTRXMIMOGUI(DeviceUISet *deviceUISet, QWidget* parent) : qDebug("XTRXMIMOGUI::XTRXMIMOGUI"); setAttribute(Qt::WA_DeleteOnClose, true); ui->setupUi(getContents()); - getContents()->setStyleSheet("#XTRXMIMOGUI { border: 1px solid #C06900 }"); + getContents()->setStyleSheet(QString(tr("#XTRXMIMOGUI { border: 1px solid %1 }") + .arg(palette().highlight().color().darker(115).name()))); m_helpURL = "plugins/samplemimo/xtrxmimo/readme.md"; m_xtrxMIMO = (XTRXMIMO*) m_deviceUISet->m_deviceAPI->getSampleMIMO(); diff --git a/plugins/samplesink/audiooutput/audiooutputgui.cpp b/plugins/samplesink/audiooutput/audiooutputgui.cpp index 8aeddf0c8..d298dfe39 100644 --- a/plugins/samplesink/audiooutput/audiooutputgui.cpp +++ b/plugins/samplesink/audiooutput/audiooutputgui.cpp @@ -44,7 +44,8 @@ AudioOutputGui::AudioOutputGui(DeviceUISet *deviceUISet, QWidget* parent) : m_audioOutput = (AudioOutput*) m_deviceUISet->m_deviceAPI->getSampleSink(); ui->setupUi(getContents()); - getContents()->setStyleSheet("#AudioOutputGui { border: 1px solid #C06900 }"); + getContents()->setStyleSheet(QString(tr("#AudioOutputGui { border: 1px solid %1 }") + .arg(palette().highlight().color().darker(115).name()))); m_helpURL = "plugins/samplesink/audiooutput/readme.md"; connect(&m_updateTimer, SIGNAL(timeout()), this, SLOT(updateHardware())); diff --git a/plugins/samplesink/bladerf1output/bladerf1outputgui.cpp b/plugins/samplesink/bladerf1output/bladerf1outputgui.cpp index 4e96bb307..b8766bb00 100644 --- a/plugins/samplesink/bladerf1output/bladerf1outputgui.cpp +++ b/plugins/samplesink/bladerf1output/bladerf1outputgui.cpp @@ -47,7 +47,8 @@ Bladerf1OutputGui::Bladerf1OutputGui(DeviceUISet *deviceUISet, QWidget* parent) m_deviceSampleSink = (Bladerf1Output*) m_deviceUISet->m_deviceAPI->getSampleSink(); ui->setupUi(getContents()); - getContents()->setStyleSheet("#Bladerf1OutputGui { border: 1px solid #C06900 }"); + getContents()->setStyleSheet(QString(tr("#Bladerf1OutputGui { border: 1px solid %1 }") + .arg(palette().highlight().color().darker(115).name()))); m_helpURL = "/plugins/samplesink/bladerf1output/readme.md"; ui->centerFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold)); ui->centerFrequency->setValueRange(7, BLADERF_FREQUENCY_MIN_XB200/1000, BLADERF_FREQUENCY_MAX/1000); diff --git a/plugins/samplesink/bladerf2output/bladerf2outputgui.cpp b/plugins/samplesink/bladerf2output/bladerf2outputgui.cpp index 7fa2bd767..fd40138d2 100644 --- a/plugins/samplesink/bladerf2output/bladerf2outputgui.cpp +++ b/plugins/samplesink/bladerf2output/bladerf2outputgui.cpp @@ -50,7 +50,8 @@ BladeRF2OutputGui::BladeRF2OutputGui(DeviceUISet *deviceUISet, QWidget* parent) uint64_t f_min, f_max; ui->setupUi(getContents()); - getContents()->setStyleSheet("#BladeRF2OutputGui { border: 1px solid #C06900 }"); + getContents()->setStyleSheet(QString(tr("#BladeRF2OutputGui { border: 1px solid %1 }") + .arg(palette().highlight().color().darker(115).name()))); m_helpURL = "plugins/samplesink/bladerf2output/readme.md"; ui->centerFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold)); m_sampleSink->getFrequencyRange(f_min, f_max, step, scale); diff --git a/plugins/samplesink/fileoutput/fileoutputgui.cpp b/plugins/samplesink/fileoutput/fileoutputgui.cpp index 60c61e310..918944a15 100644 --- a/plugins/samplesink/fileoutput/fileoutputgui.cpp +++ b/plugins/samplesink/fileoutput/fileoutputgui.cpp @@ -53,7 +53,8 @@ FileOutputGui::FileOutputGui(DeviceUISet *deviceUISet, QWidget* parent) : { setAttribute(Qt::WA_DeleteOnClose, true); ui->setupUi(getContents()); - getContents()->setStyleSheet("#FileOutputGui { border: 1px solid #C06900 }"); + getContents()->setStyleSheet(QString(tr("#FileOutputGui { border: 1px solid %1 }") + .arg(palette().highlight().color().darker(115).name()))); m_helpURL = "plugins/samplesink/fileoutput/readme.md"; ui->centerFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold)); diff --git a/plugins/samplesink/hackrfoutput/hackrfoutputgui.cpp b/plugins/samplesink/hackrfoutput/hackrfoutputgui.cpp index 1bfa6105e..03e613cd3 100644 --- a/plugins/samplesink/hackrfoutput/hackrfoutputgui.cpp +++ b/plugins/samplesink/hackrfoutput/hackrfoutputgui.cpp @@ -49,7 +49,8 @@ HackRFOutputGui::HackRFOutputGui(DeviceUISet *deviceUISet, QWidget* parent) : m_deviceSampleSink = (HackRFOutput*) m_deviceUISet->m_deviceAPI->getSampleSink(); ui->setupUi(getContents()); - getContents()->setStyleSheet("#HackRFOutputGui { border: 1px solid #C06900 }"); + getContents()->setStyleSheet(QString(tr("#HackRFOutputGui { border: 1px solid %1 }") + .arg(palette().highlight().color().darker(115).name()))); m_helpURL = "plugins/samplesink/hackrfoutput/readme.md"; ui->centerFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold)); ui->centerFrequency->setValueRange(7, 0U, 7250000U); diff --git a/plugins/samplesink/limesdroutput/limesdroutputgui.cpp b/plugins/samplesink/limesdroutput/limesdroutputgui.cpp index 7e4ebe7dc..c8e9f9777 100644 --- a/plugins/samplesink/limesdroutput/limesdroutputgui.cpp +++ b/plugins/samplesink/limesdroutput/limesdroutputgui.cpp @@ -46,7 +46,8 @@ LimeSDROutputGUI::LimeSDROutputGUI(DeviceUISet *deviceUISet, QWidget* parent) : m_limeSDROutput = (LimeSDROutput*) m_deviceUISet->m_deviceAPI->getSampleSink(); ui->setupUi(getContents()); - getContents()->setStyleSheet("#LimeSDROutputGUI { border: 1px solid #C06900 }"); + getContents()->setStyleSheet(QString(tr("#LimeSDROutputGUI { border: 1px solid %1 }") + .arg(palette().highlight().color().darker(115).name()))); m_helpURL = "plugins/samplesink/limesdroutput/readme.md"; float minF, maxF; diff --git a/plugins/samplesink/localoutput/localoutputgui.cpp b/plugins/samplesink/localoutput/localoutputgui.cpp index 7fc23d47b..c85432967 100644 --- a/plugins/samplesink/localoutput/localoutputgui.cpp +++ b/plugins/samplesink/localoutput/localoutputgui.cpp @@ -63,7 +63,8 @@ LocalOutputGui::LocalOutputGui(DeviceUISet *deviceUISet, QWidget* parent) : m_paletteWhiteText.setColor(QPalette::WindowText, Qt::white); ui->setupUi(getContents()); - getContents()->setStyleSheet("#LocalOutputGui { border: 1px solid #C06900 }"); + getContents()->setStyleSheet(QString(tr("#LocalOutputGui { border: 1px solid %1 }") + .arg(palette().highlight().color().darker(115).name()))); m_helpURL = "plugins/samplesink/localoutput/readme.md"; CRightClickEnabler *startStopRightClickEnabler = new CRightClickEnabler(ui->startStop); diff --git a/plugins/samplesink/plutosdroutput/plutosdroutputgui.cpp b/plugins/samplesink/plutosdroutput/plutosdroutputgui.cpp index 006c31900..a0aaf5368 100644 --- a/plugins/samplesink/plutosdroutput/plutosdroutputgui.cpp +++ b/plugins/samplesink/plutosdroutput/plutosdroutputgui.cpp @@ -49,7 +49,8 @@ PlutoSDROutputGUI::PlutoSDROutputGUI(DeviceUISet *deviceUISet, QWidget* parent) m_sampleSink = (PlutoSDROutput*) m_deviceUISet->m_deviceAPI->getSampleSink(); ui->setupUi(getContents()); - getContents()->setStyleSheet("#PlutoSDROutputGUI { border: 1px solid #C06900 }"); + getContents()->setStyleSheet(QString(tr("#PlutoSDROutputGUI { border: 1px solid %1 }") + .arg(palette().highlight().color().darker(115).name()))); m_helpURL = "plugins/samplesink/plutosdroutput/readme.md"; ui->centerFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold)); updateFrequencyLimits(); diff --git a/plugins/samplesink/remoteoutput/remoteoutputgui.cpp b/plugins/samplesink/remoteoutput/remoteoutputgui.cpp index 447d0d232..65343e43f 100644 --- a/plugins/samplesink/remoteoutput/remoteoutputgui.cpp +++ b/plugins/samplesink/remoteoutput/remoteoutputgui.cpp @@ -68,7 +68,8 @@ RemoteOutputSinkGui::RemoteOutputSinkGui(DeviceUISet *deviceUISet, QWidget* pare m_paletteWhiteText.setColor(QPalette::WindowText, Qt::white); ui->setupUi(getContents()); - getContents()->setStyleSheet("#RemoteOutputGui { border: 1px solid #C06900 }"); + getContents()->setStyleSheet(QString(tr("#RemoteOutputGui { border: 1px solid %1 }") + .arg(palette().highlight().color().darker(115).name()))); m_helpURL = "plugins/samplesink/remoteoutput/readme.md"; connect(&(m_deviceUISet->m_deviceAPI->getMasterTimer()), SIGNAL(timeout()), this, SLOT(tick())); diff --git a/plugins/samplesink/soapysdroutput/soapysdroutputgui.cpp b/plugins/samplesink/soapysdroutput/soapysdroutputgui.cpp index 133925b01..fe7f7c800 100644 --- a/plugins/samplesink/soapysdroutput/soapysdroutputgui.cpp +++ b/plugins/samplesink/soapysdroutput/soapysdroutputgui.cpp @@ -60,7 +60,8 @@ SoapySDROutputGui::SoapySDROutputGui(DeviceUISet *deviceUISet, QWidget* parent) setAttribute(Qt::WA_DeleteOnClose, true); m_sampleSink = (SoapySDROutput*) m_deviceUISet->m_deviceAPI->getSampleSink(); ui->setupUi(getContents()); - getContents()->setStyleSheet("#SoapySDROutputGui { border: 1px solid #C06900 }"); + getContents()->setStyleSheet(QString(tr("#SoapySDROutputGui { border: 1px solid %1 }") + .arg(palette().highlight().color().darker(115).name()))); m_helpURL = "plugins/samplesink/soapysdroutput/readme.md"; ui->centerFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold)); diff --git a/plugins/samplesink/testsink/testsinkgui.cpp b/plugins/samplesink/testsink/testsinkgui.cpp index 9b22a7bec..e63ba074c 100644 --- a/plugins/samplesink/testsink/testsinkgui.cpp +++ b/plugins/samplesink/testsink/testsinkgui.cpp @@ -50,7 +50,8 @@ TestSinkGui::TestSinkGui(DeviceUISet *deviceUISet, QWidget* parent) : { setAttribute(Qt::WA_DeleteOnClose, true); ui->setupUi(getContents()); - getContents()->setStyleSheet("#TestSinkGui { border: 1px solid #C06900 }"); + getContents()->setStyleSheet(QString(tr("#TestSinkGui { border: 1px solid %1 }") + .arg(palette().highlight().color().darker(115).name()))); m_helpURL = "plugins/samplesink/testsink/readme.md"; m_sampleSink = (TestSinkOutput*) m_deviceUISet->m_deviceAPI->getSampleSink(); diff --git a/plugins/samplesink/usrpoutput/usrpoutputgui.cpp b/plugins/samplesink/usrpoutput/usrpoutputgui.cpp index c9848c106..4271c44de 100644 --- a/plugins/samplesink/usrpoutput/usrpoutputgui.cpp +++ b/plugins/samplesink/usrpoutput/usrpoutputgui.cpp @@ -47,7 +47,8 @@ USRPOutputGUI::USRPOutputGUI(DeviceUISet *deviceUISet, QWidget* parent) : m_usrpOutput = (USRPOutput*) m_deviceUISet->m_deviceAPI->getSampleSink(); ui->setupUi(getContents()); - getContents()->setStyleSheet("#USRPOutputGUI { border: 1px solid #C06900 }"); + getContents()->setStyleSheet(QString(tr("#USRPOutputGUI { border: 1px solid %1 }") + .arg(palette().highlight().color().darker(115).name()))); m_helpURL = "plugins/samplesink/usrpoutput/readme.md"; float minF, maxF; diff --git a/plugins/samplesink/xtrxoutput/xtrxoutputgui.cpp b/plugins/samplesink/xtrxoutput/xtrxoutputgui.cpp index 9bdc65f45..c20372447 100644 --- a/plugins/samplesink/xtrxoutput/xtrxoutputgui.cpp +++ b/plugins/samplesink/xtrxoutput/xtrxoutputgui.cpp @@ -48,7 +48,8 @@ XTRXOutputGUI::XTRXOutputGUI(DeviceUISet *deviceUISet, QWidget* parent) : m_XTRXOutput = (XTRXOutput*) m_deviceUISet->m_deviceAPI->getSampleSink(); ui->setupUi(getContents()); - getContents()->setStyleSheet("#XTRXOutputGUI { border: 1px solid #C06900 }"); + getContents()->setStyleSheet(QString(tr("#XTRXOutputGUI { border: 1px solid %1 }") + .arg(palette().highlight().color().darker(115).name()))); m_helpURL = "plugins/samplesink/xtrxoutput/readme.md"; float minF, maxF, stepF; diff --git a/plugins/samplesource/airspy/airspygui.cpp b/plugins/samplesource/airspy/airspygui.cpp index 9cc584157..b0d8de826 100644 --- a/plugins/samplesource/airspy/airspygui.cpp +++ b/plugins/samplesource/airspy/airspygui.cpp @@ -48,7 +48,8 @@ AirspyGui::AirspyGui(DeviceUISet *deviceUISet, QWidget* parent) : m_sampleSource = (AirspyInput*) m_deviceUISet->m_deviceAPI->getSampleSource(); ui->setupUi(getContents()); - getContents()->setStyleSheet("#AirspyGui { border: 1px solid #C06900 }"); + getContents()->setStyleSheet(QString(tr("#AirspyGui { border: 1px solid %1 }") + .arg(palette().highlight().color().darker(115).name()))); ui->centerFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold)); updateFrequencyLimits(); diff --git a/plugins/samplesource/airspyhf/airspyhfgui.cpp b/plugins/samplesource/airspyhf/airspyhfgui.cpp index a2e3e2c54..3dcb22d42 100644 --- a/plugins/samplesource/airspyhf/airspyhfgui.cpp +++ b/plugins/samplesource/airspyhf/airspyhfgui.cpp @@ -47,7 +47,8 @@ AirspyHFGui::AirspyHFGui(DeviceUISet *deviceUISet, QWidget* parent) : m_sampleSource = (AirspyHFInput*) m_deviceUISet->m_deviceAPI->getSampleSource(); ui->setupUi(getContents()); - getContents()->setStyleSheet("#AirspyHFGui { border: 1px solid #C06900 }"); + getContents()->setStyleSheet(QString(tr("#AirspyHFGui { border: 1px solid %1 }") + .arg(palette().highlight().color().darker(115).name()))); m_helpURL = "plugins/samplesource/airspyhf/readme.md"; ui->centerFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold)); updateFrequencyLimits(); diff --git a/plugins/samplesource/audioinput/audioinputgui.cpp b/plugins/samplesource/audioinput/audioinputgui.cpp index 29b4fd49f..c6497aa6f 100644 --- a/plugins/samplesource/audioinput/audioinputgui.cpp +++ b/plugins/samplesource/audioinput/audioinputgui.cpp @@ -44,7 +44,8 @@ AudioInputGui::AudioInputGui(DeviceUISet *deviceUISet, QWidget* parent) : m_sampleSource = (AudioInput*) m_deviceUISet->m_deviceAPI->getSampleSource(); ui->setupUi(getContents()); - getContents()->setStyleSheet("#AudioInputGui { border: 1px solid #C06900 }"); + getContents()->setStyleSheet(QString(tr("#AudioInputGui { border: 1px solid %1 }") + .arg(palette().highlight().color().darker(115).name()))); m_helpURL = "plugins/samplesource/audioinput/readme.md"; connect(&m_updateTimer, SIGNAL(timeout()), this, SLOT(updateHardware())); diff --git a/plugins/samplesource/bladerf1input/bladerf1inputgui.cpp b/plugins/samplesource/bladerf1input/bladerf1inputgui.cpp index 06bf09a2c..11ac97b8d 100644 --- a/plugins/samplesource/bladerf1input/bladerf1inputgui.cpp +++ b/plugins/samplesource/bladerf1input/bladerf1inputgui.cpp @@ -49,7 +49,8 @@ Bladerf1InputGui::Bladerf1InputGui(DeviceUISet *deviceUISet, QWidget* parent) : m_sampleSource = (Bladerf1Input*) m_deviceUISet->m_deviceAPI->getSampleSource(); ui->setupUi(getContents()); - getContents()->setStyleSheet("#Bladerf1InputGui { border: 1px solid #C06900 }"); + getContents()->setStyleSheet(QString(tr("#Bladerf1InputGui { border: 1px solid %1 }") + .arg(palette().highlight().color().darker(115).name()))); m_helpURL = "plugins/samplesource/bladerf1input/readme.md"; ui->centerFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold)); ui->centerFrequency->setValueRange(7, BLADERF_FREQUENCY_MIN_XB200/1000, BLADERF_FREQUENCY_MAX/1000); diff --git a/plugins/samplesource/bladerf2input/bladerf2inputgui.cpp b/plugins/samplesource/bladerf2input/bladerf2inputgui.cpp index 824c0e927..e05c5c149 100644 --- a/plugins/samplesource/bladerf2input/bladerf2inputgui.cpp +++ b/plugins/samplesource/bladerf2input/bladerf2inputgui.cpp @@ -52,7 +52,8 @@ BladeRF2InputGui::BladeRF2InputGui(DeviceUISet *deviceUISet, QWidget* parent) : uint64_t f_min, f_max; ui->setupUi(getContents()); - getContents()->setStyleSheet("#Bladerf2InputGui { border: 1px solid #C06900 }"); + getContents()->setStyleSheet(QString(tr("#Bladerf2InputGui { border: 1px solid %1 }") + .arg(palette().highlight().color().darker(115).name()))); m_helpURL = "plugins/samplesource/bladerf2input/readme.md"; m_sampleSource->getFrequencyRange(f_min, f_max, step, scale); diff --git a/plugins/samplesource/fcdpro/fcdprogui.cpp b/plugins/samplesource/fcdpro/fcdprogui.cpp index c3fbe5877..33840a7da 100644 --- a/plugins/samplesource/fcdpro/fcdprogui.cpp +++ b/plugins/samplesource/fcdpro/fcdprogui.cpp @@ -45,7 +45,8 @@ FCDProGui::FCDProGui(DeviceUISet *deviceUISet, QWidget* parent) : m_sampleSource = (FCDProInput*) m_deviceUISet->m_deviceAPI->getSampleSource(); ui->setupUi(getContents()); - getContents()->setStyleSheet("#FCDProGui { border: 1px solid #C06900 }"); + getContents()->setStyleSheet(QString(tr("#FCDProGui { border: 1px solid %1 }") + .arg(palette().highlight().color().darker(115).name()))); m_helpURL = "plugins/samplesource/fcdpro/readme.md"; ui->centerFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold)); updateFrequencyLimits(); diff --git a/plugins/samplesource/fcdproplus/fcdproplusgui.cpp b/plugins/samplesource/fcdproplus/fcdproplusgui.cpp index 075eb4a05..3fa840b25 100644 --- a/plugins/samplesource/fcdproplus/fcdproplusgui.cpp +++ b/plugins/samplesource/fcdproplus/fcdproplusgui.cpp @@ -46,7 +46,8 @@ FCDProPlusGui::FCDProPlusGui(DeviceUISet *deviceUISet, QWidget* parent) : m_sampleSource = (FCDProPlusInput*) m_deviceUISet->m_deviceAPI->getSampleSource(); ui->setupUi(getContents()); - getContents()->setStyleSheet("#FCDProPlusGui { border: 1px solid #C06900 }"); + getContents()->setStyleSheet(QString(tr("#FCDProPlusGui { border: 1px solid %1 }") + .arg(palette().highlight().color().darker(115).name()))); m_helpURL = "plugins/samplesource/fcdproplus/readme.md"; ui->centerFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold)); diff --git a/plugins/samplesource/fileinput/fileinputgui.cpp b/plugins/samplesource/fileinput/fileinputgui.cpp index d4f7aee5a..3a3d26dc3 100644 --- a/plugins/samplesource/fileinput/fileinputgui.cpp +++ b/plugins/samplesource/fileinput/fileinputgui.cpp @@ -57,7 +57,8 @@ FileInputGUI::FileInputGUI(DeviceUISet *deviceUISet, QWidget* parent) : { setAttribute(Qt::WA_DeleteOnClose, true); ui->setupUi(getContents()); - getContents()->setStyleSheet("#FileInputGUI { border: 1px solid #C06900 }"); + getContents()->setStyleSheet(QString(tr("#FileInputGUI { border: 1px solid %1 }") + .arg(palette().highlight().color().darker(115).name()))); m_helpURL = "plugins/samplesource/fileinput/readme.md"; ui->crcLabel->setStyleSheet("QLabel { background:rgb(79,79,79); }"); diff --git a/plugins/samplesource/hackrfinput/hackrfinputgui.cpp b/plugins/samplesource/hackrfinput/hackrfinputgui.cpp index 9bab88194..358c8b1b4 100644 --- a/plugins/samplesource/hackrfinput/hackrfinputgui.cpp +++ b/plugins/samplesource/hackrfinput/hackrfinputgui.cpp @@ -50,7 +50,8 @@ HackRFInputGui::HackRFInputGui(DeviceUISet *deviceUISet, QWidget* parent) : m_sampleSource = (HackRFInput*) m_deviceUISet->m_deviceAPI->getSampleSource(); ui->setupUi(getContents()); - getContents()->setStyleSheet("#HackRFInputGui { border: 1px solid #C06900 }"); + getContents()->setStyleSheet(QString(tr("#HackRFInputGui { border: 1px solid %1 }") + .arg(palette().highlight().color().darker(115).name()))); m_helpURL = "plugins/samplesource/hackrfinput/readme.md"; ui->centerFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold)); ui->centerFrequency->setValueRange(7, 0U, 7250000U); diff --git a/plugins/samplesource/kiwisdr/kiwisdrgui.cpp b/plugins/samplesource/kiwisdr/kiwisdrgui.cpp index dc339541a..1b1799b0f 100644 --- a/plugins/samplesource/kiwisdr/kiwisdrgui.cpp +++ b/plugins/samplesource/kiwisdr/kiwisdrgui.cpp @@ -68,7 +68,8 @@ KiwiSDRGui::KiwiSDRGui(DeviceUISet *deviceUISet, QWidget* parent) : m_statusColors.push_back("rgb(232, 85, 232)"); // Disconnected (magenta) ui->setupUi(getContents()); - getContents()->setStyleSheet("#KiwiSDRGui { border: 1px solid #C06900 }"); + getContents()->setStyleSheet(QString(tr("#KiwiSDRGui { border: 1px solid %1 }") + .arg(palette().highlight().color().darker(115).name()))); m_helpURL = "plugins/samplesource/kiwisdr/readme.md"; ui->centerFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold)); ui->centerFrequency->setValueRange(7, 0, 9999999); diff --git a/plugins/samplesource/limesdrinput/limesdrinputgui.cpp b/plugins/samplesource/limesdrinput/limesdrinputgui.cpp index 1cdfcfe58..43937ca8c 100644 --- a/plugins/samplesource/limesdrinput/limesdrinputgui.cpp +++ b/plugins/samplesource/limesdrinput/limesdrinputgui.cpp @@ -50,7 +50,8 @@ LimeSDRInputGUI::LimeSDRInputGUI(DeviceUISet *deviceUISet, QWidget* parent) : m_limeSDRInput = (LimeSDRInput*) m_deviceUISet->m_deviceAPI->getSampleSource(); ui->setupUi(getContents()); - getContents()->setStyleSheet("#LimeSDRInputGUI { border: 1px solid #C06900 }"); + getContents()->setStyleSheet(QString(tr("#LimeSDRInputGUI { border: 1px solid %1 }") + .arg(palette().highlight().color().darker(115).name()))); m_helpURL = "plugins/samplesource/limesdrinput/readme.md"; float minF, maxF; diff --git a/plugins/samplesource/localinput/localinputgui.cpp b/plugins/samplesource/localinput/localinputgui.cpp index dbfb078fb..81edc2066 100644 --- a/plugins/samplesource/localinput/localinputgui.cpp +++ b/plugins/samplesource/localinput/localinputgui.cpp @@ -76,7 +76,8 @@ LocalInputGui::LocalInputGui(DeviceUISet *deviceUISet, QWidget* parent) : m_startingTimeStampms = 0; ui->setupUi(getContents()); - getContents()->setStyleSheet("#LocalInputGui { border: 1px solid #C06900 }"); + getContents()->setStyleSheet(QString(tr("#LocalInputGui { border: 1px solid %1 }") + .arg(palette().highlight().color().darker(115).name()))); m_helpURL = "plugins/samplesource/localinput/readme.md"; CRightClickEnabler *startStopRightClickEnabler = new CRightClickEnabler(ui->startStop); diff --git a/plugins/samplesource/perseus/perseusgui.cpp b/plugins/samplesource/perseus/perseusgui.cpp index 5e1332f9a..a25a47957 100644 --- a/plugins/samplesource/perseus/perseusgui.cpp +++ b/plugins/samplesource/perseus/perseusgui.cpp @@ -45,7 +45,8 @@ PerseusGui::PerseusGui(DeviceUISet *deviceUISet, QWidget* parent) : m_sampleSource = (PerseusInput*) m_deviceUISet->m_deviceAPI->getSampleSource(); ui->setupUi(getContents()); - getContents()->setStyleSheet("#PerseusGui { border: 1px solid #C06900 }"); + getContents()->setStyleSheet(QString(tr("#PerseusGui { border: 1px solid %1 }") + .arg(palette().highlight().color().darker(115).name()))); m_helpURL = "plugins/samplesource/perseus/readme.md"; ui->centerFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold)); updateFrequencyLimits(); diff --git a/plugins/samplesource/plutosdrinput/plutosdrinputgui.cpp b/plugins/samplesource/plutosdrinput/plutosdrinputgui.cpp index 70a037c63..7b9a71a29 100644 --- a/plugins/samplesource/plutosdrinput/plutosdrinputgui.cpp +++ b/plugins/samplesource/plutosdrinput/plutosdrinputgui.cpp @@ -50,7 +50,8 @@ PlutoSDRInputGui::PlutoSDRInputGui(DeviceUISet *deviceUISet, QWidget* parent) : m_sampleSource = (PlutoSDRInput*) m_deviceUISet->m_deviceAPI->getSampleSource(); ui->setupUi(getContents()); - getContents()->setStyleSheet("#PlutoSDRInputGUI { border: 1px solid #C06900 }"); + getContents()->setStyleSheet(QString(tr("#PlutoSDRInputGUI { border: 1px solid %1 }") + .arg(palette().highlight().color().darker(115).name()))); m_helpURL = "plugins/samplesource/plutosdrinput/readme.md"; ui->centerFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold)); updateFrequencyLimits(); diff --git a/plugins/samplesource/remoteinput/remoteinputgui.cpp b/plugins/samplesource/remoteinput/remoteinputgui.cpp index 6de87f3e4..d4e821181 100644 --- a/plugins/samplesource/remoteinput/remoteinputgui.cpp +++ b/plugins/samplesource/remoteinput/remoteinputgui.cpp @@ -73,7 +73,8 @@ RemoteInputGui::RemoteInputGui(DeviceUISet *deviceUISet, QWidget* parent) : m_startingTimeStampms = 0; ui->setupUi(getContents()); - getContents()->setStyleSheet("#RemoteInputGui { border: 1px solid #C06900 }"); + getContents()->setStyleSheet(QString(tr("#RemoteInputGui { border: 1px solid %1 }") + .arg(palette().highlight().color().darker(115).name()))); m_helpURL = "plugins/samplesource/remoteinput/readme.md"; ui->remoteDeviceFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold)); diff --git a/plugins/samplesource/rtlsdr/rtlsdrgui.cpp b/plugins/samplesource/rtlsdr/rtlsdrgui.cpp index 36c7edee0..71a2f3b9d 100644 --- a/plugins/samplesource/rtlsdr/rtlsdrgui.cpp +++ b/plugins/samplesource/rtlsdr/rtlsdrgui.cpp @@ -47,7 +47,9 @@ RTLSDRGui::RTLSDRGui(DeviceUISet *deviceUISet, QWidget* parent) : m_sampleSource = (RTLSDRInput*) m_deviceUISet->m_deviceAPI->getSampleSource(); ui->setupUi(getContents()); - getContents()->setStyleSheet("#RTLSDRGui { border: 1px solid #C06900 }"); + QString s(tr("")); + getContents()->setStyleSheet(QString(tr("#RTLSDRGui { border: 1px solid %1 }") + .arg(palette().highlight().color().darker(115).name()))); m_helpURL = "plugins/samplesource/rtlsdr/readme.md"; ui->centerFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold)); updateFrequencyLimits(); diff --git a/plugins/samplesource/sdrplay/sdrplaygui.cpp b/plugins/samplesource/sdrplay/sdrplaygui.cpp index bba28d7e0..ddeddb627 100644 --- a/plugins/samplesource/sdrplay/sdrplaygui.cpp +++ b/plugins/samplesource/sdrplay/sdrplaygui.cpp @@ -44,7 +44,8 @@ SDRPlayGui::SDRPlayGui(DeviceUISet *deviceUISet, QWidget* parent) : m_sampleSource = (SDRPlayInput*) m_deviceUISet->m_deviceAPI->getSampleSource(); ui->setupUi(getContents()); - getContents()->setStyleSheet("#SDRPlayGui { border: 1px solid #C06900 }"); + getContents()->setStyleSheet(QString(tr("#SDRPlayGui { border: 1px solid %1 }") + .arg(palette().highlight().color().darker(115).name()))); m_helpURL = "plugins/samplesource/sdrplay/readme.md"; ui->centerFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold)); ui->centerFrequency->setValueRange(7, 10U, 12000U); diff --git a/plugins/samplesource/sdrplayv3/sdrplayv3gui.cpp b/plugins/samplesource/sdrplayv3/sdrplayv3gui.cpp index 56344262e..73be322df 100644 --- a/plugins/samplesource/sdrplayv3/sdrplayv3gui.cpp +++ b/plugins/samplesource/sdrplayv3/sdrplayv3gui.cpp @@ -44,7 +44,8 @@ SDRPlayV3Gui::SDRPlayV3Gui(DeviceUISet *deviceUISet, QWidget* parent) : m_sdrPlayV3Input = (SDRPlayV3Input*) m_deviceUISet->m_deviceAPI->getSampleSource(); ui->setupUi(getContents()); - getContents()->setStyleSheet("#SDRPlayV3Gui { border: 1px solid #C06900 }"); + getContents()->setStyleSheet(QString(tr("#SDRPlayV3Gui { border: 1px solid %1 }") + .arg(palette().highlight().color().darker(115).name()))); m_helpURL = "plugins/samplesource/sdrplayv3/readme.md"; ui->centerFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold)); updateFrequencyLimits(); diff --git a/plugins/samplesource/sigmffileinput/sigmffileinputgui.cpp b/plugins/samplesource/sigmffileinput/sigmffileinputgui.cpp index 1e07aab74..41bcd1979 100644 --- a/plugins/samplesource/sigmffileinput/sigmffileinputgui.cpp +++ b/plugins/samplesource/sigmffileinput/sigmffileinputgui.cpp @@ -63,7 +63,8 @@ SigMFFileInputGUI::SigMFFileInputGUI(DeviceUISet *deviceUISet, QWidget* parent) { setAttribute(Qt::WA_DeleteOnClose, true); ui->setupUi(getContents()); - getContents()->setStyleSheet("#SigMFFileInputGUI { border: 1px solid #C06900 }"); + getContents()->setStyleSheet(QString(tr("#SigMFFileInputGUI { border: 1px solid %1 }") + .arg(palette().highlight().color().darker(115).name()))); m_helpURL = "plugins/samplesource/sigmffileinput/readme.md"; ui->fileNameText->setText(m_metaFileName); diff --git a/plugins/samplesource/soapysdrinput/soapysdrinputgui.cpp b/plugins/samplesource/soapysdrinput/soapysdrinputgui.cpp index b13efebe3..500b3cedc 100644 --- a/plugins/samplesource/soapysdrinput/soapysdrinputgui.cpp +++ b/plugins/samplesource/soapysdrinput/soapysdrinputgui.cpp @@ -62,7 +62,8 @@ SoapySDRInputGui::SoapySDRInputGui(DeviceUISet *deviceUISet, QWidget* parent) : setAttribute(Qt::WA_DeleteOnClose, true); m_sampleSource = (SoapySDRInput*) m_deviceUISet->m_deviceAPI->getSampleSource(); ui->setupUi(getContents()); - getContents()->setStyleSheet("#SoapySDRInputGui { border: 1px solid #C06900 }"); + getContents()->setStyleSheet(QString(tr("#SoapySDRInputGui { border: 1px solid %1 }") + .arg(palette().highlight().color().darker(115).name()))); m_helpURL = "plugins/samplesource/soapysdrinput/readme.md"; ui->centerFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold)); diff --git a/plugins/samplesource/testsource/testsourcegui.cpp b/plugins/samplesource/testsource/testsourcegui.cpp index 211d411cd..27a859af3 100644 --- a/plugins/samplesource/testsource/testsourcegui.cpp +++ b/plugins/samplesource/testsource/testsourcegui.cpp @@ -55,7 +55,8 @@ TestSourceGui::TestSourceGui(DeviceUISet *deviceUISet, QWidget* parent) : m_sampleSource = m_deviceUISet->m_deviceAPI->getSampleSource(); ui->setupUi(getContents()); - getContents()->setStyleSheet("#TestSourceGui { border: 1px solid #C06900 }"); + getContents()->setStyleSheet(QString(tr("#TestSourceGui { border: 1px solid %1 }") + .arg(palette().highlight().color().darker(115).name()))); m_helpURL = "plugins/samplesource/testsource/readme.md"; ui->centerFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold)); ui->centerFrequency->setValueRange(7, 0, 9999999); diff --git a/plugins/samplesource/usrpinput/usrpinputgui.cpp b/plugins/samplesource/usrpinput/usrpinputgui.cpp index 3325f91d9..bdc8459d4 100644 --- a/plugins/samplesource/usrpinput/usrpinputgui.cpp +++ b/plugins/samplesource/usrpinput/usrpinputgui.cpp @@ -51,7 +51,8 @@ USRPInputGUI::USRPInputGUI(DeviceUISet *deviceUISet, QWidget* parent) : m_usrpInput = (USRPInput*) m_deviceUISet->m_deviceAPI->getSampleSource(); ui->setupUi(getContents()); - getContents()->setStyleSheet("#USRPInputGUI { border: 1px solid #C06900 }"); + getContents()->setStyleSheet(QString(tr("#USRPInputGUI { border: 1px solid %1 }") + .arg(palette().highlight().color().darker(115).name()))); m_helpURL = "plugins/samplesource/usrpinput/readme.md"; float minF, maxF; diff --git a/plugins/samplesource/xtrxinput/xtrxinputgui.cpp b/plugins/samplesource/xtrxinput/xtrxinputgui.cpp index 17ff6f57c..4f46ed0da 100644 --- a/plugins/samplesource/xtrxinput/xtrxinputgui.cpp +++ b/plugins/samplesource/xtrxinput/xtrxinputgui.cpp @@ -51,7 +51,8 @@ XTRXInputGUI::XTRXInputGUI(DeviceUISet *deviceUISet, QWidget* parent) : m_XTRXInput = (XTRXInput*) m_deviceUISet->m_deviceAPI->getSampleSource(); ui->setupUi(getContents()); - getContents()->setStyleSheet("#XTRXInputGUI { border: 1px solid #C06900 }"); + getContents()->setStyleSheet(QString(tr("#XTRXInputGUI { border: 1px solid %1 }") + .arg(palette().highlight().color().darker(115).name()))); m_helpURL = "plugins/samplesource/xtrxinput/readme.md"; float minF, maxF, stepF; diff --git a/sdrgui/gui/rollupcontents.cpp b/sdrgui/gui/rollupcontents.cpp index d8077c96c..19294e78e 100644 --- a/sdrgui/gui/rollupcontents.cpp +++ b/sdrgui/gui/rollupcontents.cpp @@ -41,44 +41,6 @@ RollupContents::RollupContents(QWidget* parent) : setAttribute(Qt::WA_OpaquePaintEvent, true); } -// QByteArray RollupWidget::saveState(int version) const -// { -// QByteArray state; -// QDataStream stream(&state, QIODevice::WriteOnly); -// int count = 0; - -// for (int i = 0; i < children().count(); ++i) -// { -// QWidget* r = qobject_cast(children()[i]); - -// if (r) { -// count++; -// } -// } - -// stream << VersionMarker; -// stream << version; -// stream << count; - -// for (int i = 0; i < children().count(); ++i) -// { -// QWidget* r = qobject_cast(children()[i]); - -// if (r) -// { -// stream << r->objectName(); - -// if (r->isHidden()) { -// stream << (int) 0; -// } else { -// stream << (int) 1; -// } -// } -// } - -// return state; -// } - void RollupContents::saveState(RollupState &state) const { QList& childrenStates = state.getChildren(); @@ -94,64 +56,6 @@ void RollupContents::saveState(RollupState &state) const } } -// bool RollupWidget::restoreState(const QByteArray& state, int version) -// { -// if (state.isEmpty()) { -// return false; -// } - -// QByteArray sd = state; -// QDataStream stream(&sd, QIODevice::ReadOnly); -// int marker, v; -// stream >> marker; -// stream >> v; - -// if ((stream.status() != QDataStream::Ok) || (marker != VersionMarker) || (v != version)) { -// return false; -// } - -// int count; -// stream >> count; - -// if (stream.status() != QDataStream::Ok) { -// return false; -// } - -// for (int i = 0; i < count; ++i) -// { -// QString name; -// int visible; - -// stream >> name; -// stream >> visible; - -// if (stream.status() != QDataStream::Ok) { -// return false; -// } - -// for (int j = 0; j < children().count(); ++j) -// { -// QWidget* r = qobject_cast(children()[j]); - -// if (r) -// { -// if (r->objectName() == name) -// { -// if (visible) { -// r->show(); -// } else { -// r->hide(); -// } - -// break; -// } -// } -// } -// } - -// return true; -// } - void RollupContents::restoreState(const RollupState& state) { const QList& childrenStates = state.getChildren(); @@ -284,7 +188,7 @@ int RollupContents::arrangeRollups() void RollupContents::paintEvent(QPaintEvent*) { QPainter p(this); - QColor frame = palette().highlight().color(); + QColor frameColor = palette().highlight().color().darker(115); // Eigenbau QFontMetrics fm(font()); @@ -292,82 +196,20 @@ void RollupContents::paintEvent(QPaintEvent*) p.setRenderHint(QPainter::Antialiasing, true); // Ecken (corners) - p.setPen(Qt::NoPen); - p.setBrush(palette().base()); - p.drawRect(0, 0, 5, 5); - p.drawRect(width() - 5, 0, 5, 5); - p.drawRect(0, height() - 5, 5, 5); - p.drawRect(width() - 5, height() - 5, 5, 5); + // p.setPen(Qt::NoPen); + // p.setBrush(palette().base()); + // p.drawRect(0, 0, 5, 5); + // p.drawRect(width() - 5, 0, 5, 5); + // p.drawRect(0, height() - 5, 5, 5); + // p.drawRect(width() - 5, height() - 5, 5, 5); // Rahmen (frame) - p.setPen(m_highlighted ? Qt::white : frame); + p.setPen(m_highlighted ? Qt::white : frameColor); p.setBrush(palette().window()); QRectF r(rect()); r.adjust(0.5, 0.5, -0.5, -0.5); - p.drawRoundedRect(r, 3.0, 3.0, Qt::AbsoluteSize); - - // // Titel-Hintergrund (Title background) - // p.setPen(Qt::NoPen); - // p.setBrush(m_titleColor); - // QPainterPath path; - // path.moveTo(1.5, fm.height() + 2.5); - // path.lineTo(width() - 1.5, fm.height() + 2.5); - // path.lineTo(width() - 1.5, 3.5); - // path.arcTo(QRectF(width() - 3.5, 0, 2.5, 2.5), 270, -90); - // path.lineTo(3.5, 1.5); - // path.arcTo(QRectF(1.5, 2.5, 2.5, 2.5), 90, 90); - // p.drawPath(path); - - // // Titel-Abschlusslinie (Title closing line) - // p.setPen(frame); - // p.drawLine(QPointF(0.5, 2 + fm.height() + 1.5), QPointF(width() - 1.5, 2 + fm.height() + 1.5)); - - // // Aktiv-Button links - // p.setPen(QPen(palette().windowText().color(), 1.0)); - // p.setBrush(palette().light()); - // p.drawRoundedRect(QRectF(3.5, 3.5, fm.ascent(), fm.ascent()), 2.0, 2.0, Qt::AbsoluteSize); - // p.setPen(QPen(Qt::white, 1.0)); - // p.drawText(QRectF(3.5, 2.5, fm.ascent(), fm.ascent()), Qt::AlignCenter, "c"); - - // if (m_channelWidget) - // { - // // Stromkanal-Button links (Current channel) - // p.setPen(QPen(palette().windowText().color(), 1.0)); - // p.setBrush(palette().light()); - // p.drawRoundedRect(QRectF(5.5 + fm.ascent(), 2.5, fm.ascent() + 2.0, fm.ascent() + 2.0), 2.0, 2.0, Qt::AbsoluteSize); - // p.setPen(QPen(Qt::white, 1.0)); - // p.drawText(QRectF(5.5 + fm.ascent(), 2.5, fm.ascent() + 2.0, fm.ascent() + 2.0), Qt::AlignCenter, m_streamIndicator); - // } - - // // Help button - // if (!m_helpURL.isEmpty()) - // { - // p.setRenderHint(QPainter::Antialiasing, true); - // p.setPen(QPen(palette().windowText().color(), 1.0)); - // p.setBrush(palette().light()); - // r = QRectF(width() - 2*(3.5 + fm.ascent()), 3.5, fm.ascent(), fm.ascent()); - // p.drawRoundedRect(r, 2.0, 2.0, Qt::AbsoluteSize); - // p.drawText(QRectF(width() - 2*(3.5 + fm.ascent()), 5, fm.ascent(), fm.ascent() - 2), Qt::AlignCenter, "?"); - // } - - // //p.drawLine(r.topLeft() + QPointF(1, 1), r.bottomRight() + QPointF(-1, -1)); - // //p.drawLine(r.bottomLeft() + QPointF(1, -1), r.topRight() + QPointF(-1, 1)); - - // // Schließen-Button rechts (Close button on the right) - // p.setRenderHint(QPainter::Antialiasing, true); - // p.setPen(QPen(palette().windowText().color(), 1.0)); - // p.setBrush(palette().light()); - // r = QRectF(width() - 3.5 - fm.ascent(), 3.5, fm.ascent(), fm.ascent()); - // p.drawRoundedRect(r, 2.0, 2.0, Qt::AbsoluteSize); - // p.setPen(QPen(palette().windowText().color(), 1.5)); - // p.drawLine(r.topLeft() + QPointF(1, 1), r.bottomRight() + QPointF(-1, -1)); - // p.drawLine(r.bottomLeft() + QPointF(1, -1), r.topRight() + QPointF(-1, 1)); - - // // Titel - // //p.setPen(palette().highlightedText().color()); - // p.setPen(m_titleTextColor); - // p.drawText(QRect(2 + 2*fm.height() + 2, 2, width() - 6 - 3*fm.height(), fm.height()), - // fm.elidedText(windowTitle(), Qt::ElideMiddle, width() - 6 - 3*fm.height(), 0)); + // p.drawRoundedRect(r, 3.0, 3.0, Qt::AbsoluteSize); + p.drawRect(r); // Rollups int pos = 2; // fm.height() + 4; @@ -396,11 +238,11 @@ void RollupContents::paintEvent(QPaintEvent*) } } - pos += paintRollup(qobject_cast(*w), pos, &p, n == c.end(), frame); + pos += paintRollup(qobject_cast(*w), pos, &p, n == c.end(), frameColor); } } -int RollupContents::paintRollup(QWidget* rollup, int pos, QPainter* p, bool last, const QColor& frame) +int RollupContents::paintRollup(QWidget* rollup, int pos, QPainter* p, bool last, const QColor& frameColor) { QFontMetrics fm(font()); int height = 1; @@ -418,7 +260,7 @@ int RollupContents::paintRollup(QWidget* rollup, int pos, QPainter* p, bool last { if (!last) { - p->setPen(frame); + p->setPen(frameColor); p->drawLine(QPointF(1.5, pos + fm.height() + 1.5), QPointF(width() - 1.5, pos + fm.height() + 1.5)); height++; } @@ -455,7 +297,7 @@ int RollupContents::paintRollup(QWidget* rollup, int pos, QPainter* p, bool last if (!rollup->isHidden() && (!last)) { // Rollup-Abschlusslinie - p->setPen(frame); + p->setPen(frameColor); p->drawLine(QPointF(1.5, pos + fm.height() + rollup->height() + 6.5), QPointF(width() - 1.5, pos + fm.height() + rollup->height() + 6.5)); height += rollup->height() + 4; @@ -475,44 +317,6 @@ void RollupContents::mousePressEvent(QMouseEvent* event) { QFontMetrics fm(font()); - // // menu box left - // if (QRectF(3.5, 3.5, fm.ascent(), fm.ascent()).contains(event->pos())) - // { - // m_contextMenuType = ContextMenuChannelSettings; - // emit customContextMenuRequested(event->globalPos()); - // return; - // } - - // if (m_channelWidget) - // { - // // Stream channel menu left - // if (QRectF(5.5 + fm.ascent(), 2.5, fm.ascent() + 2.0, fm.ascent() + 2.0).contains(event->pos())) - // { - // m_contextMenuType = ContextMenuStreamSettings; - // emit customContextMenuRequested(event->globalPos()); - // return; - // } - // } - - // // help button - // if(!m_helpURL.isEmpty() && QRectF(width() - 2*(3.5 + fm.ascent()), 3.5, fm.ascent(), fm.ascent()).contains(event->pos())) - // { - // QString url; - // if (m_helpURL.startsWith("http")) { - // url = m_helpURL; - // } else { - // url = QString("https://github.com/f4exb/sdrangel/blob/master/%1").arg(m_helpURL); // Something like "plugins/channelrx/chanalyzer/readme.md" - // } - // QDesktopServices::openUrl(QUrl(url)); - // return; - // } - - // // close button right - // if(QRectF(width() - 3.5 - fm.ascent(), 3.5, fm.ascent(), fm.ascent()).contains(event->pos())) { - // close(); - // return; - // } - // check if we need to change a rollup widget int pos = 2; // fm.height() + 4; diff --git a/sdrgui/gui/rollupcontents.h b/sdrgui/gui/rollupcontents.h index e4eeb3ca6..649398a17 100644 --- a/sdrgui/gui/rollupcontents.h +++ b/sdrgui/gui/rollupcontents.h @@ -31,10 +31,7 @@ class SDRGUI_API RollupContents : public QWidget { public: RollupContents(QWidget* parent = nullptr); void setHighlighted(bool highlighted); - // void setChannelWidget(bool channelWidget) { m_channelWidget = channelWidget; } - // QByteArray saveState(int version = 0) const; void saveState(RollupState& state) const; - // bool restoreState(const QByteArray& state, int version = 0); void restoreState(const RollupState& state); int arrangeRollups(); @@ -51,7 +48,7 @@ protected: QString m_helpURL; void paintEvent(QPaintEvent*); - int paintRollup(QWidget* rollup, int pos, QPainter* p, bool last, const QColor& frame); + int paintRollup(QWidget* rollup, int pos, QPainter* p, bool last, const QColor& frameColor); void resizeEvent(QResizeEvent* size); void mousePressEvent(QMouseEvent* event); From 2f89b79c8491a970c9208a616e1599ac4e2c10ba Mon Sep 17 00:00:00 2001 From: f4exb Date: Tue, 12 Apr 2022 16:20:45 +0200 Subject: [PATCH 010/115] Massive UI revamping (v7): added channels --- .../beamsteeringcwmodgui.cpp | 20 +- .../beamsteeringcwmod/beamsteeringcwmodgui.h | 7 + .../beamsteeringcwmod/beamsteeringcwmodgui.ui | 36 +- .../interferometer/interferometergui.cpp | 20 +- .../interferometer/interferometergui.h | 7 + .../interferometer/interferometergui.ui | 52 ++- .../channelrx/chanalyzer/chanalyzergui.cpp | 34 +- plugins/channelrx/chanalyzer/chanalyzergui.h | 7 + plugins/channelrx/chanalyzer/chanalyzergui.ui | 82 ++-- .../chanalyzer/chanalyzersettings.cpp | 5 + .../channelrx/chanalyzer/chanalyzersettings.h | 2 + plugins/channelrx/demodadsb/adsbdemodgui.cpp | 44 +- plugins/channelrx/demodadsb/adsbdemodgui.h | 9 + plugins/channelrx/demodadsb/adsbdemodgui.ui | 26 +- .../channelrx/demodadsb/adsbdemodsettings.cpp | 5 + .../channelrx/demodadsb/adsbdemodsettings.h | 2 + plugins/channelrx/demodais/aisdemodgui.cpp | 32 +- plugins/channelrx/demodais/aisdemodgui.h | 7 + plugins/channelrx/demodais/aisdemodgui.ui | 20 +- .../channelrx/demodais/aisdemodsettings.cpp | 7 + plugins/channelrx/demodais/aisdemodsettings.h | 2 + plugins/channelrx/demodam/amdemodgui.cpp | 25 +- plugins/channelrx/demodam/amdemodgui.h | 7 + plugins/channelrx/demodam/amdemodgui.ui | 42 +- plugins/channelrx/demodam/amdemodsettings.cpp | 7 + plugins/channelrx/demodam/amdemodsettings.h | 2 + plugins/channelrx/demodapt/aptdemodgui.cpp | 44 +- plugins/channelrx/demodapt/aptdemodgui.h | 10 +- plugins/channelrx/demodapt/aptdemodgui.ui | 6 +- .../channelrx/demodapt/aptdemodsettings.cpp | 5 + plugins/channelrx/demodapt/aptdemodsettings.h | 2 + plugins/channelrx/demodatv/atvdemodgui.cpp | 38 +- plugins/channelrx/demodatv/atvdemodgui.h | 7 + plugins/channelrx/demodatv/atvdemodgui.ui | 6 +- .../channelrx/demodatv/atvdemodsettings.cpp | 32 ++ plugins/channelrx/demodatv/atvdemodsettings.h | 7 + plugins/channelrx/demodbfm/bfmdemodgui.cpp | 28 +- plugins/channelrx/demodbfm/bfmdemodgui.h | 7 + plugins/channelrx/demodbfm/bfmdemodgui.ui | 6 +- .../channelrx/demodbfm/bfmdemodsettings.cpp | 5 + plugins/channelrx/demodbfm/bfmdemodsettings.h | 2 + .../demodchirpchat/chirpchatdemodgui.cpp | 36 +- .../demodchirpchat/chirpchatdemodgui.h | 7 + .../demodchirpchat/chirpchatdemodgui.ui | 6 +- .../demodchirpchat/chirpchatdemodsettings.cpp | 7 + .../demodchirpchat/chirpchatdemodsettings.h | 2 + plugins/channelrx/demoddab/dabdemodgui.cpp | 32 +- plugins/channelrx/demoddab/dabdemodgui.h | 8 + plugins/channelrx/demoddab/dabdemodgui.ui | 6 +- .../channelrx/demoddab/dabdemodsettings.cpp | 7 + plugins/channelrx/demoddab/dabdemodsettings.h | 2 + plugins/channelrx/demoddatv/datvdemodgui.cpp | 47 +- plugins/channelrx/demoddatv/datvdemodgui.h | 11 +- plugins/channelrx/demoddatv/datvdemodgui.ui | 6 +- .../channelrx/demoddatv/datvdemodsettings.cpp | 7 + .../channelrx/demoddatv/datvdemodsettings.h | 3 + plugins/channelrx/demoddsd/dsddemodgui.cpp | 36 +- plugins/channelrx/demoddsd/dsddemodgui.h | 7 + plugins/channelrx/demoddsd/dsddemodgui.ui | 6 +- .../channelrx/demoddsd/dsddemodsettings.cpp | 7 + plugins/channelrx/demoddsd/dsddemodsettings.h | 2 + .../channelrx/demodfreedv/freedvdemodgui.cpp | 25 +- .../channelrx/demodfreedv/freedvdemodgui.h | 7 + .../channelrx/demodfreedv/freedvdemodgui.ui | 6 +- .../demodfreedv/freedvdemodsettings.cpp | 7 + .../demodfreedv/freedvdemodsettings.h | 2 + plugins/channelrx/demodnfm/nfmdemodgui.cpp | 33 +- plugins/channelrx/demodnfm/nfmdemodgui.h | 7 + plugins/channelrx/demodnfm/nfmdemodgui.ui | 28 +- .../channelrx/demodnfm/nfmdemodsettings.cpp | 7 + plugins/channelrx/demodnfm/nfmdemodsettings.h | 2 + .../channelrx/demodpacket/packetdemodgui.cpp | 32 +- .../channelrx/demodpacket/packetdemodgui.h | 7 + .../channelrx/demodpacket/packetdemodgui.ui | 6 +- .../demodpacket/packetdemodsettings.cpp | 7 + .../demodpacket/packetdemodsettings.h | 2 + .../channelrx/demodpager/pagerdemodgui.cpp | 32 +- plugins/channelrx/demodpager/pagerdemodgui.h | 7 + plugins/channelrx/demodpager/pagerdemodgui.ui | 6 +- .../demodpager/pagerdemodsettings.cpp | 7 + .../channelrx/demodpager/pagerdemodsettings.h | 2 + .../demodradiosonde/radiosondedemodgui.cpp | 31 +- .../demodradiosonde/radiosondedemodgui.h | 7 + .../demodradiosonde/radiosondedemodgui.ui | 6 +- .../radiosondedemodsettings.cpp | 7 + .../demodradiosonde/radiosondedemodsettings.h | 2 + plugins/channelrx/demodssb/ssbdemodgui.cpp | 33 +- plugins/channelrx/demodssb/ssbdemodgui.h | 7 + plugins/channelrx/demodssb/ssbdemodgui.ui | 48 +- .../channelrx/demodssb/ssbdemodsettings.cpp | 7 + plugins/channelrx/demodssb/ssbdemodsettings.h | 2 + plugins/channelrx/demodvor/vordemodgui.cpp | 26 +- plugins/channelrx/demodvor/vordemodgui.h | 8 + plugins/channelrx/demodvor/vordemodgui.ui | 6 +- .../channelrx/demodvor/vordemodsettings.cpp | 7 + plugins/channelrx/demodvor/vordemodsettings.h | 2 + .../channelrx/demodvorsc/vordemodscgui.cpp | 23 +- plugins/channelrx/demodvorsc/vordemodscgui.h | 7 + plugins/channelrx/demodvorsc/vordemodscgui.ui | 6 +- .../demodvorsc/vordemodscsettings.cpp | 7 + .../channelrx/demodvorsc/vordemodscsettings.h | 2 + plugins/channelrx/demodwfm/wfmdemodgui.cpp | 24 +- plugins/channelrx/demodwfm/wfmdemodgui.h | 7 + plugins/channelrx/demodwfm/wfmdemodgui.ui | 6 +- .../channelrx/demodwfm/wfmdemodsettings.cpp | 7 + plugins/channelrx/demodwfm/wfmdemodsettings.h | 2 + plugins/channelrx/filesink/filesinkgui.cpp | 28 +- plugins/channelrx/filesink/filesinkgui.h | 7 + plugins/channelrx/filesink/filesinkgui.ui | 6 +- .../channelrx/filesink/filesinksettings.cpp | 7 + plugins/channelrx/filesink/filesinksettings.h | 2 + .../channelrx/freqtracker/freqtrackergui.cpp | 27 +- .../channelrx/freqtracker/freqtrackergui.h | 7 + .../channelrx/freqtracker/freqtrackergui.ui | 6 +- .../freqtracker/freqtrackersettings.cpp | 7 + .../freqtracker/freqtrackersettings.h | 2 + plugins/channelrx/localsink/localsinkgui.cpp | 21 +- plugins/channelrx/localsink/localsinkgui.h | 7 + plugins/channelrx/localsink/localsinkgui.ui | 6 +- .../channelrx/localsink/localsinksettings.cpp | 7 + .../channelrx/localsink/localsinksettings.h | 2 + .../channelrx/noisefigure/noisefiguregui.cpp | 36 +- .../channelrx/noisefigure/noisefiguregui.h | 7 + .../channelrx/noisefigure/noisefiguregui.ui | 6 +- .../noisefigure/noisefiguresettings.cpp | 7 + .../noisefigure/noisefiguresettings.h | 2 + .../radioastronomy/radioastronomygui.cpp | 148 +++++- .../radioastronomy/radioastronomygui.h | 8 + .../radioastronomy/radioastronomygui.ui | 6 +- .../radioastronomy/radioastronomysettings.cpp | 5 + .../radioastronomy/radioastronomysettings.h | 2 + .../channelrx/radioclock/radioclockgui.cpp | 23 +- plugins/channelrx/radioclock/radioclockgui.h | 8 + plugins/channelrx/radioclock/radioclockgui.ui | 6 +- .../radioclock/radioclocksettings.cpp | 7 + .../channelrx/radioclock/radioclocksettings.h | 3 + .../channelrx/remotesink/remotesinkgui.cpp | 23 +- plugins/channelrx/remotesink/remotesinkgui.h | 7 + plugins/channelrx/remotesink/remotesinkgui.ui | 6 +- .../remotesink/remotesinksettings.cpp | 7 + .../channelrx/remotesink/remotesinksettings.h | 2 + .../sigmffilesink/sigmffilesinkgui.cpp | 28 +- .../sigmffilesink/sigmffilesinkgui.h | 7 + .../sigmffilesink/sigmffilesinkgui.ui | 6 +- .../sigmffilesink/sigmffilesinksettings.cpp | 7 + .../sigmffilesink/sigmffilesinksettings.h | 2 + plugins/channelrx/udpsink/udpsinkgui.cpp | 34 +- plugins/channelrx/udpsink/udpsinkgui.h | 7 + plugins/channelrx/udpsink/udpsinkgui.ui | 6 +- plugins/channelrx/udpsink/udpsinksettings.cpp | 7 + plugins/channelrx/udpsink/udpsinksettings.h | 2 + .../channeltx/filesource/filesourcegui.cpp | 23 +- plugins/channeltx/filesource/filesourcegui.h | 7 + plugins/channeltx/filesource/filesourcegui.ui | 6 +- .../filesource/filesourcesettings.cpp | 7 + .../channeltx/filesource/filesourcesettings.h | 2 + .../channeltx/localsource/localsourcegui.cpp | 21 +- .../channeltx/localsource/localsourcegui.h | 7 + .../channeltx/localsource/localsourcegui.ui | 6 +- .../localsource/localsourcesettings.cpp | 7 + .../localsource/localsourcesettings.h | 2 + .../mod802.15.4/ieee_802_15_4_modgui.cpp | 34 +- .../mod802.15.4/ieee_802_15_4_modgui.h | 11 +- .../mod802.15.4/ieee_802_15_4_modgui.ui | 6 +- .../mod802.15.4/ieee_802_15_4_modsettings.cpp | 7 + .../mod802.15.4/ieee_802_15_4_modsettings.h | 2 + plugins/channeltx/modais/aismodgui.cpp | 40 +- plugins/channeltx/modais/aismodgui.h | 7 + plugins/channeltx/modais/aismodgui.ui | 6 +- plugins/channeltx/modais/aismodsettings.cpp | 7 + plugins/channeltx/modais/aismodsettings.h | 2 + plugins/channeltx/modam/ammodgui.cpp | 31 +- plugins/channeltx/modam/ammodgui.h | 7 + plugins/channeltx/modam/ammodgui.ui | 6 +- plugins/channeltx/modam/ammodsettings.cpp | 7 + plugins/channeltx/modam/ammodsettings.h | 2 + plugins/channeltx/modatv/atvmodgui.cpp | 40 +- plugins/channeltx/modatv/atvmodgui.h | 7 + plugins/channeltx/modatv/atvmodgui.ui | 6 +- plugins/channeltx/modatv/atvmodsettings.cpp | 7 + plugins/channeltx/modatv/atvmodsettings.h | 2 + .../modchirpchat/chirpchatmodgui.cpp | 42 +- .../channeltx/modchirpchat/chirpchatmodgui.h | 7 + .../channeltx/modchirpchat/chirpchatmodgui.ui | 6 +- .../modchirpchat/chirpchatmodsettings.cpp | 7 + .../modchirpchat/chirpchatmodsettings.h | 2 + plugins/channeltx/moddatv/datvmodgui.cpp | 30 +- plugins/channeltx/moddatv/datvmodgui.h | 8 + plugins/channeltx/moddatv/datvmodgui.ui | 6 +- plugins/channeltx/moddatv/datvmodsettings.cpp | 7 + plugins/channeltx/moddatv/datvmodsettings.h | 2 + plugins/channeltx/modfreedv/freedvmodgui.cpp | 29 +- plugins/channeltx/modfreedv/freedvmodgui.h | 7 + plugins/channeltx/modfreedv/freedvmodgui.ui | 6 +- .../channeltx/modfreedv/freedvmodsettings.cpp | 7 + .../channeltx/modfreedv/freedvmodsettings.h | 2 + plugins/channeltx/modnfm/nfmmodgui.cpp | 38 +- plugins/channeltx/modnfm/nfmmodgui.h | 7 + plugins/channeltx/modnfm/nfmmodgui.ui | 6 +- plugins/channeltx/modnfm/nfmmodsettings.cpp | 7 + plugins/channeltx/modnfm/nfmmodsettings.h | 2 + plugins/channeltx/modpacket/packetmodgui.cpp | 35 +- plugins/channeltx/modpacket/packetmodgui.h | 7 + plugins/channeltx/modpacket/packetmodgui.ui | 6 +- .../channeltx/modpacket/packetmodsettings.cpp | 7 + .../channeltx/modpacket/packetmodsettings.h | 2 + plugins/channeltx/modssb/ssbmodgui.cpp | 38 +- plugins/channeltx/modssb/ssbmodgui.h | 7 + plugins/channeltx/modssb/ssbmodgui.ui | 6 +- plugins/channeltx/modssb/ssbmodsettings.cpp | 7 + plugins/channeltx/modssb/ssbmodsettings.h | 2 + plugins/channeltx/modwfm/wfmmodgui.cpp | 32 +- plugins/channeltx/modwfm/wfmmodgui.h | 7 + plugins/channeltx/modwfm/wfmmodgui.ui | 6 +- plugins/channeltx/modwfm/wfmmodsettings.cpp | 7 + plugins/channeltx/modwfm/wfmmodsettings.h | 2 + .../remotesource/remotesourcegui.cpp | 23 +- .../channeltx/remotesource/remotesourcegui.h | 7 + .../channeltx/remotesource/remotesourcegui.ui | 6 +- .../remotesource/remotesourcesettings.cpp | 7 + .../remotesource/remotesourcesettings.h | 2 + plugins/channeltx/udpsource/udpsourcegui.cpp | 36 +- plugins/channeltx/udpsource/udpsourcegui.h | 7 + plugins/channeltx/udpsource/udpsourcegui.ui | 6 +- .../channeltx/udpsource/udpsourcesettings.cpp | 7 + .../channeltx/udpsource/udpsourcesettings.h | 2 + plugins/feature/ais/aisgui.cpp | 1 - plugins/feature/ais/aissettings.cpp | 8 +- .../feature/demodanalyzer/demodanalyzergui.ui | 24 +- plugins/feature/map/map.cpp | 2 +- plugins/feature/pertester/pertestergui.ui | 2 +- plugins/samplemimo/testmi/testmigui.cpp | 2 + sdrbase/device/deviceapi.cpp | 6 + sdrbase/device/deviceapi.h | 1 + sdrbase/device/deviceset.h | 1 + sdrbase/dsp/dspengine.cpp | 100 ++-- sdrbase/dsp/dspengine.h | 30 +- sdrbase/maincore.cpp | 19 + sdrbase/maincore.h | 1 + sdrbase/settings/configuration.h | 1 + sdrgui/channel/channelgui.cpp | 302 ++++++++++++ sdrgui/channel/channelgui.h | 99 +++- sdrgui/device/devicegui.cpp | 19 + sdrgui/device/devicegui.h | 7 + sdrgui/device/deviceuiset.cpp | 142 +++++- sdrgui/device/deviceuiset.h | 8 +- sdrgui/feature/featuregui.cpp | 23 + sdrgui/feature/featuregui.h | 3 + sdrgui/feature/featureuiset.cpp | 10 + sdrgui/mainwindow.cpp | 435 ++++++++++++++---- sdrgui/mainwindow.h | 12 +- 251 files changed, 3455 insertions(+), 678 deletions(-) diff --git a/plugins/channelmimo/beamsteeringcwmod/beamsteeringcwmodgui.cpp b/plugins/channelmimo/beamsteeringcwmod/beamsteeringcwmodgui.cpp index bcf3afc0b..9e73950b7 100644 --- a/plugins/channelmimo/beamsteeringcwmod/beamsteeringcwmodgui.cpp +++ b/plugins/channelmimo/beamsteeringcwmod/beamsteeringcwmodgui.cpp @@ -96,11 +96,12 @@ BeamSteeringCWModGUI::BeamSteeringCWModGUI(PluginAPI* pluginAPI, DeviceUISet *de m_centerFrequency(435000000), m_tickCount(0) { - ui->setupUi(this); + ui->setupUi(getRollupContents()); + getRollupContents()->arrangeRollups(); setAttribute(Qt::WA_DeleteOnClose, true); setStreamIndicator("M"); - connect(this, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + connect(getRollupContents(), SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &))); m_bsCWSource = (BeamSteeringCWMod*) mimoChannel; @@ -127,6 +128,7 @@ BeamSteeringCWModGUI::BeamSteeringCWModGUI(PluginAPI* pluginAPI, DeviceUISet *de connect(getInputMessageQueue(), SIGNAL(messageEnqueued()), this, SLOT(handleSourceMessages())); displaySettings(); + makeUIConnections(); displayRateAndShift(); applySettings(true); } @@ -164,12 +166,13 @@ void BeamSteeringCWModGUI::displaySettings() setTitleColor(m_settings.m_rgbColor); setWindowTitle(m_channelMarker.getTitle()); + setTitle(m_channelMarker.getTitle()); blockApplySettings(true); ui->interpolationFactor->setCurrentIndex(m_settings.m_log2Interp); applyInterpolation(); ui->steeringDegreesText->setText(tr("%1").arg(m_settings.m_steerDegrees)); - restoreState(m_rollupState); + getRollupContents()->restoreState(m_rollupState); blockApplySettings(false); } @@ -212,7 +215,7 @@ void BeamSteeringCWModGUI::onWidgetRolled(QWidget* widget, bool rollDown) (void) widget; (void) rollDown; - saveState(m_rollupState); + getRollupContents()->saveState(m_rollupState); applySettings(); } @@ -239,6 +242,7 @@ void BeamSteeringCWModGUI::onMenuDialogCalled(const QPoint &p) m_settings.m_reverseAPIChannelIndex = dialog.getReverseAPIChannelIndex(); setWindowTitle(m_settings.m_title); + setTitle(m_channelMarker.getTitle()); setTitleColor(m_settings.m_rgbColor); applySettings(); @@ -303,3 +307,11 @@ void BeamSteeringCWModGUI::tick() m_tickCount = 0; } } + +void BeamSteeringCWModGUI::makeUIConnections() +{ + QObject::connect(ui->channelOutput, QOverload::of(&QComboBox::currentIndexChanged), this, &BeamSteeringCWModGUI::on_channelOutput_currentIndexChanged); + QObject::connect(ui->interpolationFactor, QOverload::of(&QComboBox::currentIndexChanged), this, &BeamSteeringCWModGUI::on_interpolationFactor_currentIndexChanged); + QObject::connect(ui->position, &QSlider::valueChanged, this, &BeamSteeringCWModGUI::on_position_valueChanged); + QObject::connect(ui->steeringDegrees, &QSlider::valueChanged, this, &BeamSteeringCWModGUI::on_steeringDegrees_valueChanged); +} diff --git a/plugins/channelmimo/beamsteeringcwmod/beamsteeringcwmodgui.h b/plugins/channelmimo/beamsteeringcwmod/beamsteeringcwmodgui.h index 8e5eaef05..5b55b94fc 100644 --- a/plugins/channelmimo/beamsteeringcwmod/beamsteeringcwmodgui.h +++ b/plugins/channelmimo/beamsteeringcwmod/beamsteeringcwmodgui.h @@ -48,6 +48,12 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } + virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; }; + virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; }; + virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; }; + virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; }; + virtual QString getTitle() const { return m_settings.m_title; }; + virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; private: Ui::BeamSteeringCWModGUI* ui; @@ -74,6 +80,7 @@ private: void displaySettings(); void displayRateAndShift(); bool handleMessage(const Message& message); + void makeUIConnections(); void leaveEvent(QEvent*); void enterEvent(QEvent*); diff --git a/plugins/channelmimo/beamsteeringcwmod/beamsteeringcwmodgui.ui b/plugins/channelmimo/beamsteeringcwmod/beamsteeringcwmodgui.ui index ec970d077..4872d6abb 100644 --- a/plugins/channelmimo/beamsteeringcwmod/beamsteeringcwmodgui.ui +++ b/plugins/channelmimo/beamsteeringcwmod/beamsteeringcwmodgui.ui @@ -1,13 +1,13 @@ BeamSteeringCWModGUI - + 0 0 - 320 - 102 + 360 + 100 @@ -18,16 +18,10 @@ - 320 + 360 100 - - - 320 - 16777215 - - Liberation Sans @@ -40,12 +34,24 @@ - 10 - 10 - 301 + 0 + 0 + 358 91 + + + 0 + 0 + + + + + 358 + 0 + + Settings @@ -326,9 +332,9 @@ - RollupWidget + RollupContents QWidget -
gui/rollupwidget.h
+
gui/rollupcontents.h
1
diff --git a/plugins/channelmimo/interferometer/interferometergui.cpp b/plugins/channelmimo/interferometer/interferometergui.cpp index 4930a24e9..19e01348d 100644 --- a/plugins/channelmimo/interferometer/interferometergui.cpp +++ b/plugins/channelmimo/interferometer/interferometergui.cpp @@ -106,11 +106,12 @@ InterferometerGUI::InterferometerGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUI m_centerFrequency(435000000), m_tickCount(0) { - ui->setupUi(this); + ui->setupUi(getRollupContents()); + getRollupContents()->arrangeRollups(); setAttribute(Qt::WA_DeleteOnClose, true); setStreamIndicator("M"); - connect(this, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + connect(getRollupContents(), SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &))); m_interferometer = (Interferometer*) channelMIMO; @@ -157,6 +158,7 @@ InterferometerGUI::InterferometerGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUI connect(getInputMessageQueue(), SIGNAL(messageEnqueued()), this, SLOT(handleSourceMessages())); displaySettings(); + makeUIConnections(); displayRateAndShift(); applySettings(true); } @@ -196,13 +198,14 @@ void InterferometerGUI::displaySettings() setTitleColor(m_settings.m_rgbColor); setWindowTitle(m_channelMarker.getTitle()); + setTitle(m_channelMarker.getTitle()); blockApplySettings(true); ui->decimationFactor->setCurrentIndex(m_settings.m_log2Decim); applyDecimation(); ui->phaseCorrection->setValue(m_settings.m_phase); ui->phaseCorrectionText->setText(tr("%1").arg(m_settings.m_phase)); - restoreState(m_rollupState); + getRollupContents()->restoreState(m_rollupState); blockApplySettings(false); } @@ -247,7 +250,7 @@ void InterferometerGUI::onWidgetRolled(QWidget* widget, bool rollDown) (void) widget; (void) rollDown; - saveState(m_rollupState); + getRollupContents()->saveState(m_rollupState); applySettings(); } @@ -274,6 +277,7 @@ void InterferometerGUI::onMenuDialogCalled(const QPoint &p) m_settings.m_reverseAPIChannelIndex = dialog.getReverseAPIChannelIndex(); setWindowTitle(m_settings.m_title); + setTitle(m_channelMarker.getTitle()); setTitleColor(m_settings.m_rgbColor); applySettings(); @@ -338,3 +342,11 @@ void InterferometerGUI::tick() m_tickCount = 0; } } + +void InterferometerGUI::makeUIConnections() +{ + QObject::connect(ui->decimationFactor, QOverload::of(&QComboBox::currentIndexChanged), this, &InterferometerGUI::on_decimationFactor_currentIndexChanged); + QObject::connect(ui->position, &QSlider::valueChanged, this, &InterferometerGUI::on_position_valueChanged); + QObject::connect(ui->phaseCorrection, &QSlider::valueChanged, this, &InterferometerGUI::on_phaseCorrection_valueChanged); + QObject::connect(ui->correlationType, QOverload::of(&QComboBox::currentIndexChanged), this, &InterferometerGUI::on_correlationType_currentIndexChanged); +} diff --git a/plugins/channelmimo/interferometer/interferometergui.h b/plugins/channelmimo/interferometer/interferometergui.h index 97a4a2b3f..d3dd6a03d 100644 --- a/plugins/channelmimo/interferometer/interferometergui.h +++ b/plugins/channelmimo/interferometer/interferometergui.h @@ -47,6 +47,12 @@ public: virtual QByteArray serialize() const; virtual bool deserialize(const QByteArray& data); virtual MessageQueue* getInputMessageQueue(); + virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; }; + virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; }; + virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; }; + virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; }; + virtual QString getTitle() const { return m_settings.m_title; }; + virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; private: Ui::InterferometerGUI* ui; @@ -76,6 +82,7 @@ private: void displaySettings(); void displayRateAndShift(); bool handleMessage(const Message& message); + void makeUIConnections(); void leaveEvent(QEvent*); void enterEvent(QEvent*); diff --git a/plugins/channelmimo/interferometer/interferometergui.ui b/plugins/channelmimo/interferometer/interferometergui.ui index 51d12c1cb..dc806c9c7 100644 --- a/plugins/channelmimo/interferometer/interferometergui.ui +++ b/plugins/channelmimo/interferometer/interferometergui.ui @@ -1,15 +1,21 @@ InterferometerGUI - + 0 0 - 739 - 778 + 720 + 770 + + + 0 + 0 + + 720 @@ -30,10 +36,16 @@ 0 10 - 631 + 718 81 + + + 718 + 0 + + Settings @@ -352,9 +364,15 @@ 284
+ + + 0 + 0 + + - 716 + 718 0 @@ -379,6 +397,12 @@
+ + + 0 + 0 + + 200 @@ -407,9 +431,15 @@ 334 + + + 0 + 0 + + - 716 + 718 0 @@ -434,6 +464,12 @@
+ + + 0 + 0 + + 200 @@ -456,9 +492,9 @@ - RollupWidget + RollupContents QWidget -
gui/rollupwidget.h
+
gui/rollupcontents.h
1
diff --git a/plugins/channelrx/chanalyzer/chanalyzergui.cpp b/plugins/channelrx/chanalyzer/chanalyzergui.cpp index f3d8fc032..5ddf44f33 100644 --- a/plugins/channelrx/chanalyzer/chanalyzergui.cpp +++ b/plugins/channelrx/chanalyzer/chanalyzergui.cpp @@ -76,6 +76,7 @@ void ChannelAnalyzerGUI::displaySettings() setTitleColor(m_settings.m_rgbColor); setWindowTitle(m_channelMarker.getTitle()); + setTitle(m_channelMarker.getTitle()); blockApplySettings(true); @@ -97,7 +98,7 @@ void ChannelAnalyzerGUI::displaySettings() QString rolloffStr = QString::number(m_settings.m_rrcRolloff/100.0, 'f', 2); ui->rrcRolloffText->setText(rolloffStr); - restoreState(m_rollupState); + getRollupContents()->restoreState(m_rollupState); blockApplySettings(false); } @@ -181,7 +182,7 @@ void ChannelAnalyzerGUI::setPLLVisibility() else ui->pllPskOrder->setCurrentIndex(i); ui->pllPskOrder->blockSignals(false); - arrangeRollups(); + getRollupContents()->arrangeRollups(); } void ChannelAnalyzerGUI::setSpectrumDisplay() @@ -279,7 +280,7 @@ void ChannelAnalyzerGUI::channelMarkerChangedByCursor() void ChannelAnalyzerGUI::channelMarkerHighlightedByCursor() { - setHighlighted(m_channelMarker.getHighlighted()); + getRollupContents()->setHighlighted(m_channelMarker.getHighlighted()); } void ChannelAnalyzerGUI::tick() @@ -450,7 +451,7 @@ void ChannelAnalyzerGUI::onWidgetRolled(QWidget* widget, bool rollDown) (void) widget; (void) rollDown; - saveState(m_rollupState); + getRollupContents()->saveState(m_rollupState); applySettings(); } @@ -477,6 +478,7 @@ void ChannelAnalyzerGUI::onMenuDialogCalled(const QPoint& p) m_settings.m_reverseAPIChannelIndex = dialog.getReverseAPIChannelIndex(); setWindowTitle(m_settings.m_title); + setTitle(m_channelMarker.getTitle()); setTitleColor(m_settings.m_rgbColor); applySettings(); @@ -494,11 +496,12 @@ ChannelAnalyzerGUI::ChannelAnalyzerGUI(PluginAPI* pluginAPI, DeviceUISet *device m_doApplySettings(true), m_basebandSampleRate(48000) { - ui->setupUi(this); + ui->setupUi(getRollupContents()); + getRollupContents()->arrangeRollups(); m_helpURL = "plugins/channelrx/chanalyzer/readme.md"; setAttribute(Qt::WA_DeleteOnClose, true); - connect(this, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + connect(getRollupContents(), SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &))); m_channelAnalyzer = (ChannelAnalyzer*) rxChannel; @@ -555,6 +558,7 @@ ChannelAnalyzerGUI::ChannelAnalyzerGUI(PluginAPI* pluginAPI, DeviceUISet *device connect(getInputMessageQueue(), SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages())); displaySettings(); + makeUIConnections(); applySettings(true); } @@ -681,3 +685,21 @@ void ChannelAnalyzerGUI::enterEvent(QEvent*) m_channelMarker.setHighlighted(true); } +void ChannelAnalyzerGUI::makeUIConnections() +{ + QObject::connect(ui->deltaFrequency, &ValueDialZ::changed, this, &ChannelAnalyzerGUI::on_deltaFrequency_changed); + QObject::connect(ui->rationalDownSamplerRate, &ValueDial::changed, this, &ChannelAnalyzerGUI::on_rationalDownSamplerRate_changed); + QObject::connect(ui->pll, &QToolButton::toggled, this, &ChannelAnalyzerGUI::on_pll_toggled); + QObject::connect(ui->pllType, QOverload::of(&QComboBox::currentIndexChanged), this, &ChannelAnalyzerGUI::on_pllType_currentIndexChanged); + QObject::connect(ui->pllPskOrder, QOverload::of(&QComboBox::currentIndexChanged), this, &ChannelAnalyzerGUI::on_pllPskOrder_currentIndexChanged); + QObject::connect(ui->pllBandwidth, &QDial::valueChanged, this, &ChannelAnalyzerGUI::on_pllBandwidth_valueChanged); + QObject::connect(ui->pllDampingFactor, &QDial::valueChanged, this, &ChannelAnalyzerGUI::on_pllDampingFactor_valueChanged); + QObject::connect(ui->pllLoopGain, &QDial::valueChanged, this, &ChannelAnalyzerGUI::on_pllLoopGain_valueChanged); + QObject::connect(ui->useRationalDownsampler, &ButtonSwitch::toggled, this, &ChannelAnalyzerGUI::on_useRationalDownsampler_toggled); + QObject::connect(ui->signalSelect, QOverload::of(&QComboBox::currentIndexChanged), this, &ChannelAnalyzerGUI::on_signalSelect_currentIndexChanged); + QObject::connect(ui->rrcFilter, &ButtonSwitch::toggled, this, &ChannelAnalyzerGUI::on_rrcFilter_toggled); + QObject::connect(ui->rrcRolloff, &QDial::valueChanged, this, &ChannelAnalyzerGUI::on_rrcRolloff_valueChanged); + QObject::connect(ui->BW, &QSlider::valueChanged, this, &ChannelAnalyzerGUI::on_BW_valueChanged); + QObject::connect(ui->lowCut, &QSlider::valueChanged, this, &ChannelAnalyzerGUI::on_lowCut_valueChanged); + QObject::connect(ui->ssb, &QCheckBox::toggled, this, &ChannelAnalyzerGUI::on_ssb_toggled); +} diff --git a/plugins/channelrx/chanalyzer/chanalyzergui.h b/plugins/channelrx/chanalyzer/chanalyzergui.h index 1e847a84c..69bee761e 100644 --- a/plugins/channelrx/chanalyzer/chanalyzergui.h +++ b/plugins/channelrx/chanalyzer/chanalyzergui.h @@ -49,6 +49,12 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } + virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; }; + virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; }; + virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; }; + virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; }; + virtual QString getTitle() const { return m_settings.m_title; }; + virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; public slots: void channelMarkerChangedByCursor(); @@ -84,6 +90,7 @@ private: void setPLLVisibility(); void setSpectrumDisplay(); bool handleMessage(const Message& message); + void makeUIConnections(); void leaveEvent(QEvent*); void enterEvent(QEvent*); diff --git a/plugins/channelrx/chanalyzer/chanalyzergui.ui b/plugins/channelrx/chanalyzer/chanalyzergui.ui index a8c641eb8..13fe10ab3 100644 --- a/plugins/channelrx/chanalyzer/chanalyzergui.ui +++ b/plugins/channelrx/chanalyzer/chanalyzergui.ui @@ -1,12 +1,12 @@ ChannelAnalyzerGUI - + 0 0 - 739 + 720 778 @@ -36,10 +36,16 @@ 0 0 - 524 + 718 101
+ + + 718 + 0 + + Settings @@ -861,9 +867,15 @@ 284
+ + + 0 + 0 + + - 716 + 718 0 @@ -888,6 +900,12 @@
+ + + 0 + 0 + + 200 @@ -916,9 +934,15 @@ 334 + + + 0 + 0 + + - 716 + 718 0 @@ -943,6 +967,12 @@
+ + + 0 + 0 + + 200 @@ -965,9 +995,20 @@ - RollupWidget + ValueDial QWidget -
gui/rollupwidget.h
+
gui/valuedial.h
+ 1 +
+ + ButtonSwitch + QToolButton +
gui/buttonswitch.h
+
+ + RollupContents + QWidget +
gui/rollupcontents.h
1
@@ -976,23 +1017,6 @@
gui/valuedialz.h
1
- - GLScope - QWidget -
gui/glscope.h
- 1 -
- - GLScopeGUI - QWidget -
gui/glscopegui.h
- 1 -
- - ButtonSwitch - QToolButton -
gui/buttonswitch.h
-
GLSpectrum QWidget @@ -1006,9 +1030,15 @@ 1 - ValueDial + GLScope QWidget -
gui/valuedial.h
+
gui/glscope.h
+ 1 +
+ + GLScopeGUI + QWidget +
gui/glscopegui.h
1
diff --git a/plugins/channelrx/chanalyzer/chanalyzersettings.cpp b/plugins/channelrx/chanalyzer/chanalyzersettings.cpp index d357c936a..3f3a21774 100644 --- a/plugins/channelrx/chanalyzer/chanalyzersettings.cpp +++ b/plugins/channelrx/chanalyzer/chanalyzersettings.cpp @@ -59,6 +59,7 @@ void ChannelAnalyzerSettings::resetToDefaults() m_reverseAPIPort = 8888; m_reverseAPIDeviceIndex = 0; m_reverseAPIChannelIndex = 0; + m_workspaceIndex = 0; } QByteArray ChannelAnalyzerSettings::serialize() const @@ -105,6 +106,8 @@ QByteArray ChannelAnalyzerSettings::serialize() const s.writeU32(26, m_reverseAPIDeviceIndex); s.writeU32(27, m_reverseAPIChannelIndex); s.writeS32(28, m_streamIndex); + s.writeS32(29, m_workspaceIndex); + s.writeString(30, m_geometryBytes); return s.final(); } @@ -179,6 +182,8 @@ bool ChannelAnalyzerSettings::deserialize(const QByteArray& data) d.readU32(27, &utmp, 0); m_reverseAPIChannelIndex = utmp > 99 ? 99 : utmp; d.readS32(28, &m_streamIndex, 0); + d.readS32(29, &m_workspaceIndex, 0); + d.readBlob(30, &m_geometryBytes); return true; } diff --git a/plugins/channelrx/chanalyzer/chanalyzersettings.h b/plugins/channelrx/chanalyzer/chanalyzersettings.h index f0878e489..2469007ca 100644 --- a/plugins/channelrx/chanalyzer/chanalyzersettings.h +++ b/plugins/channelrx/chanalyzer/chanalyzersettings.h @@ -60,6 +60,8 @@ struct ChannelAnalyzerSettings uint16_t m_reverseAPIPort; uint16_t m_reverseAPIDeviceIndex; uint16_t m_reverseAPIChannelIndex; + int m_workspaceIndex; + QByteArray m_geometryBytes; ChannelAnalyzerSettings(); void resetToDefaults(); diff --git a/plugins/channelrx/demodadsb/adsbdemodgui.cpp b/plugins/channelrx/demodadsb/adsbdemodgui.cpp index 37e8084de..303019598 100644 --- a/plugins/channelrx/demodadsb/adsbdemodgui.cpp +++ b/plugins/channelrx/demodadsb/adsbdemodgui.cpp @@ -2143,7 +2143,7 @@ if (DSPSignalNotification::match(message)) } else { ui->warning->setText(""); } - arrangeRollups(); + getRollupContents()->arrangeRollups(); return true; } else if (ADSBDemodReport::MsgReportADSB::match(message)) @@ -2204,7 +2204,7 @@ void ADSBDemodGUI::channelMarkerChangedByCursor() void ADSBDemodGUI::channelMarkerHighlightedByCursor() { - setHighlighted(m_channelMarker.getHighlighted()); + getRollupContents()->setHighlighted(m_channelMarker.getHighlighted()); } void ADSBDemodGUI::on_deltaFrequency_changed(qint64 value) @@ -2759,7 +2759,7 @@ void ADSBDemodGUI::onWidgetRolled(QWidget* widget, bool rollDown) (void) widget; (void) rollDown; - saveState(m_rollupState); + getRollupContents()->saveState(m_rollupState); applySettings(); } @@ -2786,6 +2786,7 @@ void ADSBDemodGUI::onMenuDialogCalled(const QPoint &p) m_settings.m_reverseAPIChannelIndex = dialog.getReverseAPIChannelIndex(); setWindowTitle(m_settings.m_title); + setTitle(m_channelMarker.getTitle()); setTitleColor(m_settings.m_rgbColor); applySettings(); @@ -3684,7 +3685,8 @@ ADSBDemodGUI::ADSBDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Baseb m_highlightAircraft(nullptr), m_progressDialog(nullptr) { - ui->setupUi(this); + ui->setupUi(getRollupContents()); + getRollupContents()->arrangeRollups(); m_helpURL = "plugins/channelrx/demodadsb/readme.md"; m_osmPort = 0; // Pick a free port @@ -3698,7 +3700,7 @@ ADSBDemodGUI::ADSBDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Baseb setAttribute(Qt::WA_DeleteOnClose, true); - connect(this, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + connect(getRollupContents(), SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &))); connect(&m_dlm, &HttpDownloadManager::downloadComplete, this, &ADSBDemodGUI::downloadFinished); @@ -3829,6 +3831,7 @@ ADSBDemodGUI::ADSBDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Baseb updateDeviceSetList(); displaySettings(); + makeUIConnections(); applySettings(true); connect(&m_importTimer, &QTimer::timeout, this, &ADSBDemodGUI::import); @@ -3903,6 +3906,7 @@ void ADSBDemodGUI::displaySettings() setTitleColor(m_settings.m_rgbColor); setWindowTitle(m_channelMarker.getTitle()); + setTitle(m_channelMarker.getTitle()); blockApplySettings(true); @@ -3989,7 +3993,7 @@ void ADSBDemodGUI::displaySettings() applyMapSettings(); applyImportSettings(); - restoreState(m_rollupState); + getRollupContents()->restoreState(m_rollupState); blockApplySettings(false); } @@ -4659,3 +4663,31 @@ void ADSBDemodGUI::preferenceChanged(int elementType) } } } + +void ADSBDemodGUI::makeUIConnections() +{ + QObject::connect(ui->deltaFrequency, &ValueDialZ::changed, this, &ADSBDemodGUI::on_deltaFrequency_changed); + QObject::connect(ui->rfBW, &QSlider::valueChanged, this, &ADSBDemodGUI::on_rfBW_valueChanged); + QObject::connect(ui->threshold, &QDial::valueChanged, this, &ADSBDemodGUI::on_threshold_valueChanged); + QObject::connect(ui->phaseSteps, &QDial::valueChanged, this, &ADSBDemodGUI::on_phaseSteps_valueChanged); + QObject::connect(ui->tapsPerPhase, &QDial::valueChanged, this, &ADSBDemodGUI::on_tapsPerPhase_valueChanged); + QObject::connect(ui->adsbData, &QTableWidget::cellClicked, this, &ADSBDemodGUI::on_adsbData_cellClicked); + QObject::connect(ui->adsbData, &QTableWidget::cellDoubleClicked, this, &ADSBDemodGUI::on_adsbData_cellDoubleClicked); + QObject::connect(ui->spb, QOverload::of(&QComboBox::currentIndexChanged), this, &ADSBDemodGUI::on_spb_currentIndexChanged); + QObject::connect(ui->correlateFullPreamble, &ButtonSwitch::clicked, this, &ADSBDemodGUI::on_correlateFullPreamble_clicked); + QObject::connect(ui->demodModeS, &ButtonSwitch::clicked, this, &ADSBDemodGUI::on_demodModeS_clicked); + QObject::connect(ui->feed, &ButtonSwitch::clicked, this, &ADSBDemodGUI::on_feed_clicked); + QObject::connect(ui->notifications, &QToolButton::clicked, this, &ADSBDemodGUI::on_notifications_clicked); + QObject::connect(ui->flightInfo, &QToolButton::clicked, this, &ADSBDemodGUI::on_flightInfo_clicked); + QObject::connect(ui->findOnMapFeature, &QToolButton::clicked, this, &ADSBDemodGUI::on_findOnMapFeature_clicked); + QObject::connect(ui->getOSNDB, &QToolButton::clicked, this, &ADSBDemodGUI::on_getOSNDB_clicked); + QObject::connect(ui->getAirportDB, &QToolButton::clicked, this, &ADSBDemodGUI::on_getAirportDB_clicked); + QObject::connect(ui->getAirspacesDB, &QToolButton::clicked, this, &ADSBDemodGUI::on_getAirspacesDB_clicked); + QObject::connect(ui->flightPaths, &ButtonSwitch::clicked, this, &ADSBDemodGUI::on_flightPaths_clicked); + QObject::connect(ui->allFlightPaths, &ButtonSwitch::clicked, this, &ADSBDemodGUI::on_allFlightPaths_clicked); + QObject::connect(ui->device, QOverload::of(&QComboBox::currentIndexChanged), this, &ADSBDemodGUI::on_device_currentIndexChanged); + QObject::connect(ui->displaySettings, &QToolButton::clicked, this, &ADSBDemodGUI::on_displaySettings_clicked); + QObject::connect(ui->logEnable, &ButtonSwitch::clicked, this, &ADSBDemodGUI::on_logEnable_clicked); + QObject::connect(ui->logFilename, &QToolButton::clicked, this, &ADSBDemodGUI::on_logFilename_clicked); + QObject::connect(ui->logOpen, &QToolButton::clicked, this, &ADSBDemodGUI::on_logOpen_clicked); +} diff --git a/plugins/channelrx/demodadsb/adsbdemodgui.h b/plugins/channelrx/demodadsb/adsbdemodgui.h index 8f0aa60f5..22157eecd 100644 --- a/plugins/channelrx/demodadsb/adsbdemodgui.h +++ b/plugins/channelrx/demodadsb/adsbdemodgui.h @@ -752,6 +752,13 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } + virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; }; + virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; }; + virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; }; + virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; }; + virtual QString getTitle() const { return m_settings.m_title; }; + virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; + void highlightAircraft(Aircraft *aircraft); void targetAircraft(Aircraft *aircraft); void target(const QString& name, float az, float el, float range); @@ -838,6 +845,8 @@ private: void displaySettings(); void displayStreamIndex(); bool handleMessage(const Message& message); + void makeUIConnections(); + void updatePosition(Aircraft *aircraft); bool updateLocalPosition(Aircraft *aircraft, double latitude, double longitude, bool surfacePosition); void sendToMap(Aircraft *aircraft, QList *animations); diff --git a/plugins/channelrx/demodadsb/adsbdemodgui.ui b/plugins/channelrx/demodadsb/adsbdemodgui.ui index 3239b18df..a0ea5dc91 100644 --- a/plugins/channelrx/demodadsb/adsbdemodgui.ui +++ b/plugins/channelrx/demodadsb/adsbdemodgui.ui @@ -1,12 +1,12 @@ ADSBDemodGUI - + 0 0 - 604 + 600 1046 @@ -18,7 +18,7 @@
- 350 + 600 0 @@ -35,13 +35,13 @@ 0 0 - 600 + 598 141
- 600 + 598 0 @@ -758,7 +758,7 @@ 0 140 - 600 + 598 600
@@ -770,7 +770,7 @@
- 600 + 598 0 @@ -1269,17 +1269,17 @@ QWidget
QtQuickWidgets/QQuickWidget
- - RollupWidget - QWidget -
gui/rollupwidget.h
- 1 -
ButtonSwitch QToolButton
gui/buttonswitch.h
+ + RollupContents + QWidget +
gui/rollupcontents.h
+ 1 +
ValueDialZ QWidget diff --git a/plugins/channelrx/demodadsb/adsbdemodsettings.cpp b/plugins/channelrx/demodadsb/adsbdemodsettings.cpp index 5790fa66e..dcb35d644 100644 --- a/plugins/channelrx/demodadsb/adsbdemodsettings.cpp +++ b/plugins/channelrx/demodadsb/adsbdemodsettings.cpp @@ -96,6 +96,7 @@ void ADSBDemodSettings::resetToDefaults() m_displayPhotos = true; m_verboseModelMatching = false; m_airfieldElevation = 0; + m_workspaceIndex = 0; } QByteArray ADSBDemodSettings::serialize() const @@ -172,6 +173,8 @@ QByteArray ADSBDemodSettings::serialize() const s.writeString(56, m_importMaxLatitude); s.writeString(57, m_importMinLongitude); s.writeString(58, m_importMaxLongitude); + s.writeS32(59, m_workspaceIndex); + s.writeBlob(60, m_geometryBytes); for (int i = 0; i < ADSBDEMOD_COLUMNS; i++) { s.writeS32(100 + i, m_columnIndexes[i]); @@ -298,6 +301,8 @@ bool ADSBDemodSettings::deserialize(const QByteArray& data) d.readString(56, &m_importMaxLatitude, ""); d.readString(57, &m_importMinLongitude, ""); d.readString(58, &m_importMaxLongitude, ""); + d.readS32(59, &m_workspaceIndex, 0); + d.readBlob(60, &m_geometryBytes); for (int i = 0; i < ADSBDEMOD_COLUMNS; i++) { d.readS32(100 + i, &m_columnIndexes[i], i); diff --git a/plugins/channelrx/demodadsb/adsbdemodsettings.h b/plugins/channelrx/demodadsb/adsbdemodsettings.h index 88ae38cc4..5e14da134 100644 --- a/plugins/channelrx/demodadsb/adsbdemodsettings.h +++ b/plugins/channelrx/demodadsb/adsbdemodsettings.h @@ -116,6 +116,8 @@ struct ADSBDemodSettings uint16_t m_reverseAPIPort; uint16_t m_reverseAPIDeviceIndex; uint16_t m_reverseAPIChannelIndex; + int m_workspaceIndex; + QByteArray m_geometryBytes; int m_columnIndexes[ADSBDEMOD_COLUMNS];//!< How the columns are ordered in the table int m_columnSizes[ADSBDEMOD_COLUMNS]; //!< Size of the coumns in the table diff --git a/plugins/channelrx/demodais/aisdemodgui.cpp b/plugins/channelrx/demodais/aisdemodgui.cpp index 0f167d629..ab01d438a 100644 --- a/plugins/channelrx/demodais/aisdemodgui.cpp +++ b/plugins/channelrx/demodais/aisdemodgui.cpp @@ -243,7 +243,7 @@ void AISDemodGUI::channelMarkerChangedByCursor() void AISDemodGUI::channelMarkerHighlightedByCursor() { - setHighlighted(m_channelMarker.getHighlighted()); + getRollupContents()->setHighlighted(m_channelMarker.getHighlighted()); } void AISDemodGUI::on_deltaFrequency_changed(qint64 value) @@ -371,7 +371,7 @@ void AISDemodGUI::onWidgetRolled(QWidget* widget, bool rollDown) } } - saveState(m_rollupState); + getRollupContents()->saveState(m_rollupState); applySettings(); } @@ -397,6 +397,7 @@ void AISDemodGUI::onMenuDialogCalled(const QPoint &p) m_settings.m_reverseAPIChannelIndex = dialog.getReverseAPIChannelIndex(); setWindowTitle(m_settings.m_title); + setTitle(m_channelMarker.getTitle()); setTitleColor(m_settings.m_rgbColor); applySettings(); @@ -428,11 +429,12 @@ AISDemodGUI::AISDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Baseban m_doApplySettings(true), m_tickCount(0) { - ui->setupUi(this); + ui->setupUi(getRollupContents()); + getRollupContents()->arrangeRollups(); m_helpURL = "plugins/channelrx/demodais/readme.md"; setAttribute(Qt::WA_DeleteOnClose, true); - connect(this, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + connect(getRollupContents(), SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &))); m_aisDemod = reinterpret_cast(rxChannel); @@ -518,6 +520,7 @@ AISDemodGUI::AISDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Baseban ui->scopeContainer->setVisible(false); displaySettings(); + makeUIConnections(); applySettings(true); } @@ -569,6 +572,7 @@ void AISDemodGUI::displaySettings() setTitleColor(m_settings.m_rgbColor); setWindowTitle(m_channelMarker.getTitle()); + setTitle(m_channelMarker.getTitle()); blockApplySettings(true); @@ -612,7 +616,7 @@ void AISDemodGUI::displaySettings() filter(); - restoreState(m_rollupState); + getRollupContents()->restoreState(m_rollupState); blockApplySettings(false); } @@ -756,3 +760,21 @@ void AISDemodGUI::on_logOpen_clicked() } } } + +void AISDemodGUI::makeUIConnections() +{ + QObject::connect(ui->deltaFrequency, &ValueDialZ::changed, this, &AISDemodGUI::on_deltaFrequency_changed); + QObject::connect(ui->rfBW, &QSlider::valueChanged, this, &AISDemodGUI::on_rfBW_valueChanged); + QObject::connect(ui->fmDev, &QSlider::valueChanged, this, &AISDemodGUI::on_fmDev_valueChanged); + QObject::connect(ui->threshold, &QDial::valueChanged, this, &AISDemodGUI::on_threshold_valueChanged); + QObject::connect(ui->filterMMSI, &QLineEdit::editingFinished, this, &AISDemodGUI::on_filterMMSI_editingFinished); + QObject::connect(ui->clearTable, &QPushButton::clicked, this, &AISDemodGUI::on_clearTable_clicked); + QObject::connect(ui->udpEnabled, &QCheckBox::clicked, this, &AISDemodGUI::on_udpEnabled_clicked); + QObject::connect(ui->udpAddress, &QLineEdit::editingFinished, this, &AISDemodGUI::on_udpAddress_editingFinished); + QObject::connect(ui->udpPort, &QLineEdit::editingFinished, this, &AISDemodGUI::on_udpPort_editingFinished); + QObject::connect(ui->udpFormat, QOverload::of(&QComboBox::currentIndexChanged), this, &AISDemodGUI::on_udpFormat_currentIndexChanged); + QObject::connect(ui->messages, &QTableWidget::cellDoubleClicked, this, &AISDemodGUI::on_messages_cellDoubleClicked); + QObject::connect(ui->logEnable, &ButtonSwitch::clicked, this, &AISDemodGUI::on_logEnable_clicked); + QObject::connect(ui->logFilename, &QToolButton::clicked, this, &AISDemodGUI::on_logFilename_clicked); + QObject::connect(ui->logOpen, &QToolButton::clicked, this, &AISDemodGUI::on_logOpen_clicked); +} diff --git a/plugins/channelrx/demodais/aisdemodgui.h b/plugins/channelrx/demodais/aisdemodgui.h index ac6a329ee..1cdf5d3ba 100644 --- a/plugins/channelrx/demodais/aisdemodgui.h +++ b/plugins/channelrx/demodais/aisdemodgui.h @@ -58,6 +58,12 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } + virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; }; + virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; }; + virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; }; + virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; }; + virtual QString getTitle() const { return m_settings.m_title; }; + virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; public slots: void channelMarkerChangedByCursor(); @@ -89,6 +95,7 @@ private: void displayStreamIndex(); void messageReceived(const QByteArray& message, const QDateTime& dateTime); bool handleMessage(const Message& message); + void makeUIConnections(); void leaveEvent(QEvent*); void enterEvent(QEvent*); diff --git a/plugins/channelrx/demodais/aisdemodgui.ui b/plugins/channelrx/demodais/aisdemodgui.ui index 8645357fe..85ea93b7b 100644 --- a/plugins/channelrx/demodais/aisdemodgui.ui +++ b/plugins/channelrx/demodais/aisdemodgui.ui @@ -1,13 +1,13 @@ AISDemodGUI - + 0 0 - 404 - 764 + 388 + 446 @@ -912,9 +912,14 @@ - RollupWidget + ButtonSwitch + QToolButton +
gui/buttonswitch.h
+
+ + RollupContents QWidget -
gui/rollupwidget.h
+
gui/rollupcontents.h
1
@@ -929,11 +934,6 @@
gui/levelmeter.h
1
- - ButtonSwitch - QToolButton -
gui/buttonswitch.h
-
GLScope QWidget diff --git a/plugins/channelrx/demodais/aisdemodsettings.cpp b/plugins/channelrx/demodais/aisdemodsettings.cpp index 9adad474a..5597c447f 100644 --- a/plugins/channelrx/demodais/aisdemodsettings.cpp +++ b/plugins/channelrx/demodais/aisdemodsettings.cpp @@ -55,6 +55,7 @@ void AISDemodSettings::resetToDefaults() m_reverseAPIPort = 8888; m_reverseAPIDeviceIndex = 0; m_reverseAPIChannelIndex = 0; + m_workspaceIndex = 0; for (int i = 0; i < AISDEMOD_MESSAGE_COLUMNS; i++) { @@ -100,6 +101,9 @@ QByteArray AISDemodSettings::serialize() const s.writeBlob(25, m_rollupState->serialize()); } + s.writeS32(26, m_workspaceIndex); + s.writeBlob(27, m_geometryBytes); + for (int i = 0; i < AISDEMOD_MESSAGE_COLUMNS; i++) s.writeS32(100 + i, m_messageColumnIndexes[i]); for (int i = 0; i < AISDEMOD_MESSAGE_COLUMNS; i++) @@ -183,6 +187,9 @@ bool AISDemodSettings::deserialize(const QByteArray& data) m_rollupState->deserialize(bytetmp); } + d.readS32(26, &m_workspaceIndex, 0); + d.readBlob(27, &m_geometryBytes); + for (int i = 0; i < AISDEMOD_MESSAGE_COLUMNS; i++) { d.readS32(100 + i, &m_messageColumnIndexes[i], i); } diff --git a/plugins/channelrx/demodais/aisdemodsettings.h b/plugins/channelrx/demodais/aisdemodsettings.h index 02fd0396b..bd06a99e5 100644 --- a/plugins/channelrx/demodais/aisdemodsettings.h +++ b/plugins/channelrx/demodais/aisdemodsettings.h @@ -61,6 +61,8 @@ struct AISDemodSettings uint16_t m_reverseAPIChannelIndex; Serializable *m_scopeGUI; Serializable *m_rollupState; + int m_workspaceIndex; + QByteArray m_geometryBytes; int m_messageColumnIndexes[AISDEMOD_MESSAGE_COLUMNS];//!< How the columns are ordered in the table int m_messageColumnSizes[AISDEMOD_MESSAGE_COLUMNS]; //!< Size of the columns in the table diff --git a/plugins/channelrx/demodam/amdemodgui.cpp b/plugins/channelrx/demodam/amdemodgui.cpp index c37dfb739..0b7f8c723 100644 --- a/plugins/channelrx/demodam/amdemodgui.cpp +++ b/plugins/channelrx/demodam/amdemodgui.cpp @@ -111,7 +111,7 @@ void AMDemodGUI::channelMarkerChangedByCursor() void AMDemodGUI::channelMarkerHighlightedByCursor() { - setHighlighted(m_channelMarker.getHighlighted()); + getRollupContents()->setHighlighted(m_channelMarker.getHighlighted()); } void AMDemodGUI::on_deltaFrequency_changed(qint64 value) @@ -182,7 +182,7 @@ void AMDemodGUI::onWidgetRolled(QWidget* widget, bool rollDown) m_nfmDemod->setSpectrum(m_threadedSampleSink->getMessageQueue(), rollDown); */ - saveState(m_rollupState); + getRollupContents()->saveState(m_rollupState); applySettings(); } @@ -209,6 +209,7 @@ void AMDemodGUI::onMenuDialogCalled(const QPoint &p) m_settings.m_reverseAPIChannelIndex = dialog.getReverseAPIChannelIndex(); setWindowTitle(m_settings.m_title); + setTitle(m_channelMarker.getTitle()); setTitleColor(m_settings.m_rgbColor); applySettings(); @@ -243,10 +244,11 @@ AMDemodGUI::AMDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandS m_samUSB(true), m_tickCount(0) { - ui->setupUi(this); + ui->setupUi(getRollupContents()); + getRollupContents()->arrangeRollups(); m_helpURL = "plugins/channelrx/demodam/readme.md"; setAttribute(Qt::WA_DeleteOnClose, true); - connect(this, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + connect(getRollupContents(), SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &))); m_amDemod = reinterpret_cast(rxChannel); @@ -290,6 +292,7 @@ AMDemodGUI::AMDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandS m_iconDSBLSB.addPixmap(QPixmap("://lsb.png"), QIcon::Normal, QIcon::On); displaySettings(); + makeUIConnections(); applySettings(true); } @@ -323,6 +326,7 @@ void AMDemodGUI::displaySettings() setTitleColor(m_settings.m_rgbColor); setWindowTitle(m_channelMarker.getTitle()); + setTitle(m_channelMarker.getTitle()); blockApplySettings(true); @@ -373,7 +377,7 @@ void AMDemodGUI::displaySettings() displayStreamIndex(); - restoreState(m_rollupState); + getRollupContents()->restoreState(m_rollupState); blockApplySettings(false); } @@ -478,3 +482,14 @@ void AMDemodGUI::tick() m_tickCount++; } +void AMDemodGUI::makeUIConnections() +{ + QObject::connect(ui->deltaFrequency, &ValueDialZ::changed, this, &AMDemodGUI::on_deltaFrequency_changed); + QObject::connect(ui->pll, &QToolButton::toggled, this, &AMDemodGUI::on_pll_toggled); + QObject::connect(ui->ssb, &QToolButton::toggled, this, &AMDemodGUI::on_ssb_toggled); + QObject::connect(ui->bandpassEnable, &ButtonSwitch::toggled, this, &AMDemodGUI::on_bandpassEnable_toggled); + QObject::connect(ui->rfBW, &QSlider::valueChanged, this, &AMDemodGUI::on_rfBW_valueChanged); + QObject::connect(ui->volume, &QSlider::valueChanged, this, &AMDemodGUI::on_volume_valueChanged); + QObject::connect(ui->squelch, &QSlider::valueChanged, this, &AMDemodGUI::on_squelch_valueChanged); + QObject::connect(ui->audioMute, &QToolButton::toggled, this, &AMDemodGUI::on_audioMute_toggled); +} diff --git a/plugins/channelrx/demodam/amdemodgui.h b/plugins/channelrx/demodam/amdemodgui.h index 7320227bd..adfc37404 100644 --- a/plugins/channelrx/demodam/amdemodgui.h +++ b/plugins/channelrx/demodam/amdemodgui.h @@ -31,6 +31,12 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } + virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; }; + virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; }; + virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; }; + virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; }; + virtual QString getTitle() const { return m_settings.m_title; }; + virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; public slots: void channelMarkerChangedByCursor(); @@ -63,6 +69,7 @@ private: void displaySettings(); void displayStreamIndex(); bool handleMessage(const Message& message); + void makeUIConnections(); void leaveEvent(QEvent*); void enterEvent(QEvent*); diff --git a/plugins/channelrx/demodam/amdemodgui.ui b/plugins/channelrx/demodam/amdemodgui.ui index 2bf40f6b2..d4f9a2cf7 100644 --- a/plugins/channelrx/demodam/amdemodgui.ui +++ b/plugins/channelrx/demodam/amdemodgui.ui @@ -1,24 +1,24 @@ AMDemodGUI - + 0 0 - 396 - 170 + 360 + 153 - + 0 0 - 352 + 360 0 @@ -39,13 +39,13 @@ 0 0 - 390 - 131 + 358 + 151
- 350 + 358 0 @@ -428,9 +428,20 @@ - RollupWidget + ButtonSwitch + QToolButton +
gui/buttonswitch.h
+
+ + RollupContents QWidget -
gui/rollupwidget.h
+
gui/rollupcontents.h
+ 1 +
+ + ValueDialZ + QWidget +
gui/valuedialz.h
1
@@ -439,17 +450,6 @@
gui/levelmeter.h
1
- - ButtonSwitch - QToolButton -
gui/buttonswitch.h
-
- - ValueDialZ - QWidget -
gui/valuedialz.h
- 1 -
diff --git a/plugins/channelrx/demodam/amdemodsettings.cpp b/plugins/channelrx/demodam/amdemodsettings.cpp index db6a1950e..cf5fcd8f3 100644 --- a/plugins/channelrx/demodam/amdemodsettings.cpp +++ b/plugins/channelrx/demodam/amdemodsettings.cpp @@ -49,6 +49,7 @@ void AMDemodSettings::resetToDefaults() m_reverseAPIPort = 8888; m_reverseAPIDeviceIndex = 0; m_reverseAPIChannelIndex = 0; + m_workspaceIndex = 0; } QByteArray AMDemodSettings::serialize() const @@ -80,6 +81,9 @@ QByteArray AMDemodSettings::serialize() const s.writeBlob(19, m_rollupState->serialize()); } + s.writeS32(20, m_workspaceIndex); + s.writeBlob(21, m_geometryBytes); + return s.final(); } @@ -143,6 +147,9 @@ bool AMDemodSettings::deserialize(const QByteArray& data) m_rollupState->deserialize(bytetmp); } + d.readS32(20, &m_workspaceIndex, 0); + d.readBlob(21, &m_geometryBytes); + return true; } else diff --git a/plugins/channelrx/demodam/amdemodsettings.h b/plugins/channelrx/demodam/amdemodsettings.h index d200eeb06..810d03b30 100644 --- a/plugins/channelrx/demodam/amdemodsettings.h +++ b/plugins/channelrx/demodam/amdemodsettings.h @@ -50,6 +50,8 @@ struct AMDemodSettings uint16_t m_reverseAPIDeviceIndex; uint16_t m_reverseAPIChannelIndex; Serializable *m_rollupState; + int m_workspaceIndex; + QByteArray m_geometryBytes; AMDemodSettings(); void resetToDefaults(); diff --git a/plugins/channelrx/demodapt/aptdemodgui.cpp b/plugins/channelrx/demodapt/aptdemodgui.cpp index 7a23c088a..1aa2d942f 100644 --- a/plugins/channelrx/demodapt/aptdemodgui.cpp +++ b/plugins/channelrx/demodapt/aptdemodgui.cpp @@ -303,7 +303,7 @@ void APTDemodGUI::channelMarkerChangedByCursor() void APTDemodGUI::channelMarkerHighlightedByCursor() { - setHighlighted(m_channelMarker.getHighlighted()); + getRollupContents()->setHighlighted(m_channelMarker.getHighlighted()); } void APTDemodGUI::on_deltaFrequency_changed(qint64 value) @@ -527,7 +527,7 @@ void APTDemodGUI::on_zoomAll_clicked(bool checked) } } -void APTDemodGUI::on_image_zoomed() +void APTDemodGUI::onImageZoomed() { ui->zoomAll->setChecked(false); } @@ -537,7 +537,7 @@ void APTDemodGUI::onWidgetRolled(QWidget* widget, bool rollDown) (void) widget; (void) rollDown; - saveState(m_rollupState); + getRollupContents()->saveState(m_rollupState); applySettings(); } @@ -563,6 +563,7 @@ void APTDemodGUI::onMenuDialogCalled(const QPoint &p) m_settings.m_reverseAPIChannelIndex = dialog.getReverseAPIChannelIndex(); setWindowTitle(m_settings.m_title); + setTitle(m_channelMarker.getTitle()); setTitleColor(m_settings.m_rgbColor); applySettings(); @@ -596,11 +597,12 @@ APTDemodGUI::APTDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Baseban m_scene(nullptr), m_pixmapItem(nullptr) { - ui->setupUi(this); + ui->setupUi(getRollupContents()); + getRollupContents()->arrangeRollups(); m_helpURL = "plugins/channelrx/demodapt/readme.md"; setAttribute(Qt::WA_DeleteOnClose, true); - connect(this, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + connect(getRollupContents(), SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &))); m_aptDemod = reinterpret_cast(rxChannel); @@ -633,7 +635,7 @@ APTDemodGUI::APTDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Baseban connect(getInputMessageQueue(), SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages())); m_zoom = new GraphicsViewZoom(ui->image); // Deleted automatically when view is deleted - connect(m_zoom, SIGNAL(zoomed()), this, SLOT(on_image_zoomed())); + connect(m_zoom, SIGNAL(zoomed()), this, SLOT(onImageZoomed())); // Create slightly transparent white background so labels can be seen m_tempScale = new TempScale(); @@ -658,6 +660,7 @@ APTDemodGUI::APTDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Baseban m_scene->installEventFilter(this); displaySettings(); + makeUIConnections(); applySettings(true); } @@ -740,6 +743,7 @@ void APTDemodGUI::displaySettings() setTitleColor(m_settings.m_rgbColor); setWindowTitle(m_channelMarker.getTitle()); + setTitle(m_channelMarker.getTitle()); blockApplySettings(true); @@ -774,7 +778,7 @@ void APTDemodGUI::displaySettings() displayLabels(); displayStreamIndex(); - restoreState(m_rollupState); + getRollupContents()->restoreState(m_rollupState); blockApplySettings(false); } @@ -882,3 +886,29 @@ void APTDemodGUI::deleteImageFromMap(const QString &name) messageQueue->push(msg); } } + +void APTDemodGUI::makeUIConnections() +{ + QObject::connect(ui->deltaFrequency, &ValueDialZ::changed, this, &APTDemodGUI::on_deltaFrequency_changed); + QObject::connect(ui->rfBW, &QSlider::valueChanged, this, &APTDemodGUI::on_rfBW_valueChanged); + QObject::connect(ui->fmDev, &QSlider::valueChanged, this, &APTDemodGUI::on_fmDev_valueChanged); + QObject::connect(ui->channels, QOverload::of(&QComboBox::currentIndexChanged), this, &APTDemodGUI::on_channels_currentIndexChanged); + QObject::connect(ui->transparencyThreshold, &QDial::valueChanged, this, &APTDemodGUI::on_transparencyThreshold_valueChanged); + QObject::connect(ui->transparencyThreshold, &QDial::sliderReleased, this, &APTDemodGUI::on_transparencyThreshold_sliderReleased); + QObject::connect(ui->opacityThreshold, &QDial::valueChanged, this, &APTDemodGUI::on_opacityThreshold_valueChanged); + QObject::connect(ui->opacityThreshold, &QDial::sliderReleased, this, &APTDemodGUI::on_opacityThreshold_sliderReleased); + QObject::connect(ui->deleteImageFromMap, &QToolButton::clicked, this, &APTDemodGUI::on_deleteImageFromMap_clicked); + QObject::connect(ui->cropNoise, &ButtonSwitch::clicked, this, &APTDemodGUI::on_cropNoise_clicked); + QObject::connect(ui->denoise, &ButtonSwitch::clicked, this, &APTDemodGUI::on_denoise_clicked); + QObject::connect(ui->linear, &ButtonSwitch::clicked, this, &APTDemodGUI::on_linear_clicked); + QObject::connect(ui->histogram, &ButtonSwitch::clicked, this, &APTDemodGUI::on_histogram_clicked); + QObject::connect(ui->precipitation, &ButtonSwitch::clicked, this, &APTDemodGUI::on_precipitation_clicked); + QObject::connect(ui->flip, &ButtonSwitch::clicked, this, &APTDemodGUI::on_flip_clicked); + QObject::connect(ui->startStop, &ButtonSwitch::clicked, this, &APTDemodGUI::on_startStop_clicked); + QObject::connect(ui->showSettings, &QToolButton::clicked, this, &APTDemodGUI::on_showSettings_clicked); + QObject::connect(ui->resetDecoder, &QToolButton::clicked, this, &APTDemodGUI::on_resetDecoder_clicked); + QObject::connect(ui->saveImage, &QToolButton::clicked, this, &APTDemodGUI::on_saveImage_clicked); + QObject::connect(ui->zoomIn, &QToolButton::clicked, this, &APTDemodGUI::on_zoomIn_clicked); + QObject::connect(ui->zoomOut, &QToolButton::clicked, this, &APTDemodGUI::on_zoomOut_clicked); + QObject::connect(ui->zoomAll, &ButtonSwitch::clicked, this, &APTDemodGUI::on_zoomAll_clicked); +} diff --git a/plugins/channelrx/demodapt/aptdemodgui.h b/plugins/channelrx/demodapt/aptdemodgui.h index 368fe4b26..9b28c1641 100644 --- a/plugins/channelrx/demodapt/aptdemodgui.h +++ b/plugins/channelrx/demodapt/aptdemodgui.h @@ -75,6 +75,12 @@ public: bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } virtual bool eventFilter(QObject *watched, QEvent *event) override; + virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; }; + virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; }; + virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; }; + virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; }; + virtual QString getTitle() const { return m_settings.m_title; }; + virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; public slots: void channelMarkerChangedByCursor(); @@ -115,6 +121,8 @@ private: void displayLabels(); void displayStreamIndex(); bool handleMessage(const Message& message); + void makeUIConnections(); + void deleteImageFromMap(const QString &name); void resetDecoder(); @@ -144,7 +152,7 @@ private slots: void on_zoomIn_clicked(); void on_zoomOut_clicked(); void on_zoomAll_clicked(bool checked=false); - void on_image_zoomed(); + void onImageZoomed(); void onWidgetRolled(QWidget* widget, bool rollDown); void onMenuDialogCalled(const QPoint& p); void handleInputMessages(); diff --git a/plugins/channelrx/demodapt/aptdemodgui.ui b/plugins/channelrx/demodapt/aptdemodgui.ui index e9147bba6..da837aa0a 100644 --- a/plugins/channelrx/demodapt/aptdemodgui.ui +++ b/plugins/channelrx/demodapt/aptdemodgui.ui @@ -1,7 +1,7 @@ APTDemodGUI - + 0 @@ -803,9 +803,9 @@ - RollupWidget + RollupContents QWidget -
gui/rollupwidget.h
+
gui/rollupcontents.h
1
diff --git a/plugins/channelrx/demodapt/aptdemodsettings.cpp b/plugins/channelrx/demodapt/aptdemodsettings.cpp index 36beb2c3b..115905f9b 100644 --- a/plugins/channelrx/demodapt/aptdemodsettings.cpp +++ b/plugins/channelrx/demodapt/aptdemodsettings.cpp @@ -69,6 +69,7 @@ void APTDemodSettings::resetToDefaults() m_reverseAPIPort = 8888; m_reverseAPIDeviceIndex = 0; m_reverseAPIChannelIndex = 0; + m_workspaceIndex = 0; } QByteArray APTDemodSettings::serialize() const @@ -120,6 +121,8 @@ QByteArray APTDemodSettings::serialize() const s.writeS32(36, m_verticalPixelsPerDegree); s.writeFloat(37, m_satTimeOffset); s.writeFloat(38, m_satYaw); + s.writeS32(39, m_workspaceIndex); + s.writeBlob(40, m_geometryBytes); return s.final(); } @@ -201,6 +204,8 @@ bool APTDemodSettings::deserialize(const QByteArray& data) d.readS32(36, &m_verticalPixelsPerDegree, 20); d.readFloat(37, &m_satTimeOffset, 0.0f); d.readFloat(38, &m_satYaw, 0.0f); + d.readS32(39, &m_workspaceIndex, 0); + d.readBlob(40, &m_geometryBytes); return true; } diff --git a/plugins/channelrx/demodapt/aptdemodsettings.h b/plugins/channelrx/demodapt/aptdemodsettings.h index 7ed001928..08a53c1af 100644 --- a/plugins/channelrx/demodapt/aptdemodsettings.h +++ b/plugins/channelrx/demodapt/aptdemodsettings.h @@ -66,6 +66,8 @@ struct APTDemodSettings uint16_t m_reverseAPIDeviceIndex; uint16_t m_reverseAPIChannelIndex; Serializable *m_rollupState; + int m_workspaceIndex; + QByteArray m_geometryBytes; // The following are really working state, rather than settings QString m_tle; // Satelite two-line elements, from satellite tracker diff --git a/plugins/channelrx/demodatv/atvdemodgui.cpp b/plugins/channelrx/demodatv/atvdemodgui.cpp index f804b197d..8c7b72861 100644 --- a/plugins/channelrx/demodatv/atvdemodgui.cpp +++ b/plugins/channelrx/demodatv/atvdemodgui.cpp @@ -86,6 +86,7 @@ void ATVDemodGUI::displaySettings() setTitleColor(m_settings.m_rgbColor); setWindowTitle(m_channelMarker.getTitle()); + setTitle(m_channelMarker.getTitle()); displayStreamIndex(); m_doApplySettings = false; @@ -119,7 +120,7 @@ void ATVDemodGUI::displaySettings() ui->amScaleOffsetText->setText(QString("%1").arg(m_settings.m_amOffsetFactor)); applySampleRate(); - restoreState(m_rollupState); + getRollupContents()->restoreState(m_rollupState); m_doApplySettings = true; } @@ -187,7 +188,7 @@ void ATVDemodGUI::channelMarkerChangedByCursor() void ATVDemodGUI::channelMarkerHighlightedByCursor() { - setHighlighted(m_channelMarker.getHighlighted()); + getRollupContents()->setHighlighted(m_channelMarker.getHighlighted()); } void ATVDemodGUI::handleSourceMessages() @@ -208,7 +209,7 @@ void ATVDemodGUI::onWidgetRolled(QWidget* widget, bool rollDown) (void) widget; (void) rollDown; - saveState(m_rollupState); + getRollupContents()->saveState(m_rollupState); applySettings(); } @@ -222,10 +223,11 @@ ATVDemodGUI::ATVDemodGUI(PluginAPI* objPluginAPI, DeviceUISet *deviceUISet, Base m_intTickCount(0), m_basebandSampleRate(48000) { - ui->setupUi(this); + ui->setupUi(getRollupContents()); + getRollupContents()->arrangeRollups(); m_helpURL = "plugins/channelrx/demodatv/readme.md"; setAttribute(Qt::WA_DeleteOnClose, true); - connect(this, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + connect(getRollupContents(), SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); m_atvDemod = (ATVDemod*) rxChannel; m_atvDemod->setMessageQueueToGUI(getInputMessageQueue()); @@ -278,6 +280,8 @@ ATVDemodGUI::ATVDemodGUI(PluginAPI* objPluginAPI, DeviceUISet *deviceUISet, Base QChar delta = QChar(0x94, 0x03); ui->fmDeviationLabel->setText(delta); + + makeUIConnections(); } ATVDemodGUI::~ATVDemodGUI() @@ -564,3 +568,27 @@ void ATVDemodGUI::topTimeUpdate() else ui->topTimeText->setText(tr("%1 s").arg(nominalTopTime * 1.0, 0, 'f', 2)); } + +void ATVDemodGUI::makeUIConnections() +{ + QObject::connect(ui->synchLevel, &QSlider::valueChanged, this, &ATVDemodGUI::on_synchLevel_valueChanged); + QObject::connect(ui->blackLevel, &QSlider::valueChanged, this, &ATVDemodGUI::on_blackLevel_valueChanged); + QObject::connect(ui->hSync, &QCheckBox::clicked, this, &ATVDemodGUI::on_hSync_clicked); + QObject::connect(ui->vSync, &QCheckBox::clicked, this, &ATVDemodGUI::on_vSync_clicked); + QObject::connect(ui->invertVideo, &QCheckBox::clicked, this, &ATVDemodGUI::on_invertVideo_clicked); + QObject::connect(ui->halfImage, &QCheckBox::clicked, this, &ATVDemodGUI::on_halfImage_clicked); + QObject::connect(ui->modulation, QOverload::of(&QComboBox::currentIndexChanged), this, &ATVDemodGUI::on_modulation_currentIndexChanged); + QObject::connect(ui->nbLines, QOverload::of(&QComboBox::currentIndexChanged), this, &ATVDemodGUI::on_nbLines_currentIndexChanged); + QObject::connect(ui->fps, QOverload::of(&QComboBox::currentIndexChanged), this, &ATVDemodGUI::on_fps_currentIndexChanged); + QObject::connect(ui->standard, QOverload::of(&QComboBox::currentIndexChanged), this, &ATVDemodGUI::on_standard_currentIndexChanged); + QObject::connect(ui->reset, &QPushButton::clicked, this, &ATVDemodGUI::on_reset_clicked); + QObject::connect(ui->rfBW, &QSlider::valueChanged, this, &ATVDemodGUI::on_rfBW_valueChanged); + QObject::connect(ui->rfOppBW, &QSlider::valueChanged, this, &ATVDemodGUI::on_rfOppBW_valueChanged); + QObject::connect(ui->rfFiltering, &ButtonSwitch::toggled, this, &ATVDemodGUI::on_rfFiltering_toggled); + QObject::connect(ui->deltaFrequency, &ValueDialZ::changed, this, &ATVDemodGUI::on_deltaFrequency_changed); + QObject::connect(ui->bfo, &QDial::valueChanged, this, &ATVDemodGUI::on_bfo_valueChanged); + QObject::connect(ui->fmDeviation, &QDial::valueChanged, this, &ATVDemodGUI::on_fmDeviation_valueChanged); + QObject::connect(ui->amScaleFactor, &QDial::valueChanged, this, &ATVDemodGUI::on_amScaleFactor_valueChanged); + QObject::connect(ui->amScaleOffset, &QDial::valueChanged, this, &ATVDemodGUI::on_amScaleOffset_valueChanged); + QObject::connect(ui->screenTabWidget, &QTabWidget::currentChanged, this, &ATVDemodGUI::on_screenTabWidget_currentChanged); +} diff --git a/plugins/channelrx/demodatv/atvdemodgui.h b/plugins/channelrx/demodatv/atvdemodgui.h index cadfa42a0..c4ce6a57c 100644 --- a/plugins/channelrx/demodatv/atvdemodgui.h +++ b/plugins/channelrx/demodatv/atvdemodgui.h @@ -50,6 +50,12 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& arrData); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } + virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; }; + virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; }; + virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; }; + virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; }; + virtual QString getTitle() const { return m_settings.m_title; }; + virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; public slots: void channelMarkerChangedByCursor(); @@ -88,6 +94,7 @@ private: void lineTimeUpdate(); void topTimeUpdate(); bool handleMessage(const Message& objMessage); + void makeUIConnections(); void leaveEvent(QEvent*); void enterEvent(QEvent*); diff --git a/plugins/channelrx/demodatv/atvdemodgui.ui b/plugins/channelrx/demodatv/atvdemodgui.ui index 8590ee373..d0678c3dd 100644 --- a/plugins/channelrx/demodatv/atvdemodgui.ui +++ b/plugins/channelrx/demodatv/atvdemodgui.ui @@ -1,7 +1,7 @@ ATVDemodGUI - + 0 @@ -1155,9 +1155,9 @@ - RollupWidget + RollupContents QWidget -
gui/rollupwidget.h
+
gui/rollupcontents.h
1
diff --git a/plugins/channelrx/demodatv/atvdemodsettings.cpp b/plugins/channelrx/demodatv/atvdemodsettings.cpp index 82b689946..be423fb78 100644 --- a/plugins/channelrx/demodatv/atvdemodsettings.cpp +++ b/plugins/channelrx/demodatv/atvdemodsettings.cpp @@ -54,6 +54,12 @@ void ATVDemodSettings::resetToDefaults() m_udpAddress = "127.0.0.1"; m_udpPort = 9999; m_streamIndex = 0; + m_useReverseAPI = false; + m_reverseAPIAddress = "127.0.0.1"; + m_reverseAPIPort = 8888; + m_reverseAPIDeviceIndex = 0; + m_reverseAPIChannelIndex = 0; + m_workspaceIndex = 0; } QByteArray ATVDemodSettings::serialize() const @@ -91,6 +97,14 @@ QByteArray ATVDemodSettings::serialize() const s.writeBlob(25, m_rollupState->serialize()); } + s.writeBool(26, m_useReverseAPI); + s.writeString(27, m_reverseAPIAddress); + s.writeU32(28, m_reverseAPIPort); + s.writeU32(29, m_reverseAPIDeviceIndex); + s.writeU32(30, m_reverseAPIChannelIndex); + s.writeS32(31, m_workspaceIndex); + s.writeBlob(32, m_geometryBytes); + return s.final(); } @@ -108,6 +122,7 @@ bool ATVDemodSettings::deserialize(const QByteArray& arrData) { QByteArray bytetmp; int tmp; + uint32_t utmp; d.readS64(1, &m_inputFrequencyOffset, 0); // TODO: rgb color @@ -152,6 +167,23 @@ bool ATVDemodSettings::deserialize(const QByteArray& arrData) m_rollupState->deserialize(bytetmp); } + d.readBool(26, &m_useReverseAPI, false); + d.readString(27, &m_reverseAPIAddress, "127.0.0.1"); + d.readU32(28, &utmp, 0); + + if ((utmp > 1023) && (utmp < 65535)) { + m_reverseAPIPort = utmp; + } else { + m_reverseAPIPort = 8888; + } + + d.readU32(29, &utmp, 0); + m_reverseAPIDeviceIndex = utmp > 99 ? 99 : utmp; + d.readU32(30, &utmp, 0); + m_reverseAPIChannelIndex = utmp > 99 ? 99 : utmp; + d.readS32(31, &m_workspaceIndex, 0); + d.readBlob(32, &m_geometryBytes); + return true; } else diff --git a/plugins/channelrx/demodatv/atvdemodsettings.h b/plugins/channelrx/demodatv/atvdemodsettings.h index 64f8f5275..5a8eba3f3 100644 --- a/plugins/channelrx/demodatv/atvdemodsettings.h +++ b/plugins/channelrx/demodatv/atvdemodsettings.h @@ -74,7 +74,14 @@ struct ATVDemodSettings uint16_t m_udpPort; Serializable *m_channelMarker; int m_streamIndex; + bool m_useReverseAPI; + QString m_reverseAPIAddress; + uint16_t m_reverseAPIPort; + uint16_t m_reverseAPIDeviceIndex; + uint16_t m_reverseAPIChannelIndex; Serializable *m_rollupState; + int m_workspaceIndex; + QByteArray m_geometryBytes; ATVDemodSettings(); void resetToDefaults(); diff --git a/plugins/channelrx/demodbfm/bfmdemodgui.cpp b/plugins/channelrx/demodbfm/bfmdemodgui.cpp index 3405b11f2..936ed0146 100644 --- a/plugins/channelrx/demodbfm/bfmdemodgui.cpp +++ b/plugins/channelrx/demodbfm/bfmdemodgui.cpp @@ -138,7 +138,7 @@ void BFMDemodGUI::channelMarkerChangedByCursor() void BFMDemodGUI::channelMarkerHighlightedByCursor() { - setHighlighted(m_channelMarker.getHighlighted()); + getRollupContents()->setHighlighted(m_channelMarker.getHighlighted()); } void BFMDemodGUI::on_deltaFrequency_changed(qint64 value) @@ -304,7 +304,7 @@ void BFMDemodGUI::onWidgetRolled(QWidget* widget, bool rollDown) (void) widget; (void) rollDown; - saveState(m_rollupState); + getRollupContents()->saveState(m_rollupState); applySettings(); } @@ -332,6 +332,7 @@ void BFMDemodGUI::onMenuDialogCalled(const QPoint &p) m_settings.m_reverseAPIChannelIndex = dialog.getReverseAPIChannelIndex(); setWindowTitle(m_settings.m_title); + setTitle(m_channelMarker.getTitle()); setTitleColor(m_settings.m_rgbColor); applySettings(); @@ -364,7 +365,8 @@ BFMDemodGUI::BFMDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Baseban m_radiotext_AB_flag(false), m_rate(625000) { - ui->setupUi(this); + ui->setupUi(getRollupContents()); + getRollupContents()->arrangeRollups(); m_helpURL = "plugins/channelrx/demodbfm/readme.md"; ui->deltaFrequencyLabel->setText(QString("%1f").arg(QChar(0x94, 0x03))); ui->deltaFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold)); @@ -375,7 +377,7 @@ BFMDemodGUI::BFMDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Baseban connect(audioMuteRightClickEnabler, SIGNAL(rightClick(const QPoint &)), this, SLOT(audioSelect())); setAttribute(Qt::WA_DeleteOnClose, true); - connect(this, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + connect(getRollupContents(), SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &))); connect(getInputMessageQueue(), SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages())); @@ -421,6 +423,7 @@ BFMDemodGUI::BFMDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Baseban rdsUpdateFixedFields(); rdsUpdate(true); displaySettings(); + makeUIConnections(); applySettings(true); } @@ -454,6 +457,7 @@ void BFMDemodGUI::displaySettings() setTitleColor(m_settings.m_rgbColor); setWindowTitle(m_channelMarker.getTitle()); + setTitle(m_channelMarker.getTitle()); blockApplySettings(true); @@ -478,7 +482,7 @@ void BFMDemodGUI::displaySettings() displayStreamIndex(); - restoreState(m_rollupState); + getRollupContents()->restoreState(m_rollupState); blockApplySettings(false); } @@ -829,3 +833,17 @@ void BFMDemodGUI::changeFrequency(qint64 f) qint64 df = m_channelMarker.getCenterFrequency(); qDebug() << "BFMDemodGUI::changeFrequency: " << f - df; } + +void BFMDemodGUI::makeUIConnections() +{ + QObject::connect(ui->deltaFrequency, &ValueDialZ::changed, this, &BFMDemodGUI::on_deltaFrequency_changed); + QObject::connect(ui->rfBW, &QSlider::valueChanged, this, &BFMDemodGUI::on_rfBW_valueChanged); + QObject::connect(ui->afBW, &QSlider::valueChanged, this, &BFMDemodGUI::on_afBW_valueChanged); + QObject::connect(ui->volume, &QSlider::valueChanged, this, &BFMDemodGUI::on_volume_valueChanged); + QObject::connect(ui->squelch, &QSlider::valueChanged, this, &BFMDemodGUI::on_squelch_valueChanged); + QObject::connect(ui->audioStereo, &QToolButton::toggled, this, &BFMDemodGUI::on_audioStereo_toggled); + QObject::connect(ui->lsbStereo, &ButtonSwitch::toggled, this, &BFMDemodGUI::on_lsbStereo_toggled); + QObject::connect(ui->showPilot, &ButtonSwitch::clicked, this, &BFMDemodGUI::on_showPilot_clicked); + QObject::connect(ui->rds, &ButtonSwitch::clicked, this, &BFMDemodGUI::on_rds_clicked); + QObject::connect(ui->clearData, &QPushButton::clicked, this, &BFMDemodGUI::on_clearData_clicked); +} diff --git a/plugins/channelrx/demodbfm/bfmdemodgui.h b/plugins/channelrx/demodbfm/bfmdemodgui.h index b5b561726..b3d8f77c5 100644 --- a/plugins/channelrx/demodbfm/bfmdemodgui.h +++ b/plugins/channelrx/demodbfm/bfmdemodgui.h @@ -48,6 +48,12 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } + virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; }; + virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; }; + virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; }; + virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; }; + virtual QString getTitle() const { return m_settings.m_title; }; + virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; public slots: void channelMarkerChangedByCursor(); @@ -81,6 +87,7 @@ private: void rdsUpdate(bool force); void rdsUpdateFixedFields(); bool handleMessage(const Message& message); + void makeUIConnections(); void leaveEvent(QEvent*); void enterEvent(QEvent*); diff --git a/plugins/channelrx/demodbfm/bfmdemodgui.ui b/plugins/channelrx/demodbfm/bfmdemodgui.ui index aff87e627..c4ade4633 100644 --- a/plugins/channelrx/demodbfm/bfmdemodgui.ui +++ b/plugins/channelrx/demodbfm/bfmdemodgui.ui @@ -1,7 +1,7 @@ BFMDemodGUI - + 0 @@ -1884,9 +1884,9 @@ - RollupWidget + RollupContents QWidget -
gui/rollupwidget.h
+
gui/rollupcontents.h
1
diff --git a/plugins/channelrx/demodbfm/bfmdemodsettings.cpp b/plugins/channelrx/demodbfm/bfmdemodsettings.cpp index ee2446627..c09fcabfa 100644 --- a/plugins/channelrx/demodbfm/bfmdemodsettings.cpp +++ b/plugins/channelrx/demodbfm/bfmdemodsettings.cpp @@ -56,6 +56,7 @@ void BFMDemodSettings::resetToDefaults() m_reverseAPIPort = 8888; m_reverseAPIDeviceIndex = 0; m_reverseAPIChannelIndex = 0; + m_workspaceIndex = 0; } QByteArray BFMDemodSettings::serialize() const @@ -93,6 +94,8 @@ QByteArray BFMDemodSettings::serialize() const } s.writeBool(21, m_rdsActive); + s.writeS32(22, m_workspaceIndex); + s.writeBlob(23, m_geometryBytes); return s.final(); } @@ -167,6 +170,8 @@ bool BFMDemodSettings::deserialize(const QByteArray& data) } d.readBool(21, &m_rdsActive, false); + d.readS32(22, &m_workspaceIndex, 0); + d.readBlob(23, &m_geometryBytes); return true; } diff --git a/plugins/channelrx/demodbfm/bfmdemodsettings.h b/plugins/channelrx/demodbfm/bfmdemodsettings.h index b2cdf7003..866f1589e 100644 --- a/plugins/channelrx/demodbfm/bfmdemodsettings.h +++ b/plugins/channelrx/demodbfm/bfmdemodsettings.h @@ -42,6 +42,8 @@ struct BFMDemodSettings uint16_t m_reverseAPIPort; uint16_t m_reverseAPIDeviceIndex; uint16_t m_reverseAPIChannelIndex; + int m_workspaceIndex; + QByteArray m_geometryBytes; Serializable *m_channelMarker; Serializable *m_spectrumGUI; diff --git a/plugins/channelrx/demodchirpchat/chirpchatdemodgui.cpp b/plugins/channelrx/demodchirpchat/chirpchatdemodgui.cpp index 6d937002a..211c1c2f4 100644 --- a/plugins/channelrx/demodchirpchat/chirpchatdemodgui.cpp +++ b/plugins/channelrx/demodchirpchat/chirpchatdemodgui.cpp @@ -156,7 +156,7 @@ void ChirpChatDemodGUI::on_deltaFrequency_changed(qint64 value) void ChirpChatDemodGUI::channelMarkerHighlightedByCursor() { - setHighlighted(m_channelMarker.getHighlighted()); + getRollupContents()->setHighlighted(m_channelMarker.getHighlighted()); } void ChirpChatDemodGUI::on_BW_valueChanged(int value) @@ -323,7 +323,7 @@ void ChirpChatDemodGUI::onWidgetRolled(QWidget* widget, bool rollDown) (void) widget; (void) rollDown; - saveState(m_rollupState); + getRollupContents()->saveState(m_rollupState); applySettings(); } @@ -350,6 +350,7 @@ void ChirpChatDemodGUI::onMenuDialogCalled(const QPoint &p) m_settings.m_reverseAPIChannelIndex = dialog.getReverseAPIChannelIndex(); setWindowTitle(m_settings.m_title); + setTitle(m_channelMarker.getTitle()); setTitleColor(m_settings.m_rgbColor); applySettings(); @@ -382,10 +383,11 @@ ChirpChatDemodGUI::ChirpChatDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUI m_doApplySettings(true), m_tickCount(0) { - ui->setupUi(this); + ui->setupUi(getRollupContents()); + getRollupContents()->arrangeRollups(); m_helpURL = "plugins/channelrx/demodchirpchat/readme.md"; setAttribute(Qt::WA_DeleteOnClose, true); - connect(this, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + connect(getRollupContents(), SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &))); m_chirpChatDemod = (ChirpChatDemod*) rxChannel; @@ -424,6 +426,7 @@ ChirpChatDemodGUI::ChirpChatDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUI setBandwidths(); displaySettings(); + makeUIConnections(); resetLoRaStatus(); applySettings(true); } @@ -459,6 +462,7 @@ void ChirpChatDemodGUI::displaySettings() m_channelMarker.blockSignals(false); m_channelMarker.setColor(m_settings.m_rgbColor); setTitleColor(m_settings.m_rgbColor); + setTitle(m_channelMarker.getTitle()); ui->glSpectrum->setSampleRate(thisBW); ui->glSpectrum->setCenterFrequency(thisBW/2); @@ -499,7 +503,7 @@ void ChirpChatDemodGUI::displaySettings() displaySquelch(); - restoreState(m_rollupState); + getRollupContents()->restoreState(m_rollupState); blockApplySettings(false); } @@ -767,3 +771,25 @@ void ChirpChatDemodGUI::tick() } } +void ChirpChatDemodGUI::makeUIConnections() +{ + QObject::connect(ui->deltaFrequency, &ValueDialZ::changed, this, &ChirpChatDemodGUI::on_deltaFrequency_changed); + QObject::connect(ui->BW, &QSlider::valueChanged, this, &ChirpChatDemodGUI::on_BW_valueChanged); + QObject::connect(ui->Spread, &QSlider::valueChanged, this, &ChirpChatDemodGUI::on_Spread_valueChanged); + QObject::connect(ui->deBits, &QSlider::valueChanged, this, &ChirpChatDemodGUI::on_deBits_valueChanged); + QObject::connect(ui->fftWindow, QOverload::of(&QComboBox::currentIndexChanged), this, &ChirpChatDemodGUI::on_fftWindow_currentIndexChanged); + QObject::connect(ui->preambleChirps, &QSlider::valueChanged, this, &ChirpChatDemodGUI::on_preambleChirps_valueChanged); + QObject::connect(ui->scheme, QOverload::of(&QComboBox::currentIndexChanged), this, &ChirpChatDemodGUI::on_scheme_currentIndexChanged); + QObject::connect(ui->mute, &QToolButton::toggled, this, &ChirpChatDemodGUI::on_mute_toggled); + QObject::connect(ui->clear, &QPushButton::clicked, this, &ChirpChatDemodGUI::on_clear_clicked); + QObject::connect(ui->eomSquelch, &QDial::valueChanged, this, &ChirpChatDemodGUI::on_eomSquelch_valueChanged); + QObject::connect(ui->messageLength, &QDial::valueChanged, this, &ChirpChatDemodGUI::on_messageLength_valueChanged); + QObject::connect(ui->messageLengthAuto, &QCheckBox::stateChanged, this, &ChirpChatDemodGUI::on_messageLengthAuto_stateChanged); + QObject::connect(ui->header, &QCheckBox::stateChanged, this, &ChirpChatDemodGUI::on_header_stateChanged); + QObject::connect(ui->fecParity, &QDial::valueChanged, this, &ChirpChatDemodGUI::on_fecParity_valueChanged); + QObject::connect(ui->crc, &QCheckBox::stateChanged, this, &ChirpChatDemodGUI::on_crc_stateChanged); + QObject::connect(ui->packetLength, &QDial::valueChanged, this, &ChirpChatDemodGUI::on_packetLength_valueChanged); + QObject::connect(ui->udpSend, &QCheckBox::stateChanged, this, &ChirpChatDemodGUI::on_udpSend_stateChanged); + QObject::connect(ui->udpAddress, &QLineEdit::editingFinished, this, &ChirpChatDemodGUI::on_udpAddress_editingFinished); + QObject::connect(ui->udpPort, &QLineEdit::editingFinished, this, &ChirpChatDemodGUI::on_udpPort_editingFinished); +} diff --git a/plugins/channelrx/demodchirpchat/chirpchatdemodgui.h b/plugins/channelrx/demodchirpchat/chirpchatdemodgui.h index 0dc73f3ba..fd1ebf4b6 100644 --- a/plugins/channelrx/demodchirpchat/chirpchatdemodgui.h +++ b/plugins/channelrx/demodchirpchat/chirpchatdemodgui.h @@ -46,6 +46,12 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } + virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; }; + virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; }; + virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; }; + virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; }; + virtual QString getTitle() const { return m_settings.m_title; }; + virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; private slots: void channelMarkerChangedByCursor(); @@ -115,6 +121,7 @@ private: QString getParityStr(int parityStatus); void resetLoRaStatus(); bool handleMessage(const Message& message); + void makeUIConnections(); }; #endif // INCLUDE_CHIRPCHATDEMODGUI_H diff --git a/plugins/channelrx/demodchirpchat/chirpchatdemodgui.ui b/plugins/channelrx/demodchirpchat/chirpchatdemodgui.ui index 0506899de..4415af525 100644 --- a/plugins/channelrx/demodchirpchat/chirpchatdemodgui.ui +++ b/plugins/channelrx/demodchirpchat/chirpchatdemodgui.ui @@ -1,7 +1,7 @@ ChirpChatDemodGUI - + 0 @@ -1223,9 +1223,9 @@ - RollupWidget + RollupContents QWidget -
gui/rollupwidget.h
+
gui/rollupcontents.h
1
diff --git a/plugins/channelrx/demodchirpchat/chirpchatdemodsettings.cpp b/plugins/channelrx/demodchirpchat/chirpchatdemodsettings.cpp index 4a51c67da..4ae9d9411 100644 --- a/plugins/channelrx/demodchirpchat/chirpchatdemodsettings.cpp +++ b/plugins/channelrx/demodchirpchat/chirpchatdemodsettings.cpp @@ -89,6 +89,7 @@ void ChirpChatDemodSettings::resetToDefaults() m_reverseAPIPort = 8888; m_reverseAPIDeviceIndex = 0; m_reverseAPIChannelIndex = 0; + m_workspaceIndex = 0; } QByteArray ChirpChatDemodSettings::serialize() const @@ -132,6 +133,9 @@ QByteArray ChirpChatDemodSettings::serialize() const s.writeBlob(29, m_rollupState->serialize()); } + s.writeS32(30, m_workspaceIndex); + s.writeBlob(31, m_geometryBytes); + return s.final(); } @@ -212,6 +216,9 @@ bool ChirpChatDemodSettings::deserialize(const QByteArray& data) m_rollupState->deserialize(bytetmp); } + d.readS32(30, &m_workspaceIndex, 0); + d.readBlob(31, &m_geometryBytes); + return true; } else diff --git a/plugins/channelrx/demodchirpchat/chirpchatdemodsettings.h b/plugins/channelrx/demodchirpchat/chirpchatdemodsettings.h index 93d9ba6b6..f2bb10613 100644 --- a/plugins/channelrx/demodchirpchat/chirpchatdemodsettings.h +++ b/plugins/channelrx/demodchirpchat/chirpchatdemodsettings.h @@ -63,6 +63,8 @@ struct ChirpChatDemodSettings uint16_t m_reverseAPIPort; uint16_t m_reverseAPIDeviceIndex; uint16_t m_reverseAPIChannelIndex; + int m_workspaceIndex; + QByteArray m_geometryBytes; Serializable *m_channelMarker; Serializable *m_spectrumGUI; diff --git a/plugins/channelrx/demoddab/dabdemodgui.cpp b/plugins/channelrx/demoddab/dabdemodgui.cpp index 406410d5b..ebc036720 100644 --- a/plugins/channelrx/demoddab/dabdemodgui.cpp +++ b/plugins/channelrx/demoddab/dabdemodgui.cpp @@ -208,7 +208,7 @@ bool DABDemodGUI::handleMessage(const Message& message) } else { ui->warning->setText(""); } - arrangeRollups(); + getRollupContents()->arrangeRollups(); return true; } else if (DABDemod::MsgDABEnsembleName::match(message)) @@ -281,7 +281,7 @@ bool DABDemodGUI::handleMessage(const Message& message) ui->motImage->resize(ui->motImage->width(), pixmap.height()); ui->motImage->setVisible(true); ui->motImage->setPixmap(pixmap, pixmap.size()); - arrangeRollups(); + getRollupContents()->arrangeRollups(); } return true; } @@ -311,7 +311,7 @@ void DABDemodGUI::channelMarkerChangedByCursor() void DABDemodGUI::channelMarkerHighlightedByCursor() { - setHighlighted(m_channelMarker.getHighlighted()); + getRollupContents()->setHighlighted(m_channelMarker.getHighlighted()); } void DABDemodGUI::on_deltaFrequency_changed(qint64 value) @@ -384,7 +384,7 @@ void DABDemodGUI::onWidgetRolled(QWidget* widget, bool rollDown) (void) widget; (void) rollDown; - saveState(m_rollupState); + getRollupContents()->saveState(m_rollupState); applySettings(); } @@ -410,6 +410,7 @@ void DABDemodGUI::onMenuDialogCalled(const QPoint &p) m_settings.m_reverseAPIChannelIndex = dialog.getReverseAPIChannelIndex(); setWindowTitle(m_settings.m_title); + setTitle(m_channelMarker.getTitle()); setTitleColor(m_settings.m_rgbColor); applySettings(); @@ -442,11 +443,12 @@ DABDemodGUI::DABDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Baseban m_tickCount(0), m_channelFreq(0.0) { - ui->setupUi(this); + ui->setupUi(getRollupContents()); + getRollupContents()->arrangeRollups(); m_helpURL = "plugins/channelrx/demoddab/readme.md"; setAttribute(Qt::WA_DeleteOnClose, true); - connect(this, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + connect(getRollupContents(), SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &))); m_dabDemod = reinterpret_cast(rxChannel); @@ -506,6 +508,7 @@ DABDemodGUI::DABDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Baseban connect(ui->programs->horizontalHeader(), SIGNAL(sectionResized(int, int, int)), SLOT(programs_sectionResized(int, int, int))); displaySettings(); + makeUIConnections(); applySettings(true); } @@ -539,6 +542,7 @@ void DABDemodGUI::displaySettings() setTitleColor(m_settings.m_rgbColor); setWindowTitle(m_channelMarker.getTitle()); + setTitle(m_channelMarker.getTitle()); blockApplySettings(true); @@ -569,7 +573,7 @@ void DABDemodGUI::displaySettings() filter(); - restoreState(m_rollupState); + getRollupContents()->restoreState(m_rollupState); blockApplySettings(false); } @@ -605,7 +609,7 @@ void DABDemodGUI::clearProgram() ui->data->setText(""); ui->motImage->setPixmap(QPixmap()); ui->motImage->setVisible(false); - arrangeRollups(); + getRollupContents()->arrangeRollups(); } void DABDemodGUI::resetService() @@ -692,3 +696,15 @@ void DABDemodGUI::tick() m_tickCount++; } + +void DABDemodGUI::makeUIConnections() +{ + QObject::connect(ui->deltaFrequency, &ValueDialZ::changed, this, &DABDemodGUI::on_deltaFrequency_changed); + QObject::connect(ui->audioMute, &QToolButton::toggled, this, &DABDemodGUI::on_audioMute_toggled); + QObject::connect(ui->volume, &QSlider::valueChanged, this, &DABDemodGUI::on_volume_valueChanged); + QObject::connect(ui->rfBW, &QSlider::valueChanged, this, &DABDemodGUI::on_rfBW_valueChanged); + QObject::connect(ui->filter, &QLineEdit::editingFinished, this, &DABDemodGUI::on_filter_editingFinished); + QObject::connect(ui->clearTable, &QPushButton::clicked, this, &DABDemodGUI::on_clearTable_clicked); + QObject::connect(ui->programs, &QTableWidget::cellDoubleClicked, this, &DABDemodGUI::on_programs_cellDoubleClicked); + QObject::connect(ui->channel, QOverload::of(&QComboBox::currentIndexChanged), this, &DABDemodGUI::on_channel_currentIndexChanged); +} diff --git a/plugins/channelrx/demoddab/dabdemodgui.h b/plugins/channelrx/demoddab/dabdemodgui.h index c8ba0270a..f8b59f534 100644 --- a/plugins/channelrx/demoddab/dabdemodgui.h +++ b/plugins/channelrx/demoddab/dabdemodgui.h @@ -53,6 +53,12 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } + virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; }; + virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; }; + virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; }; + virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; }; + virtual QString getTitle() const { return m_settings.m_title; }; + virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; public slots: void channelMarkerChangedByCursor(); @@ -84,6 +90,8 @@ private: void displayStreamIndex(); void addProgramName(const DABDemod::MsgDABProgramName& program); bool handleMessage(const Message& message); + void makeUIConnections(); + void leaveEvent(QEvent*); void enterEvent(QEvent*); void resetService(); diff --git a/plugins/channelrx/demoddab/dabdemodgui.ui b/plugins/channelrx/demoddab/dabdemodgui.ui index c166e5c5a..5367f6909 100644 --- a/plugins/channelrx/demoddab/dabdemodgui.ui +++ b/plugins/channelrx/demoddab/dabdemodgui.ui @@ -1,7 +1,7 @@ DABDemodGUI - + 0 @@ -1009,9 +1009,9 @@ - RollupWidget + RollupContents QWidget -
gui/rollupwidget.h
+
gui/rollupcontents.h
1
diff --git a/plugins/channelrx/demoddab/dabdemodsettings.cpp b/plugins/channelrx/demoddab/dabdemodsettings.cpp index 029729006..db575b573 100644 --- a/plugins/channelrx/demoddab/dabdemodsettings.cpp +++ b/plugins/channelrx/demoddab/dabdemodsettings.cpp @@ -47,6 +47,7 @@ void DABDemodSettings::resetToDefaults() m_reverseAPIPort = 8888; m_reverseAPIDeviceIndex = 0; m_reverseAPIChannelIndex = 0; + m_workspaceIndex = 0; for (int i = 0; i < DABDEMOD_COLUMNS; i++) { @@ -82,6 +83,9 @@ QByteArray DABDemodSettings::serialize() const s.writeBlob(16, m_rollupState->serialize()); } + s.writeS32(17, m_workspaceIndex); + s.writeBlob(18, m_geometryBytes); + for (int i = 0; i < DABDEMOD_COLUMNS; i++) { s.writeS32(100 + i, m_columnIndexes[i]); } @@ -146,6 +150,9 @@ bool DABDemodSettings::deserialize(const QByteArray& data) m_rollupState->deserialize(bytetmp); } + d.readS32(17, &m_workspaceIndex, 0); + d.readBlob(18, &m_geometryBytes); + for (int i = 0; i < DABDEMOD_COLUMNS; i++) { d.readS32(100 + i, &m_columnIndexes[i], i); } diff --git a/plugins/channelrx/demoddab/dabdemodsettings.h b/plugins/channelrx/demoddab/dabdemodsettings.h index 80234e935..596ba2eaf 100644 --- a/plugins/channelrx/demoddab/dabdemodsettings.h +++ b/plugins/channelrx/demoddab/dabdemodsettings.h @@ -46,6 +46,8 @@ struct DABDemodSettings uint16_t m_reverseAPIDeviceIndex; uint16_t m_reverseAPIChannelIndex; Serializable *m_rollupState; + int m_workspaceIndex; + QByteArray m_geometryBytes; int m_columnIndexes[DABDEMOD_COLUMNS];//!< How the columns are ordered in the table int m_columnSizes[DABDEMOD_COLUMNS]; //!< Size of the columns in the table diff --git a/plugins/channelrx/demoddatv/datvdemodgui.cpp b/plugins/channelrx/demoddatv/datvdemodgui.cpp index b0863b099..5af728860 100644 --- a/plugins/channelrx/demoddatv/datvdemodgui.cpp +++ b/plugins/channelrx/demoddatv/datvdemodgui.cpp @@ -128,7 +128,7 @@ void DATVDemodGUI::channelMarkerChangedByCursor() void DATVDemodGUI::channelMarkerHighlightedByCursor() { - setHighlighted(m_objChannelMarker.getHighlighted()); + getRollupContents()->setHighlighted(m_objChannelMarker.getHighlighted()); } @@ -137,7 +137,7 @@ void DATVDemodGUI::onWidgetRolled(QWidget* widget, bool rollDown) (void) widget; (void) rollDown; - saveState(m_rollupState); + getRollupContents()->saveState(m_rollupState); applySettings(); } @@ -164,6 +164,7 @@ void DATVDemodGUI::onMenuDialogCalled(const QPoint &p) m_settings.m_reverseAPIChannelIndex = dialog.getReverseAPIChannelIndex(); setWindowTitle(m_settings.m_title); + setTitle(m_objChannelMarker.getTitle()); setTitleColor(m_settings.m_rgbColor); applySettings(); @@ -198,12 +199,13 @@ DATVDemodGUI::DATVDemodGUI(PluginAPI* objPluginAPI, DeviceUISet *deviceUISet, Ba m_modcodCodeRateIndex(-1), m_cstlnSetByModcod(false) { - ui->setupUi(this); + ui->setupUi(getRollupContents()); + getRollupContents()->arrangeRollups(); m_helpURL = "plugins/channelrx/demoddatv/readme.md"; ui->screenTV->setColor(true); ui->screenTV->resizeTVScreen(256,256); setAttribute(Qt::WA_DeleteOnClose, true); - connect(this, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + connect(getRollupContents(), SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &))); connect(getInputMessageQueue(), SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages())); @@ -283,6 +285,7 @@ DATVDemodGUI::DATVDemodGUI(PluginAPI* objPluginAPI, DeviceUISet *deviceUISet, Ba ui->playerIndicator->setStyleSheet("QLabel { background-color: gray; border-radius: 8px; }"); ui->udpIndicator->setStyleSheet("QLabel { background-color: gray; border-radius: 8px; }"); resetToDefaults(); // does applySettings() + makeUIConnections(); } DATVDemodGUI::~DATVDemodGUI() @@ -308,6 +311,7 @@ void DATVDemodGUI::displaySettings() setTitleColor(m_settings.m_rgbColor); setWindowTitle(m_objChannelMarker.getTitle()); + setTitle(m_objChannelMarker.getTitle()); ui->deltaFrequency->setValue(m_settings.m_centerFrequency); ui->chkAllowDrift->setChecked(m_settings.m_allowDrift); @@ -395,7 +399,7 @@ void DATVDemodGUI::displaySettings() connect(m_datvDemod->getUDPStream(), &DATVUDPStream::fifoData, this, &DATVDemodGUI::on_StreamDataAvailable); } - restoreState(m_rollupState); + getRollupContents()->restoreState(m_rollupState); blockApplySettings(false); } @@ -656,7 +660,7 @@ void DATVDemodGUI::on_cmbStandard_currentIndexChanged(int index) applySettings(); } -void DATVDemodGUI::on_cmbModulation_currentIndexChanged(const QString &arg1) +void DATVDemodGUI::on_cmbModulation_currentIndexChanged(int arg1) { (void) arg1; QString strModulation = ui->cmbModulation->currentText(); @@ -674,7 +678,7 @@ void DATVDemodGUI::on_cmbModulation_currentIndexChanged(const QString &arg1) applySettings(); } -void DATVDemodGUI::on_cmbFEC_currentIndexChanged(const QString &arg1) +void DATVDemodGUI::on_cmbFEC_currentIndexChanged(int arg1) { (void) arg1; QString strFEC = ui->cmbFEC->currentText(); @@ -919,3 +923,32 @@ void DATVDemodGUI::on_udpTSPort_editingFinished() ui->udpTSPort->setText(tr("%1").arg(udpPort)); applySettings(); } + +void DATVDemodGUI::makeUIConnections() +{ + QObject::connect(ui->cmbStandard, QOverload::of(&QComboBox::currentIndexChanged), this, &DATVDemodGUI::on_cmbStandard_currentIndexChanged); + QObject::connect(ui->cmbModulation, QOverload::of(&QComboBox::currentIndexChanged), this, &DATVDemodGUI::on_cmbModulation_currentIndexChanged); + QObject::connect(ui->cmbFEC, QOverload::of(&QComboBox::currentIndexChanged), this, &DATVDemodGUI::on_cmbFEC_currentIndexChanged); + QObject::connect(ui->softLDPC, &QCheckBox::clicked, this, &DATVDemodGUI::on_softLDPC_clicked); + QObject::connect(ui->maxBitflips, QOverload::of(&QSpinBox::valueChanged), this, &DATVDemodGUI::on_maxBitflips_valueChanged); + QObject::connect(ui->chkViterbi, &QCheckBox::clicked, this, &DATVDemodGUI::on_chkViterbi_clicked); + QObject::connect(ui->chkHardMetric, &QCheckBox::clicked, this, &DATVDemodGUI::on_chkHardMetric_clicked); + QObject::connect(ui->resetDefaults, &QPushButton::clicked, this, &DATVDemodGUI::on_resetDefaults_clicked); + QObject::connect(ui->spiSymbolRate, QOverload::of(&QSpinBox::valueChanged), this, &DATVDemodGUI::on_spiSymbolRate_valueChanged); + QObject::connect(ui->spiNotchFilters, QOverload::of(&QSpinBox::valueChanged), this, &DATVDemodGUI::on_spiNotchFilters_valueChanged); + QObject::connect(ui->chkAllowDrift, &QCheckBox::clicked, this, &DATVDemodGUI::on_chkAllowDrift_clicked); + QObject::connect(ui->fullScreen, &QPushButton::clicked, this, &DATVDemodGUI::on_fullScreen_clicked); + QObject::connect(ui->chkFastlock, &QCheckBox::clicked, this, &DATVDemodGUI::on_chkFastlock_clicked); + QObject::connect(ui->cmbFilter, QOverload::of(&QComboBox::currentIndexChanged), this, &DATVDemodGUI::on_cmbFilter_currentIndexChanged); + QObject::connect(ui->spiRollOff, QOverload::of(&QSpinBox::valueChanged), this, &DATVDemodGUI::on_spiRollOff_valueChanged); + QObject::connect(ui->spiExcursion, QOverload::of(&QSpinBox::valueChanged), this, &DATVDemodGUI::on_spiExcursion_valueChanged); + QObject::connect(ui->deltaFrequency, &ValueDialZ::changed, this, &DATVDemodGUI::on_deltaFrequency_changed); + QObject::connect(ui->rfBandwidth, &ValueDialZ::changed, this, &DATVDemodGUI::on_rfBandwidth_changed); + QObject::connect(ui->audioMute, &QToolButton::toggled, this, &DATVDemodGUI::on_audioMute_toggled); + QObject::connect(ui->audioVolume, &QSlider::valueChanged, this, &DATVDemodGUI::on_audioVolume_valueChanged); + QObject::connect(ui->videoMute, &QToolButton::toggled, this, &DATVDemodGUI::on_videoMute_toggled); + QObject::connect(ui->udpTS, &ButtonSwitch::clicked, this, &DATVDemodGUI::on_udpTS_clicked); + QObject::connect(ui->udpTSAddress, &QLineEdit::editingFinished, this, &DATVDemodGUI::on_udpTSAddress_editingFinished); + QObject::connect(ui->udpTSPort, &QLineEdit::editingFinished, this, &DATVDemodGUI::on_udpTSPort_editingFinished); + QObject::connect(ui->playerEnable, &QCheckBox::clicked, this, &DATVDemodGUI::on_playerEnable_clicked); +} diff --git a/plugins/channelrx/demoddatv/datvdemodgui.h b/plugins/channelrx/demoddatv/datvdemodgui.h index efd4f6283..3812290a7 100644 --- a/plugins/channelrx/demoddatv/datvdemodgui.h +++ b/plugins/channelrx/demoddatv/datvdemodgui.h @@ -50,6 +50,12 @@ public: void resetToDefaults(); QByteArray serialize() const; bool deserialize(const QByteArray& arrData); + virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; }; + virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; }; + virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; }; + virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; }; + virtual QString getTitle() const { return m_settings.m_title; }; + virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } @@ -68,8 +74,8 @@ private slots: void tickMeter(); void on_cmbStandard_currentIndexChanged(int index); - void on_cmbModulation_currentIndexChanged(const QString &arg1); - void on_cmbFEC_currentIndexChanged(const QString &arg1); + void on_cmbModulation_currentIndexChanged(int arg1); + void on_cmbFEC_currentIndexChanged(int arg1); void on_softLDPC_clicked(); void on_maxBitflips_valueChanged(int value); void on_chkViterbi_clicked(); @@ -137,6 +143,7 @@ private: void leaveEvent(QEvent*); void enterEvent(QEvent*); bool handleMessage(const Message& objMessage); + void makeUIConnections(); }; #endif // INCLUDE_DATVDEMODGUI_H diff --git a/plugins/channelrx/demoddatv/datvdemodgui.ui b/plugins/channelrx/demoddatv/datvdemodgui.ui index 976851a62..d4ec6ce0b 100644 --- a/plugins/channelrx/demoddatv/datvdemodgui.ui +++ b/plugins/channelrx/demoddatv/datvdemodgui.ui @@ -1,7 +1,7 @@ DATVDemodGUI - + 0 @@ -1394,9 +1394,9 @@ - RollupWidget + RollupContents QWidget -
gui/rollupwidget.h
+
gui/rollupcontents.h
1
diff --git a/plugins/channelrx/demoddatv/datvdemodsettings.cpp b/plugins/channelrx/demoddatv/datvdemodsettings.cpp index ceb3b4bc5..eba558f95 100644 --- a/plugins/channelrx/demoddatv/datvdemodsettings.cpp +++ b/plugins/channelrx/demoddatv/datvdemodsettings.cpp @@ -70,6 +70,7 @@ void DATVDemodSettings::resetToDefaults() m_reverseAPIPort = 8888; m_reverseAPIDeviceIndex = 0; m_reverseAPIChannelIndex = 0; + m_workspaceIndex = 0; } QByteArray DATVDemodSettings::serialize() const @@ -119,6 +120,9 @@ QByteArray DATVDemodSettings::serialize() const s.writeBlob(37, m_rollupState->serialize()); } + s.writeS32(38, m_workspaceIndex); + s.writeBlob(39, m_geometryBytes); + return s.final(); } @@ -213,6 +217,9 @@ bool DATVDemodSettings::deserialize(const QByteArray& data) m_rollupState->deserialize(bytetmp); } + d.readS32(38, &m_workspaceIndex, 0); + d.readBlob(39, &m_geometryBytes); + validateSystemConfiguration(); return true; diff --git a/plugins/channelrx/demoddatv/datvdemodsettings.h b/plugins/channelrx/demoddatv/datvdemodsettings.h index 4d5712d7e..8da3bbc9a 100644 --- a/plugins/channelrx/demoddatv/datvdemodsettings.h +++ b/plugins/channelrx/demoddatv/datvdemodsettings.h @@ -108,6 +108,9 @@ struct DATVDemodSettings uint16_t m_reverseAPIDeviceIndex; uint16_t m_reverseAPIChannelIndex; Serializable *m_rollupState; + int m_workspaceIndex; + QByteArray m_geometryBytes; + static const int m_softLDPCMaxMaxTrials = 50; DATVDemodSettings(); diff --git a/plugins/channelrx/demoddsd/dsddemodgui.cpp b/plugins/channelrx/demoddsd/dsddemodgui.cpp index b6f79dea8..ae2e33071 100644 --- a/plugins/channelrx/demoddsd/dsddemodgui.cpp +++ b/plugins/channelrx/demoddsd/dsddemodgui.cpp @@ -248,7 +248,7 @@ void DSDDemodGUI::onWidgetRolled(QWidget* widget, bool rollDown) (void) widget; (void) rollDown; - saveState(m_rollupState); + getRollupContents()->saveState(m_rollupState); applySettings(); } @@ -276,6 +276,7 @@ void DSDDemodGUI::onMenuDialogCalled(const QPoint &p) m_settings.m_reverseAPIChannelIndex = dialog.getReverseAPIChannelIndex(); setWindowTitle(m_settings.m_title); + setTitle(m_channelMarker.getTitle()); setTitleColor(m_settings.m_rgbColor); applySettings(); @@ -321,13 +322,14 @@ DSDDemodGUI::DSDDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Baseban m_tickCount(0), m_dsdStatusTextDialog(0) { - ui->setupUi(this); + ui->setupUi(getRollupContents()); + getRollupContents()->arrangeRollups(); m_helpURL = "plugins/channelrx/demoddsd/readme.md"; ui->screenTV->setColor(true); ui->screenTV->resizeTVScreen(200,200); setAttribute(Qt::WA_DeleteOnClose, true); - connect(this, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + connect(getRollupContents(), SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &))); connect(getInputMessageQueue(), SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages())); @@ -382,6 +384,7 @@ DSDDemodGUI::DSDDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Baseban updateMyPosition(); displaySettings(); + makeUIConnections(); applySettings(true); } @@ -415,6 +418,7 @@ void DSDDemodGUI::displaySettings() setTitleColor(m_settings.m_rgbColor); setWindowTitle(m_channelMarker.getTitle()); + setTitle(m_channelMarker.getTitle()); blockApplySettings(true); @@ -463,7 +467,7 @@ void DSDDemodGUI::displaySettings() displayStreamIndex(); - restoreState(m_rollupState); + getRollupContents()->restoreState(m_rollupState); blockApplySettings(false); } @@ -511,7 +515,7 @@ void DSDDemodGUI::channelMarkerChangedByCursor() void DSDDemodGUI::channelMarkerHighlightedByCursor() { - setHighlighted(m_channelMarker.getHighlighted()); + getRollupContents()->setHighlighted(m_channelMarker.getHighlighted()); } void DSDDemodGUI::audioSelect() @@ -614,3 +618,25 @@ void DSDDemodGUI::tick() m_tickCount++; } + +void DSDDemodGUI::makeUIConnections() +{ + QObject::connect(ui->deltaFrequency, &ValueDialZ::changed, this, &DSDDemodGUI::on_deltaFrequency_changed); + QObject::connect(ui->rfBW, &QSlider::valueChanged, this, &DSDDemodGUI::on_rfBW_valueChanged); + QObject::connect(ui->demodGain, &QSlider::valueChanged, this, &DSDDemodGUI::on_demodGain_valueChanged); + QObject::connect(ui->volume, &QDial::valueChanged, this, &DSDDemodGUI::on_volume_valueChanged); + QObject::connect(ui->baudRate, QOverload::of(&QComboBox::currentIndexChanged), this, &DSDDemodGUI::on_baudRate_currentIndexChanged); + QObject::connect(ui->enableCosineFiltering, &ButtonSwitch::toggled, this, &DSDDemodGUI::on_enableCosineFiltering_toggled); + QObject::connect(ui->syncOrConstellation, &QToolButton::toggled, this, &DSDDemodGUI::on_syncOrConstellation_toggled); + QObject::connect(ui->traceLength, &QDial::valueChanged, this, &DSDDemodGUI::on_traceLength_valueChanged); + QObject::connect(ui->traceStroke, &QDial::valueChanged, this, &DSDDemodGUI::on_traceStroke_valueChanged); + QObject::connect(ui->traceDecay, &QDial::valueChanged, this, &DSDDemodGUI::on_traceDecay_valueChanged); + QObject::connect(ui->tdmaStereoSplit, &QToolButton::toggled, this, &DSDDemodGUI::on_tdmaStereoSplit_toggled); + QObject::connect(ui->fmDeviation, &QSlider::valueChanged, this, &DSDDemodGUI::on_fmDeviation_valueChanged); + QObject::connect(ui->squelchGate, &QDial::valueChanged, this, &DSDDemodGUI::on_squelchGate_valueChanged); + QObject::connect(ui->squelch, &QDial::valueChanged, this, &DSDDemodGUI::on_squelch_valueChanged); + QObject::connect(ui->highPassFilter, &ButtonSwitch::toggled, this, &DSDDemodGUI::on_highPassFilter_toggled); + QObject::connect(ui->audioMute, &QToolButton::toggled, this, &DSDDemodGUI::on_audioMute_toggled); + QObject::connect(ui->symbolPLLLock, &QToolButton::toggled, this, &DSDDemodGUI::on_symbolPLLLock_toggled); + QObject::connect(ui->viewStatusLog, &QPushButton::clicked, this, &DSDDemodGUI::on_viewStatusLog_clicked); +} diff --git a/plugins/channelrx/demoddsd/dsddemodgui.h b/plugins/channelrx/demoddsd/dsddemodgui.h index d72985ae5..1d4e4aacd 100644 --- a/plugins/channelrx/demoddsd/dsddemodgui.h +++ b/plugins/channelrx/demoddsd/dsddemodgui.h @@ -52,6 +52,12 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } + virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; }; + virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; }; + virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; }; + virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; }; + virtual QString getTitle() const { return m_settings.m_title; }; + virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; public slots: void channelMarkerChangedByCursor(); @@ -104,6 +110,7 @@ private: void displayStreamIndex(); void updateMyPosition(); bool handleMessage(const Message& message); + void makeUIConnections(); void leaveEvent(QEvent*); void enterEvent(QEvent*); diff --git a/plugins/channelrx/demoddsd/dsddemodgui.ui b/plugins/channelrx/demoddsd/dsddemodgui.ui index 68565f0c5..295059813 100644 --- a/plugins/channelrx/demoddsd/dsddemodgui.ui +++ b/plugins/channelrx/demoddsd/dsddemodgui.ui @@ -1,7 +1,7 @@ DSDDemodGUI - + 0 @@ -1232,9 +1232,9 @@ - RollupWidget + RollupContents QWidget -
gui/rollupwidget.h
+
gui/rollupcontents.h
1
diff --git a/plugins/channelrx/demoddsd/dsddemodsettings.cpp b/plugins/channelrx/demoddsd/dsddemodsettings.cpp index 02dd24b33..3c025b48e 100644 --- a/plugins/channelrx/demoddsd/dsddemodsettings.cpp +++ b/plugins/channelrx/demoddsd/dsddemodsettings.cpp @@ -59,6 +59,7 @@ void DSDDemodSettings::resetToDefaults() m_reverseAPIPort = 8888; m_reverseAPIDeviceIndex = 0; m_reverseAPIChannelIndex = 0; + m_workspaceIndex = 0; } QByteArray DSDDemodSettings::serialize() const @@ -102,6 +103,9 @@ QByteArray DSDDemodSettings::serialize() const s.writeBlob(31, m_rollupState->serialize()); } + s.writeS32(32, m_workspaceIndex); + s.writeBlob(33, m_geometryBytes); + return s.final(); } @@ -181,6 +185,9 @@ bool DSDDemodSettings::deserialize(const QByteArray& data) m_rollupState->deserialize(bytetmp); } + d.readS32(32, &m_workspaceIndex, 0); + d.readBlob(33, &m_geometryBytes); + return true; } else diff --git a/plugins/channelrx/demoddsd/dsddemodsettings.h b/plugins/channelrx/demoddsd/dsddemodsettings.h index e16dd5d7d..3c875edea 100644 --- a/plugins/channelrx/demoddsd/dsddemodsettings.h +++ b/plugins/channelrx/demoddsd/dsddemodsettings.h @@ -52,6 +52,8 @@ struct DSDDemodSettings uint16_t m_reverseAPIPort; uint16_t m_reverseAPIDeviceIndex; uint16_t m_reverseAPIChannelIndex; + int m_workspaceIndex; + QByteArray m_geometryBytes; Serializable *m_channelMarker; Serializable *m_rollupState; diff --git a/plugins/channelrx/demodfreedv/freedvdemodgui.cpp b/plugins/channelrx/demodfreedv/freedvdemodgui.cpp index 51138defa..00455c0b1 100644 --- a/plugins/channelrx/demodfreedv/freedvdemodgui.cpp +++ b/plugins/channelrx/demodfreedv/freedvdemodgui.cpp @@ -123,7 +123,7 @@ void FreeDVDemodGUI::channelMarkerChangedByCursor() void FreeDVDemodGUI::channelMarkerHighlightedByCursor() { - setHighlighted(m_channelMarker.getHighlighted()); + getRollupContents()->setHighlighted(m_channelMarker.getHighlighted()); } void FreeDVDemodGUI::on_deltaFrequency_changed(qint64 value) @@ -209,6 +209,7 @@ void FreeDVDemodGUI::onMenuDialogCalled(const QPoint &p) m_settings.m_reverseAPIChannelIndex = dialog.getReverseAPIChannelIndex(); setWindowTitle(m_settings.m_title); + setTitle(m_channelMarker.getTitle()); setTitleColor(m_settings.m_rgbColor); applySettings(); @@ -236,7 +237,7 @@ void FreeDVDemodGUI::onWidgetRolled(QWidget* widget, bool rollDown) (void) widget; (void) rollDown; - saveState(m_rollupState); + getRollupContents()->saveState(m_rollupState); applySettings(); } @@ -254,10 +255,11 @@ FreeDVDemodGUI::FreeDVDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, B m_squelchOpen(false), m_audioSampleRate(-1) { - ui->setupUi(this); + ui->setupUi(getRollupContents()); + getRollupContents()->arrangeRollups(); m_helpURL = "plugins/channelrx/demodfreedv/readme.md"; setAttribute(Qt::WA_DeleteOnClose, true); - connect(this, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + connect(getRollupContents(), SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &))); m_freeDVDemod = (FreeDVDemod*) rxChannel; @@ -308,6 +310,7 @@ FreeDVDemodGUI::FreeDVDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, B m_iconDSBLSB.addPixmap(QPixmap("://lsb.png"), QIcon::Normal, QIcon::Off); displaySettings(); + makeUIConnections(); applyBandwidths(5 - ui->spanLog2->value(), true); // does applySettings(true) } @@ -371,6 +374,7 @@ void FreeDVDemodGUI::displaySettings() setTitleColor(m_settings.m_rgbColor); setWindowTitle(m_channelMarker.getTitle()); + setTitle(m_channelMarker.getTitle()); blockApplySettings(true); @@ -393,7 +397,7 @@ void FreeDVDemodGUI::displaySettings() displayStreamIndex(); - restoreState(m_rollupState); + getRollupContents()->restoreState(m_rollupState); blockApplySettings(false); } @@ -487,3 +491,14 @@ void FreeDVDemodGUI::tick() m_tickCount++; } + +void FreeDVDemodGUI::makeUIConnections() +{ + QObject::connect(ui->deltaFrequency, &ValueDialZ::changed, this, &FreeDVDemodGUI::on_deltaFrequency_changed); + QObject::connect(ui->reSync, &QPushButton::clicked, this, &FreeDVDemodGUI::on_reSync_clicked); + QObject::connect(ui->freeDVMode, QOverload::of(&QComboBox::currentIndexChanged), this, &FreeDVDemodGUI::on_freeDVMode_currentIndexChanged); + QObject::connect(ui->volume, &QDial::valueChanged, this, &FreeDVDemodGUI::on_volume_valueChanged); + QObject::connect(ui->volumeIn, &QDial::valueChanged, this, &FreeDVDemodGUI::on_volumeIn_valueChanged); + QObject::connect(ui->agc, &ButtonSwitch::toggled, this, &FreeDVDemodGUI::on_agc_toggled); + QObject::connect(ui->audioMute, &QToolButton::toggled, this, &FreeDVDemodGUI::on_audioMute_toggled); +} diff --git a/plugins/channelrx/demodfreedv/freedvdemodgui.h b/plugins/channelrx/demodfreedv/freedvdemodgui.h index d36c30436..655e2115f 100644 --- a/plugins/channelrx/demodfreedv/freedvdemodgui.h +++ b/plugins/channelrx/demodfreedv/freedvdemodgui.h @@ -50,6 +50,12 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } + virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; }; + virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; }; + virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; }; + virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; }; + virtual QString getTitle() const { return m_settings.m_title; }; + virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; public slots: void channelMarkerChangedByCursor(); @@ -88,6 +94,7 @@ private: void displaySettings(); void displayStreamIndex(); bool handleMessage(const Message& message); + void makeUIConnections(); void leaveEvent(QEvent*); void enterEvent(QEvent*); diff --git a/plugins/channelrx/demodfreedv/freedvdemodgui.ui b/plugins/channelrx/demodfreedv/freedvdemodgui.ui index f30eada83..4c1ab1c80 100644 --- a/plugins/channelrx/demodfreedv/freedvdemodgui.ui +++ b/plugins/channelrx/demodfreedv/freedvdemodgui.ui @@ -1,7 +1,7 @@ FreeDVDemodGUI - + 0 @@ -710,9 +710,9 @@ - RollupWidget + RollupContents QWidget -
gui/rollupwidget.h
+
gui/rollupcontents.h
1
diff --git a/plugins/channelrx/demodfreedv/freedvdemodsettings.cpp b/plugins/channelrx/demodfreedv/freedvdemodsettings.cpp index f5fea767f..ff0b51613 100644 --- a/plugins/channelrx/demodfreedv/freedvdemodsettings.cpp +++ b/plugins/channelrx/demodfreedv/freedvdemodsettings.cpp @@ -56,6 +56,7 @@ void FreeDVDemodSettings::resetToDefaults() m_reverseAPIPort = 8888; m_reverseAPIDeviceIndex = 0; m_reverseAPIChannelIndex = 0; + m_workspaceIndex = 0; } QByteArray FreeDVDemodSettings::serialize() const @@ -86,6 +87,9 @@ QByteArray FreeDVDemodSettings::serialize() const s.writeBlob(25, m_rollupState->serialize()); } + s.writeS32(26, m_workspaceIndex); + s.writeBlob(27, m_geometryBytes); + return s.final(); } @@ -154,6 +158,9 @@ bool FreeDVDemodSettings::deserialize(const QByteArray& data) m_rollupState->deserialize(bytetmp); } + d.readS32(26, &m_workspaceIndex, 0); + d.readBlob(27, &m_geometryBytes); + return true; } else diff --git a/plugins/channelrx/demodfreedv/freedvdemodsettings.h b/plugins/channelrx/demodfreedv/freedvdemodsettings.h index ef3ce36eb..84b4fca3a 100644 --- a/plugins/channelrx/demodfreedv/freedvdemodsettings.h +++ b/plugins/channelrx/demodfreedv/freedvdemodsettings.h @@ -51,6 +51,8 @@ struct FreeDVDemodSettings uint16_t m_reverseAPIPort; uint16_t m_reverseAPIDeviceIndex; uint16_t m_reverseAPIChannelIndex; + int m_workspaceIndex; + QByteArray m_geometryBytes; Serializable *m_channelMarker; Serializable *m_spectrumGUI; diff --git a/plugins/channelrx/demodnfm/nfmdemodgui.cpp b/plugins/channelrx/demodnfm/nfmdemodgui.cpp index 831ac89ad..b7cd4dd0b 100644 --- a/plugins/channelrx/demodnfm/nfmdemodgui.cpp +++ b/plugins/channelrx/demodnfm/nfmdemodgui.cpp @@ -106,7 +106,7 @@ void NFMDemodGUI::channelMarkerChangedByCursor() void NFMDemodGUI::channelMarkerHighlightedByCursor() { - setHighlighted(m_channelMarker.getHighlighted()); + getRollupContents()->setHighlighted(m_channelMarker.getHighlighted()); } void NFMDemodGUI::on_deltaFrequency_changed(qint64 value) @@ -280,7 +280,7 @@ void NFMDemodGUI::onWidgetRolled(QWidget* widget, bool rollDown) m_nfmDemod->setSpectrum(m_threadedSampleSink->getMessageQueue(), rollDown); */ - saveState(m_rollupState); + getRollupContents()->saveState(m_rollupState); applySettings(); } @@ -307,6 +307,7 @@ void NFMDemodGUI::onMenuDialogCalled(const QPoint &p) m_settings.m_reverseAPIChannelIndex = dialog.getReverseAPIChannelIndex(); setWindowTitle(m_settings.m_title); + setTitle(m_channelMarker.getTitle()); setTitleColor(m_settings.m_rgbColor); applySettings(); @@ -343,11 +344,12 @@ NFMDemodGUI::NFMDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Baseban m_dcsShowPositive(false), m_tickCount(0) { - ui->setupUi(this); + ui->setupUi(getRollupContents()); + getRollupContents()->arrangeRollups(); m_helpURL = "plugins/channelrx/demodnfm/readme.md"; setAttribute(Qt::WA_DeleteOnClose, true); - connect(this, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + connect(getRollupContents(), SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &))); m_nfmDemod = reinterpret_cast(rxChannel); @@ -420,6 +422,7 @@ NFMDemodGUI::NFMDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Baseban connect(getInputMessageQueue(), SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages())); displaySettings(); + makeUIConnections(); applySettings(true); } @@ -450,6 +453,7 @@ void NFMDemodGUI::displaySettings() setTitleColor(m_settings.m_rgbColor); setWindowTitle(m_channelMarker.getTitle()); + setTitle(m_channelMarker.getTitle()); blockApplySettings(true); @@ -509,7 +513,7 @@ void NFMDemodGUI::displaySettings() setDcsCode(m_reportedDcsCode); displayStreamIndex(); - restoreState(m_rollupState); + getRollupContents()->restoreState(m_rollupState); blockApplySettings(false); } @@ -611,3 +615,22 @@ void NFMDemodGUI::tick() m_tickCount++; } + +void NFMDemodGUI::makeUIConnections() +{ + QObject::connect(ui->deltaFrequency, &ValueDialZ::changed, this, &NFMDemodGUI::on_deltaFrequency_changed); + QObject::connect(ui->channelSpacingApply, &QPushButton::clicked, this, &NFMDemodGUI::on_channelSpacingApply_clicked); + QObject::connect(ui->rfBW, &QSlider::valueChanged, this, &NFMDemodGUI::on_rfBW_valueChanged); + QObject::connect(ui->afBW, &QSlider::valueChanged, this, &NFMDemodGUI::on_afBW_valueChanged); + QObject::connect(ui->fmDev, &QSlider::valueChanged, this, &NFMDemodGUI::on_fmDev_valueChanged); + QObject::connect(ui->volume, &QDial::valueChanged, this, &NFMDemodGUI::on_volume_valueChanged); + QObject::connect(ui->squelchGate, &QDial::valueChanged, this, &NFMDemodGUI::on_squelchGate_valueChanged); + QObject::connect(ui->deltaSquelch, &ButtonSwitch::toggled, this, &NFMDemodGUI::on_deltaSquelch_toggled); + QObject::connect(ui->squelch, &QDial::valueChanged, this, &NFMDemodGUI::on_squelch_valueChanged); + QObject::connect(ui->ctcss, QOverload::of(&QComboBox::currentIndexChanged), this, &NFMDemodGUI::on_ctcss_currentIndexChanged); + QObject::connect(ui->ctcssOn, &QCheckBox::toggled, this, &NFMDemodGUI::on_ctcssOn_toggled); + QObject::connect(ui->dcsOn, &QCheckBox::toggled, this, &NFMDemodGUI::on_dcsOn_toggled); + QObject::connect(ui->dcsCode, QOverload::of(&QComboBox::currentIndexChanged), this, &NFMDemodGUI::on_dcsCode_currentIndexChanged); + QObject::connect(ui->highPassFilter, &ButtonSwitch::toggled, this, &NFMDemodGUI::on_highPassFilter_toggled); + QObject::connect(ui->audioMute, &QToolButton::toggled, this, &NFMDemodGUI::on_audioMute_toggled); +} diff --git a/plugins/channelrx/demodnfm/nfmdemodgui.h b/plugins/channelrx/demodnfm/nfmdemodgui.h index da1ce7d94..1e82aed46 100644 --- a/plugins/channelrx/demodnfm/nfmdemodgui.h +++ b/plugins/channelrx/demodnfm/nfmdemodgui.h @@ -30,6 +30,12 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } + virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; }; + virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; }; + virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; }; + virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; }; + virtual QString getTitle() const { return m_settings.m_title; }; + virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; public slots: void channelMarkerChangedByCursor(); @@ -63,6 +69,7 @@ private: void setCtcssFreq(Real ctcssFreq); void setDcsCode(unsigned int dcsCode); bool handleMessage(const Message& message); + void makeUIConnections(); void leaveEvent(QEvent*); void enterEvent(QEvent*); diff --git a/plugins/channelrx/demodnfm/nfmdemodgui.ui b/plugins/channelrx/demodnfm/nfmdemodgui.ui index 9dcac0f01..6ff83f5c7 100644 --- a/plugins/channelrx/demodnfm/nfmdemodgui.ui +++ b/plugins/channelrx/demodnfm/nfmdemodgui.ui @@ -1,25 +1,25 @@ NFMDemodGUI - + 0 0 - 364 - 200 + 360 + 197 - + 0 0 - 364 - 200 + 360 + 0 @@ -36,13 +36,13 @@ 0 0 - 362 + 358 191 - 362 + 358 0 @@ -778,17 +778,17 @@ - - RollupWidget - QWidget -
gui/rollupwidget.h
- 1 -
ButtonSwitch QToolButton
gui/buttonswitch.h
+ + RollupContents + QWidget +
gui/rollupcontents.h
+ 1 +
ValueDialZ QWidget diff --git a/plugins/channelrx/demodnfm/nfmdemodsettings.cpp b/plugins/channelrx/demodnfm/nfmdemodsettings.cpp index 3be041951..07160b45c 100644 --- a/plugins/channelrx/demodnfm/nfmdemodsettings.cpp +++ b/plugins/channelrx/demodnfm/nfmdemodsettings.cpp @@ -74,6 +74,7 @@ void NFMDemodSettings::resetToDefaults() m_reverseAPIPort = 8888; m_reverseAPIDeviceIndex = 0; m_reverseAPIChannelIndex = 0; + m_workspaceIndex = 0; } QByteArray NFMDemodSettings::serialize() const @@ -113,6 +114,9 @@ QByteArray NFMDemodSettings::serialize() const s.writeBlob(26, m_rollupState->serialize()); } + s.writeS32(27, m_workspaceIndex); + s.writeBlob(28, m_geometryBytes); + return s.final(); } @@ -182,6 +186,9 @@ bool NFMDemodSettings::deserialize(const QByteArray& data) m_rollupState->deserialize(bytetmp); } + d.readS32(27, &m_workspaceIndex, 0); + d.readBlob(28, &m_geometryBytes); + return true; } else diff --git a/plugins/channelrx/demodnfm/nfmdemodsettings.h b/plugins/channelrx/demodnfm/nfmdemodsettings.h index 3db632a36..fd7da3321 100644 --- a/plugins/channelrx/demodnfm/nfmdemodsettings.h +++ b/plugins/channelrx/demodnfm/nfmdemodsettings.h @@ -54,6 +54,8 @@ struct NFMDemodSettings uint16_t m_reverseAPIPort; uint16_t m_reverseAPIDeviceIndex; uint16_t m_reverseAPIChannelIndex; + int m_workspaceIndex; + QByteArray m_geometryBytes; Serializable *m_channelMarker; Serializable *m_rollupState; diff --git a/plugins/channelrx/demodpacket/packetdemodgui.cpp b/plugins/channelrx/demodpacket/packetdemodgui.cpp index 8b2969a03..ce32d954b 100644 --- a/plugins/channelrx/demodpacket/packetdemodgui.cpp +++ b/plugins/channelrx/demodpacket/packetdemodgui.cpp @@ -253,7 +253,7 @@ void PacketDemodGUI::channelMarkerChangedByCursor() void PacketDemodGUI::channelMarkerHighlightedByCursor() { - setHighlighted(m_channelMarker.getHighlighted()); + getRollupContents()->setHighlighted(m_channelMarker.getHighlighted()); } void PacketDemodGUI::on_deltaFrequency_changed(qint64 value) @@ -369,7 +369,7 @@ void PacketDemodGUI::onWidgetRolled(QWidget* widget, bool rollDown) (void) widget; (void) rollDown; - saveState(m_rollupState); + getRollupContents()->saveState(m_rollupState); applySettings(); } @@ -395,6 +395,7 @@ void PacketDemodGUI::onMenuDialogCalled(const QPoint &p) m_settings.m_reverseAPIChannelIndex = dialog.getReverseAPIChannelIndex(); setWindowTitle(m_settings.m_title); + setTitle(m_channelMarker.getTitle()); setTitleColor(m_settings.m_rgbColor); applySettings(); @@ -426,11 +427,12 @@ PacketDemodGUI::PacketDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, B m_doApplySettings(true), m_tickCount(0) { - ui->setupUi(this); + ui->setupUi(getRollupContents()); + getRollupContents()->arrangeRollups(); m_helpURL = "plugins/channelrx/demodpacket/readme.md"; setAttribute(Qt::WA_DeleteOnClose, true); - connect(this, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + connect(getRollupContents(), SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &))); m_packetDemod = reinterpret_cast(rxChannel); @@ -482,6 +484,7 @@ PacketDemodGUI::PacketDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, B connect(ui->packets->horizontalHeader(), SIGNAL(sectionResized(int, int, int)), SLOT(packets_sectionResized(int, int, int))); displaySettings(); + makeUIConnections(); applySettings(true); } @@ -515,6 +518,7 @@ void PacketDemodGUI::displaySettings() setTitleColor(m_settings.m_rgbColor); setWindowTitle(m_channelMarker.getTitle()); + setTitle(m_channelMarker.getTitle()); blockApplySettings(true); @@ -553,7 +557,7 @@ void PacketDemodGUI::displaySettings() filter(); - restoreState(m_rollupState); + getRollupContents()->restoreState(m_rollupState); blockApplySettings(false); } @@ -682,3 +686,21 @@ void PacketDemodGUI::on_logOpen_clicked() } } } + +void PacketDemodGUI::makeUIConnections() +{ + QObject::connect(ui->deltaFrequency, &ValueDialZ::changed, this, &PacketDemodGUI::on_deltaFrequency_changed); + QObject::connect(ui->mode, QOverload::of(&QComboBox::currentIndexChanged), this, &PacketDemodGUI::on_mode_currentIndexChanged); + QObject::connect(ui->rfBW, &QSlider::valueChanged, this, &PacketDemodGUI::on_rfBW_valueChanged); + QObject::connect(ui->fmDev, &QSlider::valueChanged, this, &PacketDemodGUI::on_fmDev_valueChanged); + QObject::connect(ui->filterFrom, &QLineEdit::editingFinished, this, &PacketDemodGUI::on_filterFrom_editingFinished); + QObject::connect(ui->filterTo, &QLineEdit::editingFinished, this, &PacketDemodGUI::on_filterTo_editingFinished); + QObject::connect(ui->filterPID, &QCheckBox::stateChanged, this, &PacketDemodGUI::on_filterPID_stateChanged); + QObject::connect(ui->clearTable, &QPushButton::clicked, this, &PacketDemodGUI::on_clearTable_clicked); + QObject::connect(ui->udpEnabled, &QCheckBox::clicked, this, &PacketDemodGUI::on_udpEnabled_clicked); + QObject::connect(ui->udpAddress, &QLineEdit::editingFinished, this, &PacketDemodGUI::on_udpAddress_editingFinished); + QObject::connect(ui->udpPort, &QLineEdit::editingFinished, this, &PacketDemodGUI::on_udpPort_editingFinished); + QObject::connect(ui->logEnable, &ButtonSwitch::clicked, this, &PacketDemodGUI::on_logEnable_clicked); + QObject::connect(ui->logFilename, &QToolButton::clicked, this, &PacketDemodGUI::on_logFilename_clicked); + QObject::connect(ui->logOpen, &QToolButton::clicked, this, &PacketDemodGUI::on_logOpen_clicked); +} diff --git a/plugins/channelrx/demodpacket/packetdemodgui.h b/plugins/channelrx/demodpacket/packetdemodgui.h index 0e4bf46ca..6004a4cad 100644 --- a/plugins/channelrx/demodpacket/packetdemodgui.h +++ b/plugins/channelrx/demodpacket/packetdemodgui.h @@ -58,6 +58,12 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } + virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; }; + virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; }; + virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; }; + virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; }; + virtual QString getTitle() const { return m_settings.m_title; }; + virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; public slots: void channelMarkerChangedByCursor(); @@ -88,6 +94,7 @@ private: void displayStreamIndex(); void packetReceived(QByteArray packet); bool handleMessage(const Message& message); + void makeUIConnections(); void leaveEvent(QEvent*); void enterEvent(QEvent*); diff --git a/plugins/channelrx/demodpacket/packetdemodgui.ui b/plugins/channelrx/demodpacket/packetdemodgui.ui index e38e0856f..2fbce3533 100644 --- a/plugins/channelrx/demodpacket/packetdemodgui.ui +++ b/plugins/channelrx/demodpacket/packetdemodgui.ui @@ -1,7 +1,7 @@ PacketDemodGUI - + 0 @@ -713,9 +713,9 @@ - RollupWidget + RollupContents QWidget -
gui/rollupwidget.h
+
gui/rollupcontents.h
1
diff --git a/plugins/channelrx/demodpacket/packetdemodsettings.cpp b/plugins/channelrx/demodpacket/packetdemodsettings.cpp index a44381513..0aa16157b 100644 --- a/plugins/channelrx/demodpacket/packetdemodsettings.cpp +++ b/plugins/channelrx/demodpacket/packetdemodsettings.cpp @@ -53,6 +53,7 @@ void PacketDemodSettings::resetToDefaults() m_reverseAPIPort = 8888; m_reverseAPIDeviceIndex = 0; m_reverseAPIChannelIndex = 0; + m_workspaceIndex = 0; for (int i = 0; i < PACKETDEMOD_COLUMNS; i++) { @@ -95,6 +96,9 @@ QByteArray PacketDemodSettings::serialize() const s.writeBlob(27, m_rollupState->serialize()); } + s.writeS32(28, m_workspaceIndex); + s.writeBlob(29, m_geometryBytes); + for (int i = 0; i < PACKETDEMOD_COLUMNS; i++) { s.writeS32(100 + i, m_columnIndexes[i]); } @@ -173,6 +177,9 @@ bool PacketDemodSettings::deserialize(const QByteArray& data) m_rollupState->deserialize(bytetmp); } + d.readS32(28, &m_workspaceIndex, 0); + d.readBlob(29, &m_geometryBytes); + for (int i = 0; i < PACKETDEMOD_COLUMNS; i++) { d.readS32(100 + i, &m_columnIndexes[i], i); } diff --git a/plugins/channelrx/demodpacket/packetdemodsettings.h b/plugins/channelrx/demodpacket/packetdemodsettings.h index 76e6efa87..77be10352 100644 --- a/plugins/channelrx/demodpacket/packetdemodsettings.h +++ b/plugins/channelrx/demodpacket/packetdemodsettings.h @@ -57,6 +57,8 @@ struct PacketDemodSettings QString m_logFilename; bool m_logEnabled; Serializable *m_rollupState; + int m_workspaceIndex; + QByteArray m_geometryBytes; int m_columnIndexes[PACKETDEMOD_COLUMNS];//!< How the columns are ordered in the table int m_columnSizes[PACKETDEMOD_COLUMNS]; //!< Size of the columns in the table diff --git a/plugins/channelrx/demodpager/pagerdemodgui.cpp b/plugins/channelrx/demodpager/pagerdemodgui.cpp index a71ff7c93..6ef17a027 100644 --- a/plugins/channelrx/demodpager/pagerdemodgui.cpp +++ b/plugins/channelrx/demodpager/pagerdemodgui.cpp @@ -303,7 +303,7 @@ void PagerDemodGUI::channelMarkerChangedByCursor() void PagerDemodGUI::channelMarkerHighlightedByCursor() { - setHighlighted(m_channelMarker.getHighlighted()); + getRollupContents()->setHighlighted(m_channelMarker.getHighlighted()); } void PagerDemodGUI::on_deltaFrequency_changed(qint64 value) @@ -420,7 +420,7 @@ void PagerDemodGUI::onWidgetRolled(QWidget* widget, bool rollDown) } } - saveState(m_rollupState); + getRollupContents()->saveState(m_rollupState); applySettings(); } @@ -446,6 +446,7 @@ void PagerDemodGUI::onMenuDialogCalled(const QPoint &p) m_settings.m_reverseAPIChannelIndex = dialog.getReverseAPIChannelIndex(); setWindowTitle(m_settings.m_title); + setTitle(m_channelMarker.getTitle()); setTitleColor(m_settings.m_rgbColor); applySettings(); @@ -477,11 +478,12 @@ PagerDemodGUI::PagerDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Bas m_doApplySettings(true), m_tickCount(0) { - ui->setupUi(this); + ui->setupUi(getRollupContents()); + getRollupContents()->arrangeRollups(); m_helpURL = "plugins/channelrx/demodpager/readme.md"; setAttribute(Qt::WA_DeleteOnClose, true); - connect(this, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + connect(getRollupContents(), SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &))); m_pagerDemod = reinterpret_cast(rxChannel); @@ -545,6 +547,7 @@ PagerDemodGUI::PagerDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Bas ui->scopeContainer->setVisible(false); displaySettings(); + makeUIConnections(); applySettings(true); } @@ -596,6 +599,7 @@ void PagerDemodGUI::displaySettings() setTitleColor(m_settings.m_rgbColor); setWindowTitle(m_channelMarker.getTitle()); + setTitle(m_channelMarker.getTitle()); blockApplySettings(true); @@ -645,7 +649,7 @@ void PagerDemodGUI::displaySettings() filter(); - restoreState(m_rollupState); + getRollupContents()->restoreState(m_rollupState); blockApplySettings(false); } @@ -797,3 +801,21 @@ void PagerDemodGUI::on_logOpen_clicked() } } } + +void PagerDemodGUI::makeUIConnections() +{ + QObject::connect(ui->deltaFrequency, &ValueDialZ::changed, this, &PagerDemodGUI::on_deltaFrequency_changed); + QObject::connect(ui->rfBW, &QSlider::valueChanged, this, &PagerDemodGUI::on_rfBW_valueChanged); + QObject::connect(ui->fmDev, &QSlider::valueChanged, this, &PagerDemodGUI::on_fmDev_valueChanged); + QObject::connect(ui->baud, QOverload::of(&QComboBox::currentIndexChanged), this, &PagerDemodGUI::on_baud_currentIndexChanged); + QObject::connect(ui->decode, QOverload::of(&QComboBox::currentIndexChanged), this, &PagerDemodGUI::on_decode_currentIndexChanged); + QObject::connect(ui->charset, &QToolButton::clicked, this, &PagerDemodGUI::on_charset_clicked); + QObject::connect(ui->filterAddress, &QLineEdit::editingFinished, this, &PagerDemodGUI::on_filterAddress_editingFinished); + QObject::connect(ui->clearTable, &QToolButton::clicked, this, &PagerDemodGUI::on_clearTable_clicked); + QObject::connect(ui->udpEnabled, &QCheckBox::clicked, this, &PagerDemodGUI::on_udpEnabled_clicked); + QObject::connect(ui->udpAddress, &QLineEdit::editingFinished, this, &PagerDemodGUI::on_udpAddress_editingFinished); + QObject::connect(ui->udpPort, &QLineEdit::editingFinished, this, &PagerDemodGUI::on_udpPort_editingFinished); + QObject::connect(ui->logEnable, &ButtonSwitch::clicked, this, &PagerDemodGUI::on_logEnable_clicked); + QObject::connect(ui->logFilename, &QToolButton::clicked, this, &PagerDemodGUI::on_logFilename_clicked); + QObject::connect(ui->logOpen, &QToolButton::clicked, this, &PagerDemodGUI::on_logOpen_clicked); +} diff --git a/plugins/channelrx/demodpager/pagerdemodgui.h b/plugins/channelrx/demodpager/pagerdemodgui.h index 1ad9ed1d3..d17093c8a 100644 --- a/plugins/channelrx/demodpager/pagerdemodgui.h +++ b/plugins/channelrx/demodpager/pagerdemodgui.h @@ -52,6 +52,12 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } + virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; }; + virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; }; + virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; }; + virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; }; + virtual QString getTitle() const { return m_settings.m_title; }; + virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; public slots: void channelMarkerChangedByCursor(); @@ -84,6 +90,7 @@ private: const QString &numericMessage, const QString &alphaMessage, int evenParityErrors, int bchParityErrors); bool handleMessage(const Message& message); + void makeUIConnections(); void leaveEvent(QEvent*); void enterEvent(QEvent*); diff --git a/plugins/channelrx/demodpager/pagerdemodgui.ui b/plugins/channelrx/demodpager/pagerdemodgui.ui index d5c370095..be681d34a 100644 --- a/plugins/channelrx/demodpager/pagerdemodgui.ui +++ b/plugins/channelrx/demodpager/pagerdemodgui.ui @@ -1,7 +1,7 @@ PagerDemodGUI - + 0 @@ -1013,9 +1013,9 @@ - RollupWidget + RollupContents QWidget -
gui/rollupwidget.h
+
gui/rollupcontents.h
1
diff --git a/plugins/channelrx/demodpager/pagerdemodsettings.cpp b/plugins/channelrx/demodpager/pagerdemodsettings.cpp index 6556548f8..c2fdbb889 100644 --- a/plugins/channelrx/demodpager/pagerdemodsettings.cpp +++ b/plugins/channelrx/demodpager/pagerdemodsettings.cpp @@ -56,6 +56,7 @@ void PagerDemodSettings::resetToDefaults() m_reverseAPIDeviceIndex = 0; m_reverseAPIChannelIndex = 0; m_reverse = false; + m_workspaceIndex = 0; for (int i = 0; i < PAGERDEMOD_MESSAGE_COLUMNS; i++) { @@ -103,6 +104,9 @@ QByteArray PagerDemodSettings::serialize() const s.writeBlob(27, m_rollupState->serialize()); } + s.writeS32(28, m_workspaceIndex); + s.writeBlob(29, m_geometryBytes); + for (int i = 0; i < PAGERDEMOD_MESSAGE_COLUMNS; i++) { s.writeS32(100 + i, m_messageColumnIndexes[i]); } @@ -194,6 +198,9 @@ bool PagerDemodSettings::deserialize(const QByteArray& data) m_rollupState->deserialize(bytetmp); } + d.readS32(28, &m_workspaceIndex, 0); + d.readBlob(29, &m_geometryBytes); + for (int i = 0; i < PAGERDEMOD_MESSAGE_COLUMNS; i++) { d.readS32(100 + i, &m_messageColumnIndexes[i], i); } diff --git a/plugins/channelrx/demodpager/pagerdemodsettings.h b/plugins/channelrx/demodpager/pagerdemodsettings.h index 819f0092f..a56dcd916 100644 --- a/plugins/channelrx/demodpager/pagerdemodsettings.h +++ b/plugins/channelrx/demodpager/pagerdemodsettings.h @@ -67,6 +67,8 @@ struct PagerDemodSettings QString m_logFilename; bool m_logEnabled; Serializable *m_rollupState; + int m_workspaceIndex; + QByteArray m_geometryBytes; int m_messageColumnIndexes[PAGERDEMOD_MESSAGE_COLUMNS];//!< How the columns are ordered in the table int m_messageColumnSizes[PAGERDEMOD_MESSAGE_COLUMNS]; //!< Size of the columns in the table diff --git a/plugins/channelrx/demodradiosonde/radiosondedemodgui.cpp b/plugins/channelrx/demodradiosonde/radiosondedemodgui.cpp index 4481ad3ed..87074c6e3 100644 --- a/plugins/channelrx/demodradiosonde/radiosondedemodgui.cpp +++ b/plugins/channelrx/demodradiosonde/radiosondedemodgui.cpp @@ -350,7 +350,7 @@ void RadiosondeDemodGUI::channelMarkerChangedByCursor() void RadiosondeDemodGUI::channelMarkerHighlightedByCursor() { - setHighlighted(m_channelMarker.getHighlighted()); + getRollupContents()->setHighlighted(m_channelMarker.getHighlighted()); } void RadiosondeDemodGUI::on_deltaFrequency_changed(qint64 value) @@ -477,7 +477,7 @@ void RadiosondeDemodGUI::onWidgetRolled(QWidget* widget, bool rollDown) } } - saveState(m_rollupState); + getRollupContents()->saveState(m_rollupState); applySettings(); } @@ -503,6 +503,7 @@ void RadiosondeDemodGUI::onMenuDialogCalled(const QPoint &p) m_settings.m_reverseAPIChannelIndex = dialog.getReverseAPIChannelIndex(); setWindowTitle(m_settings.m_title); + setTitle(m_channelMarker.getTitle()); setTitleColor(m_settings.m_rgbColor); applySettings(); @@ -534,11 +535,12 @@ RadiosondeDemodGUI::RadiosondeDemodGUI(PluginAPI* pluginAPI, DeviceUISet *device m_doApplySettings(true), m_tickCount(0) { - ui->setupUi(this); + ui->setupUi(getRollupContents()); + getRollupContents()->arrangeRollups(); m_helpURL = "plugins/channelrx/demodradiosonde/readme.md"; setAttribute(Qt::WA_DeleteOnClose, true); - connect(this, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + connect(getRollupContents(), SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &))); m_radiosondeDemod = reinterpret_cast(rxChannel); @@ -634,6 +636,7 @@ RadiosondeDemodGUI::RadiosondeDemodGUI(PluginAPI* pluginAPI, DeviceUISet *device ui->scopeContainer->setVisible(false); displaySettings(); + makeUIConnections(); applySettings(true); } @@ -707,6 +710,7 @@ void RadiosondeDemodGUI::displaySettings() setTitleColor(m_settings.m_rgbColor); setWindowTitle(m_channelMarker.getTitle()); + setTitle(m_channelMarker.getTitle()); blockApplySettings(true); @@ -749,7 +753,7 @@ void RadiosondeDemodGUI::displaySettings() filter(); - restoreState(m_rollupState); + getRollupContents()->restoreState(m_rollupState); blockApplySettings(false); } @@ -893,3 +897,20 @@ void RadiosondeDemodGUI::on_logOpen_clicked() } } } + +void RadiosondeDemodGUI::makeUIConnections() +{ + QObject::connect(ui->deltaFrequency, &ValueDialZ::changed, this, &RadiosondeDemodGUI::on_deltaFrequency_changed); + QObject::connect(ui->rfBW, &QSlider::valueChanged, this, &RadiosondeDemodGUI::on_rfBW_valueChanged); + QObject::connect(ui->fmDev, &QSlider::valueChanged, this, &RadiosondeDemodGUI::on_fmDev_valueChanged); + QObject::connect(ui->threshold, &QDial::valueChanged, this, &RadiosondeDemodGUI::on_threshold_valueChanged); + QObject::connect(ui->filterSerial, &QLineEdit::editingFinished, this, &RadiosondeDemodGUI::on_filterSerial_editingFinished); + QObject::connect(ui->clearTable, &QPushButton::clicked, this, &RadiosondeDemodGUI::on_clearTable_clicked); + QObject::connect(ui->udpEnabled, &QCheckBox::clicked, this, &RadiosondeDemodGUI::on_udpEnabled_clicked); + QObject::connect(ui->udpAddress, &QLineEdit::editingFinished, this, &RadiosondeDemodGUI::on_udpAddress_editingFinished); + QObject::connect(ui->udpPort, &QLineEdit::editingFinished, this, &RadiosondeDemodGUI::on_udpPort_editingFinished); + QObject::connect(ui->frames, &QTableWidget::cellDoubleClicked, this, &RadiosondeDemodGUI::on_frames_cellDoubleClicked); + QObject::connect(ui->logEnable, &ButtonSwitch::clicked, this, &RadiosondeDemodGUI::on_logEnable_clicked); + QObject::connect(ui->logFilename, &QToolButton::clicked, this, &RadiosondeDemodGUI::on_logFilename_clicked); + QObject::connect(ui->logOpen, &QToolButton::clicked, this, &RadiosondeDemodGUI::on_logOpen_clicked); +} diff --git a/plugins/channelrx/demodradiosonde/radiosondedemodgui.h b/plugins/channelrx/demodradiosonde/radiosondedemodgui.h index 053802350..4db42e013 100644 --- a/plugins/channelrx/demodradiosonde/radiosondedemodgui.h +++ b/plugins/channelrx/demodradiosonde/radiosondedemodgui.h @@ -54,6 +54,12 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } + virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; }; + virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; }; + virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; }; + virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; }; + virtual QString getTitle() const { return m_settings.m_title; }; + virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; public slots: void channelMarkerChangedByCursor(); @@ -87,6 +93,7 @@ private: void displayStreamIndex(); void frameReceived(const QByteArray& frame, const QDateTime& dateTime, int errorsCorrected, int threshold); bool handleMessage(const Message& message); + void makeUIConnections(); void leaveEvent(QEvent*); void enterEvent(QEvent*); diff --git a/plugins/channelrx/demodradiosonde/radiosondedemodgui.ui b/plugins/channelrx/demodradiosonde/radiosondedemodgui.ui index f8624865b..3edf4d26c 100644 --- a/plugins/channelrx/demodradiosonde/radiosondedemodgui.ui +++ b/plugins/channelrx/demodradiosonde/radiosondedemodgui.ui @@ -1,7 +1,7 @@ RadiosondeDemodGUI - + 0 @@ -1055,9 +1055,9 @@ - RollupWidget + RollupContents QWidget -
gui/rollupwidget.h
+
gui/rollupcontents.h
1
diff --git a/plugins/channelrx/demodradiosonde/radiosondedemodsettings.cpp b/plugins/channelrx/demodradiosonde/radiosondedemodsettings.cpp index 03d22fb0d..64f9d163a 100644 --- a/plugins/channelrx/demodradiosonde/radiosondedemodsettings.cpp +++ b/plugins/channelrx/demodradiosonde/radiosondedemodsettings.cpp @@ -54,6 +54,7 @@ void RadiosondeDemodSettings::resetToDefaults() m_reverseAPIPort = 8888; m_reverseAPIDeviceIndex = 0; m_reverseAPIChannelIndex = 0; + m_workspaceIndex = 0; for (int i = 0; i < RADIOSONDEDEMOD_FRAME_COLUMNS; i++) { @@ -98,6 +99,9 @@ QByteArray RadiosondeDemodSettings::serialize() const s.writeBlob(25, m_rollupState->serialize()); } + s.writeS32(26, m_workspaceIndex); + s.writeBlob(27, m_geometryBytes); + for (int i = 0; i < RADIOSONDEDEMOD_FRAME_COLUMNS; i++) s.writeS32(100 + i, m_frameColumnIndexes[i]); for (int i = 0; i < RADIOSONDEDEMOD_FRAME_COLUMNS; i++) @@ -180,6 +184,9 @@ bool RadiosondeDemodSettings::deserialize(const QByteArray& data) m_rollupState->deserialize(bytetmp); } + d.readS32(26, &m_workspaceIndex, 0); + d.readBlob(27, &m_geometryBytes); + for (int i = 0; i < RADIOSONDEDEMOD_FRAME_COLUMNS; i++) { d.readS32(100 + i, &m_frameColumnIndexes[i], i); } diff --git a/plugins/channelrx/demodradiosonde/radiosondedemodsettings.h b/plugins/channelrx/demodradiosonde/radiosondedemodsettings.h index 76a4fe839..416dbf7f7 100644 --- a/plugins/channelrx/demodradiosonde/radiosondedemodsettings.h +++ b/plugins/channelrx/demodradiosonde/radiosondedemodsettings.h @@ -57,6 +57,8 @@ struct RadiosondeDemodSettings uint16_t m_reverseAPIChannelIndex; Serializable *m_scopeGUI; Serializable *m_rollupState; + int m_workspaceIndex; + QByteArray m_geometryBytes; int m_frameColumnIndexes[RADIOSONDEDEMOD_FRAME_COLUMNS];//!< How the columns are ordered in the table int m_frameColumnSizes[RADIOSONDEDEMOD_FRAME_COLUMNS]; //!< Size of the columns in the table diff --git a/plugins/channelrx/demodssb/ssbdemodgui.cpp b/plugins/channelrx/demodssb/ssbdemodgui.cpp index 177a3a6d8..511ca5358 100644 --- a/plugins/channelrx/demodssb/ssbdemodgui.cpp +++ b/plugins/channelrx/demodssb/ssbdemodgui.cpp @@ -112,7 +112,7 @@ void SSBDemodGUI::channelMarkerChangedByCursor() void SSBDemodGUI::channelMarkerHighlightedByCursor() { - setHighlighted(m_channelMarker.getHighlighted()); + getRollupContents()->setHighlighted(m_channelMarker.getHighlighted()); } void SSBDemodGUI::on_audioBinaural_toggled(bool binaural) @@ -249,6 +249,7 @@ void SSBDemodGUI::onMenuDialogCalled(const QPoint &p) m_settings.m_reverseAPIChannelIndex = dialog.getReverseAPIChannelIndex(); setWindowTitle(m_settings.m_title); + setTitle(m_channelMarker.getTitle()); setTitleColor(m_settings.m_rgbColor); applySettings(); @@ -276,7 +277,7 @@ void SSBDemodGUI::onWidgetRolled(QWidget* widget, bool rollDown) (void) widget; (void) rollDown; - saveState(m_rollupState); + getRollupContents()->saveState(m_rollupState); applySettings(); } @@ -294,10 +295,11 @@ SSBDemodGUI::SSBDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Baseban m_squelchOpen(false), m_audioSampleRate(-1) { - ui->setupUi(this); + ui->setupUi(getRollupContents()); + getRollupContents()->arrangeRollups(); m_helpURL = "plugins/channelrx/demodssb/readme.md"; setAttribute(Qt::WA_DeleteOnClose, true); - connect(this, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + connect(getRollupContents(), SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &))); m_ssbDemod = (SSBDemod*) rxChannel; @@ -352,6 +354,8 @@ SSBDemodGUI::SSBDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Baseban ui->BW->setMaximum(480); ui->lowCut->setMaximum(480); displaySettings(); + makeUIConnections(); + applyBandwidths(m_settings.m_spanLog2, true); // does applySettings(true) } @@ -520,6 +524,7 @@ void SSBDemodGUI::displaySettings() setTitleColor(m_settings.m_rgbColor); setWindowTitle(m_channelMarker.getTitle()); + setTitle(m_channelMarker.getTitle()); blockApplySettings(true); @@ -575,7 +580,7 @@ void SSBDemodGUI::displaySettings() displayStreamIndex(); - restoreState(m_rollupState); + getRollupContents()->restoreState(m_rollupState); blockApplySettings(false); } @@ -673,3 +678,21 @@ void SSBDemodGUI::tick() m_tickCount++; } + +void SSBDemodGUI::makeUIConnections() +{ + QObject::connect(ui->deltaFrequency, &ValueDialZ::changed, this, &SSBDemodGUI::on_deltaFrequency_changed); + QObject::connect(ui->audioBinaural, &QToolButton::toggled, this, &SSBDemodGUI::on_audioBinaural_toggled); + QObject::connect(ui->audioFlipChannels, &QToolButton::toggled, this, &SSBDemodGUI::on_audioFlipChannels_toggled); + QObject::connect(ui->dsb, &QToolButton::toggled, this, &SSBDemodGUI::on_dsb_toggled); + QObject::connect(ui->BW, &TickedSlider::valueChanged, this, &SSBDemodGUI::on_BW_valueChanged); + QObject::connect(ui->lowCut, &TickedSlider::valueChanged, this, &SSBDemodGUI::on_lowCut_valueChanged); + QObject::connect(ui->volume, &QDial::valueChanged, this, &SSBDemodGUI::on_volume_valueChanged); + QObject::connect(ui->agc, &ButtonSwitch::toggled, this, &SSBDemodGUI::on_agc_toggled); + QObject::connect(ui->agcClamping, &ButtonSwitch::toggled, this, &SSBDemodGUI::on_agcClamping_toggled); + QObject::connect(ui->agcPowerThreshold, &QDial::valueChanged, this, &SSBDemodGUI::on_agcPowerThreshold_valueChanged); + QObject::connect(ui->agcThresholdGate, &QDial::valueChanged, this, &SSBDemodGUI::on_agcThresholdGate_valueChanged); + QObject::connect(ui->audioMute, &QToolButton::toggled, this, &SSBDemodGUI::on_audioMute_toggled); + QObject::connect(ui->spanLog2, &QSlider::valueChanged, this, &SSBDemodGUI::on_spanLog2_valueChanged); + QObject::connect(ui->flipSidebands, &QPushButton::clicked, this, &SSBDemodGUI::on_flipSidebands_clicked); +} diff --git a/plugins/channelrx/demodssb/ssbdemodgui.h b/plugins/channelrx/demodssb/ssbdemodgui.h index 2867f51c9..b7fca1d20 100644 --- a/plugins/channelrx/demodssb/ssbdemodgui.h +++ b/plugins/channelrx/demodssb/ssbdemodgui.h @@ -33,6 +33,12 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } + virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; }; + virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; }; + virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; }; + virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; }; + virtual QString getTitle() const { return m_settings.m_title; }; + virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; public slots: void channelMarkerChangedByCursor(); @@ -73,6 +79,7 @@ private: void displayAGCPowerThreshold(int value); void displayAGCThresholdGate(int value); bool handleMessage(const Message& message); + void makeUIConnections(); void leaveEvent(QEvent*); void enterEvent(QEvent*); diff --git a/plugins/channelrx/demodssb/ssbdemodgui.ui b/plugins/channelrx/demodssb/ssbdemodgui.ui index 9eaad6728..1bc6325e8 100644 --- a/plugins/channelrx/demodssb/ssbdemodgui.ui +++ b/plugins/channelrx/demodssb/ssbdemodgui.ui @@ -1,7 +1,7 @@ SSBDemodGUI - + 0 @@ -11,7 +11,7 @@ - + 0 0 @@ -530,6 +530,7 @@ + Liberation Sans 8 @@ -551,6 +552,7 @@
+ Liberation Sans 8 @@ -563,6 +565,7 @@ + Liberation Sans 8 @@ -584,6 +587,7 @@
+ Liberation Sans 8 @@ -599,6 +603,7 @@ + Liberation Sans 8 @@ -620,6 +625,7 @@
+ Liberation Sans 8 @@ -915,6 +921,12 @@ 284
+ + + 0 + 0 + + Channel Spectrum @@ -936,6 +948,12 @@
+ + + 0 + 0 + + 200 @@ -958,9 +976,20 @@ - RollupWidget + ButtonSwitch + QToolButton +
gui/buttonswitch.h
+
+ + RollupContents QWidget -
gui/rollupwidget.h
+
gui/rollupcontents.h
+ 1 +
+ + ValueDialZ + QWidget +
gui/valuedialz.h
1
@@ -981,17 +1010,6 @@
gui/glspectrumgui.h
1
- - ValueDialZ - QWidget -
gui/valuedialz.h
- 1 -
- - ButtonSwitch - QToolButton -
gui/buttonswitch.h
-
TickedSlider QSlider diff --git a/plugins/channelrx/demodssb/ssbdemodsettings.cpp b/plugins/channelrx/demodssb/ssbdemodsettings.cpp index e980a5df3..476f88930 100644 --- a/plugins/channelrx/demodssb/ssbdemodsettings.cpp +++ b/plugins/channelrx/demodssb/ssbdemodsettings.cpp @@ -63,6 +63,7 @@ void SSBDemodSettings::resetToDefaults() m_reverseAPIPort = 8888; m_reverseAPIDeviceIndex = 0; m_reverseAPIChannelIndex = 0; + m_workspaceIndex = 0; } QByteArray SSBDemodSettings::serialize() const @@ -100,6 +101,9 @@ QByteArray SSBDemodSettings::serialize() const s.writeBlob(24, m_rollupState->serialize()); } + s.writeS32(25, m_workspaceIndex); + s.writeBlob(26, m_geometryBytes); + return s.final(); } @@ -168,6 +172,9 @@ bool SSBDemodSettings::deserialize(const QByteArray& data) m_rollupState->deserialize(bytetmp); } + d.readS32(25, &m_workspaceIndex, 0); + d.readBlob(26, &m_geometryBytes); + return true; } else diff --git a/plugins/channelrx/demodssb/ssbdemodsettings.h b/plugins/channelrx/demodssb/ssbdemodsettings.h index 9e9de931a..9ecd1a637 100644 --- a/plugins/channelrx/demodssb/ssbdemodsettings.h +++ b/plugins/channelrx/demodssb/ssbdemodsettings.h @@ -47,6 +47,8 @@ struct SSBDemodSettings uint16_t m_reverseAPIPort; uint16_t m_reverseAPIDeviceIndex; uint16_t m_reverseAPIChannelIndex; + int m_workspaceIndex; + QByteArray m_geometryBytes; Serializable *m_channelMarker; Serializable *m_spectrumGUI; diff --git a/plugins/channelrx/demodvor/vordemodgui.cpp b/plugins/channelrx/demodvor/vordemodgui.cpp index 6cf3bec8a..5ae5d0720 100644 --- a/plugins/channelrx/demodvor/vordemodgui.cpp +++ b/plugins/channelrx/demodvor/vordemodgui.cpp @@ -887,7 +887,7 @@ void VORDemodGUI::channelMarkerChangedByCursor() void VORDemodGUI::channelMarkerHighlightedByCursor() { - setHighlighted(m_channelMarker.getHighlighted()); + getRollupContents()->setHighlighted(m_channelMarker.getHighlighted()); } void VORDemodGUI::on_thresh_valueChanged(int value) @@ -1105,7 +1105,7 @@ void VORDemodGUI::onWidgetRolled(QWidget* widget, bool rollDown) (void) widget; (void) rollDown; - saveState(m_rollupState); + getRollupContents()->saveState(m_rollupState); applySettings(); } @@ -1131,6 +1131,7 @@ void VORDemodGUI::onMenuDialogCalled(const QPoint &p) m_settings.m_reverseAPIChannelIndex = dialog.getReverseAPIChannelIndex(); setWindowTitle(m_settings.m_title); + setTitle(m_channelMarker.getTitle()); setTitleColor(m_settings.m_rgbColor); applySettings(); @@ -1166,7 +1167,8 @@ VORDemodGUI::VORDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Baseban m_vorModel(this), m_vors(nullptr) { - ui->setupUi(this); + ui->setupUi(getRollupContents()); + getRollupContents()->arrangeRollups(); m_helpURL = "plugins/channelrx/demodvor/readme.md"; ui->map->rootContext()->setContextProperty("vorModel", &m_vorModel); @@ -1176,7 +1178,7 @@ VORDemodGUI::VORDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Baseban m_muteIcon.addPixmap(QPixmap("://sound_on.png"), QIcon::Normal, QIcon::Off); setAttribute(Qt::WA_DeleteOnClose, true); - connect(this, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + connect(getRollupContents(), SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &))); connect(&m_dlm, &HttpDownloadManager::downloadComplete, this, &VORDemodGUI::downloadFinished); @@ -1272,6 +1274,7 @@ VORDemodGUI::VORDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Baseban connect(ui->vorData->horizontalHeader(), SIGNAL(sectionResized(int, int, int)), SLOT(vorData_sectionResized(int, int, int))); displaySettings(); + makeUIConnections(); applySettings(true); } @@ -1305,6 +1308,7 @@ void VORDemodGUI::displaySettings() setTitleColor(m_settings.m_rgbColor); setWindowTitle(m_channelMarker.getTitle()); + setTitle(m_channelMarker.getTitle()); blockApplySettings(true); @@ -1333,7 +1337,7 @@ void VORDemodGUI::displaySettings() header->moveSection(header->visualIndex(i), m_settings.m_columnIndexes[i]); } - restoreState(m_rollupState); + getRollupContents()->restoreState(m_rollupState); blockApplySettings(false); } @@ -1425,3 +1429,15 @@ void VORDemodGUI::tick() m_tickCount++; } + +void VORDemodGUI::makeUIConnections() +{ + QObject::connect(ui->audioMute, &QToolButton::toggled, this, &VORDemodGUI::on_audioMute_toggled); + QObject::connect(ui->thresh, &QDial::valueChanged, this, &VORDemodGUI::on_thresh_valueChanged); + QObject::connect(ui->volume, &QDial::valueChanged, this, &VORDemodGUI::on_volume_valueChanged); + QObject::connect(ui->squelch, &QDial::valueChanged, this, &VORDemodGUI::on_squelch_valueChanged); + QObject::connect(ui->audioMute, &QToolButton::toggled, this, &VORDemodGUI::on_audioMute_toggled); + QObject::connect(ui->getOurAirportsVORDB, &QPushButton::clicked, this, &VORDemodGUI::on_getOurAirportsVORDB_clicked); + QObject::connect(ui->getOpenAIPVORDB, &QPushButton::clicked, this, &VORDemodGUI::on_getOpenAIPVORDB_clicked); + QObject::connect(ui->magDecAdjust, &QPushButton::clicked, this, &VORDemodGUI::on_magDecAdjust_clicked); +} diff --git a/plugins/channelrx/demodvor/vordemodgui.h b/plugins/channelrx/demodvor/vordemodgui.h index 4d3238204..950d040d6 100644 --- a/plugins/channelrx/demodvor/vordemodgui.h +++ b/plugins/channelrx/demodvor/vordemodgui.h @@ -211,6 +211,13 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } + virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; }; + virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; }; + virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; }; + virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; }; + virtual QString getTitle() const { return m_settings.m_title; }; + virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; + void selectVOR(VORGUI *vorGUI, bool selected); public slots: @@ -253,6 +260,7 @@ private: void displaySettings(); void displayStreamIndex(); bool handleMessage(const Message& message); + void makeUIConnections(); void leaveEvent(QEvent*); void enterEvent(QEvent*); diff --git a/plugins/channelrx/demodvor/vordemodgui.ui b/plugins/channelrx/demodvor/vordemodgui.ui index d93557091..ba95565bd 100644 --- a/plugins/channelrx/demodvor/vordemodgui.ui +++ b/plugins/channelrx/demodvor/vordemodgui.ui @@ -1,7 +1,7 @@ VORDemodGUI - + 0 @@ -593,9 +593,9 @@
QtQuickWidgets/QQuickWidget
- RollupWidget + RollupContents QWidget -
gui/rollupwidget.h
+
gui/rollupcontents.h
1
diff --git a/plugins/channelrx/demodvor/vordemodsettings.cpp b/plugins/channelrx/demodvor/vordemodsettings.cpp index 0c181bdc5..68d7c8c8d 100644 --- a/plugins/channelrx/demodvor/vordemodsettings.cpp +++ b/plugins/channelrx/demodvor/vordemodsettings.cpp @@ -44,6 +44,7 @@ void VORDemodSettings::resetToDefaults() m_reverseAPIPort = 8888; m_reverseAPIDeviceIndex = 0; m_reverseAPIChannelIndex = 0; + m_workspaceIndex = 0; m_identThreshold = 2.0; m_refThresholdDB = -45.0; @@ -85,6 +86,9 @@ QByteArray VORDemodSettings::serialize() const s.writeBlob(24, m_rollupState->serialize()); } + s.writeS32(25, m_workspaceIndex); + s.writeBlob(26, m_geometryBytes); + for (int i = 0; i < VORDEMOD_COLUMNS; i++) { s.writeS32(100 + i, m_columnIndexes[i]); } @@ -153,6 +157,9 @@ bool VORDemodSettings::deserialize(const QByteArray& data) m_rollupState->deserialize(bytetmp); } + d.readS32(25, &m_workspaceIndex, 0); + d.readBlob(26, &m_geometryBytes); + for (int i = 0; i < VORDEMOD_COLUMNS; i++) { d.readS32(100 + i, &m_columnIndexes[i], i); } diff --git a/plugins/channelrx/demodvor/vordemodsettings.h b/plugins/channelrx/demodvor/vordemodsettings.h index 0855dde72..4f121a2ec 100644 --- a/plugins/channelrx/demodvor/vordemodsettings.h +++ b/plugins/channelrx/demodvor/vordemodsettings.h @@ -54,6 +54,8 @@ struct VORDemodSettings Real m_varThresholdDB; //!< Threshold in dB for valid VOR variable signal bool m_magDecAdjust; //!< Adjust for magnetic declination when drawing radials on the map Serializable *m_rollupState; + int m_workspaceIndex; + QByteArray m_geometryBytes; int m_columnIndexes[VORDEMOD_COLUMNS];//!< How the columns are ordered in the table int m_columnSizes[VORDEMOD_COLUMNS]; //!< Size of the coumns in the table diff --git a/plugins/channelrx/demodvorsc/vordemodscgui.cpp b/plugins/channelrx/demodvorsc/vordemodscgui.cpp index ffaf706a4..361152963 100644 --- a/plugins/channelrx/demodvorsc/vordemodscgui.cpp +++ b/plugins/channelrx/demodvorsc/vordemodscgui.cpp @@ -181,7 +181,7 @@ void VORDemodSCGUI::channelMarkerChangedByCursor() void VORDemodSCGUI::channelMarkerHighlightedByCursor() { - setHighlighted(m_channelMarker.getHighlighted()); + getRollupContents()->setHighlighted(m_channelMarker.getHighlighted()); } void VORDemodSCGUI::on_deltaFrequency_changed(qint64 value) @@ -223,7 +223,7 @@ void VORDemodSCGUI::onWidgetRolled(QWidget* widget, bool rollDown) (void) widget; (void) rollDown; - saveState(m_rollupState); + getRollupContents()->saveState(m_rollupState); applySettings(); } @@ -249,6 +249,7 @@ void VORDemodSCGUI::onMenuDialogCalled(const QPoint &p) m_settings.m_reverseAPIChannelIndex = dialog.getReverseAPIChannelIndex(); setWindowTitle(m_settings.m_title); + setTitle(m_channelMarker.getTitle()); setTitleColor(m_settings.m_rgbColor); applySettings(); @@ -281,11 +282,12 @@ VORDemodSCGUI::VORDemodSCGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Bas m_squelchOpen(false), m_tickCount(0) { - ui->setupUi(this); + ui->setupUi(getRollupContents()); + getRollupContents()->arrangeRollups(); m_helpURL = "plugins/channelrx/demodvorsc/readme.md"; setAttribute(Qt::WA_DeleteOnClose, true); - connect(this, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + connect(getRollupContents(), SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &))); m_vorDemod = reinterpret_cast(rxChannel); @@ -321,6 +323,7 @@ VORDemodSCGUI::VORDemodSCGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Bas connect(getInputMessageQueue(), SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages())); displaySettings(); + makeUIConnections(); applySettings(true); } @@ -354,6 +357,7 @@ void VORDemodSCGUI::displaySettings() setTitleColor(m_settings.m_rgbColor); setWindowTitle(m_channelMarker.getTitle()); + setTitle(m_channelMarker.getTitle()); blockApplySettings(true); @@ -372,7 +376,7 @@ void VORDemodSCGUI::displaySettings() displayStreamIndex(); - restoreState(m_rollupState); + getRollupContents()->restoreState(m_rollupState); blockApplySettings(false); } @@ -443,3 +447,12 @@ void VORDemodSCGUI::tick() m_tickCount++; } + +void VORDemodSCGUI::makeUIConnections() +{ + QObject::connect(ui->deltaFrequency, &ValueDialZ::changed, this, &VORDemodSCGUI::on_deltaFrequency_changed); + QObject::connect(ui->thresh, &QDial::valueChanged, this, &VORDemodSCGUI::on_thresh_valueChanged); + QObject::connect(ui->volume, &QDial::valueChanged, this, &VORDemodSCGUI::on_volume_valueChanged); + QObject::connect(ui->squelch, &QDial::valueChanged, this, &VORDemodSCGUI::on_squelch_valueChanged); + QObject::connect(ui->audioMute, &QToolButton::toggled, this, &VORDemodSCGUI::on_audioMute_toggled); +} diff --git a/plugins/channelrx/demodvorsc/vordemodscgui.h b/plugins/channelrx/demodvorsc/vordemodscgui.h index 97ac65a13..460cddd2d 100644 --- a/plugins/channelrx/demodvorsc/vordemodscgui.h +++ b/plugins/channelrx/demodvorsc/vordemodscgui.h @@ -48,6 +48,12 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } + virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; }; + virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; }; + virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; }; + virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; }; + virtual QString getTitle() const { return m_settings.m_title; }; + virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; public slots: void channelMarkerChangedByCursor(); @@ -76,6 +82,7 @@ private: void displaySettings(); void displayStreamIndex(); bool handleMessage(const Message& message); + void makeUIConnections(); void leaveEvent(QEvent*); void enterEvent(QEvent*); diff --git a/plugins/channelrx/demodvorsc/vordemodscgui.ui b/plugins/channelrx/demodvorsc/vordemodscgui.ui index 979ba148e..e406cd4c2 100644 --- a/plugins/channelrx/demodvorsc/vordemodscgui.ui +++ b/plugins/channelrx/demodvorsc/vordemodscgui.ui @@ -1,7 +1,7 @@ VORDemodSCGUI - + 0 @@ -614,9 +614,9 @@ - RollupWidget + RollupContents QWidget -
gui/rollupwidget.h
+
gui/rollupcontents.h
1
diff --git a/plugins/channelrx/demodvorsc/vordemodscsettings.cpp b/plugins/channelrx/demodvorsc/vordemodscsettings.cpp index 3d4dab2b6..5f1cd2eb5 100644 --- a/plugins/channelrx/demodvorsc/vordemodscsettings.cpp +++ b/plugins/channelrx/demodvorsc/vordemodscsettings.cpp @@ -46,6 +46,7 @@ void VORDemodSCSettings::resetToDefaults() m_reverseAPIPort = 8888; m_reverseAPIDeviceIndex = 0; m_reverseAPIChannelIndex = 0; + m_workspaceIndex = 0; m_identThreshold = 2.0; m_refThresholdDB = -45.0; @@ -80,6 +81,9 @@ QByteArray VORDemodSCSettings::serialize() const s.writeBlob(23, m_rollupState->serialize()); } + s.writeS32(24, m_workspaceIndex); + s.writeBlob(25, m_geometryBytes); + return s.final(); } @@ -140,6 +144,9 @@ bool VORDemodSCSettings::deserialize(const QByteArray& data) m_rollupState->deserialize(bytetmp); } + d.readS32(24, &m_workspaceIndex, 0); + d.readBlob(25, &m_geometryBytes); + return true; } else diff --git a/plugins/channelrx/demodvorsc/vordemodscsettings.h b/plugins/channelrx/demodvorsc/vordemodscsettings.h index 5a9a6f898..a5e1ac962 100644 --- a/plugins/channelrx/demodvorsc/vordemodscsettings.h +++ b/plugins/channelrx/demodvorsc/vordemodscsettings.h @@ -46,6 +46,8 @@ struct VORDemodSCSettings Real m_refThresholdDB; //!< Threshold in dB for valid VOR reference signal Real m_varThresholdDB; //!< Threshold in dB for valid VOR variable signal Serializable *m_rollupState; + int m_workspaceIndex; + QByteArray m_geometryBytes; // Highest frequency is the FM subcarrier at up to ~11kHz // However, old VORs can have 0.005% frequency offset, which is 6kHz diff --git a/plugins/channelrx/demodwfm/wfmdemodgui.cpp b/plugins/channelrx/demodwfm/wfmdemodgui.cpp index 0781bbbd0..723c4b43f 100644 --- a/plugins/channelrx/demodwfm/wfmdemodgui.cpp +++ b/plugins/channelrx/demodwfm/wfmdemodgui.cpp @@ -95,7 +95,7 @@ void WFMDemodGUI::channelMarkerChangedByCursor() void WFMDemodGUI::channelMarkerHighlightedByCursor() { - setHighlighted(m_channelMarker.getHighlighted()); + getRollupContents()->setHighlighted(m_channelMarker.getHighlighted()); } void WFMDemodGUI::on_deltaFrequency_changed(qint64 value) @@ -144,7 +144,7 @@ void WFMDemodGUI::onWidgetRolled(QWidget* widget, bool rollDown) (void) widget; (void) rollDown; - saveState(m_rollupState); + getRollupContents()->saveState(m_rollupState); applySettings(); } @@ -172,6 +172,7 @@ void WFMDemodGUI::onMenuDialogCalled(const QPoint &p) m_settings.m_reverseAPIChannelIndex = dialog.getReverseAPIChannelIndex(); setWindowTitle(m_settings.m_title); + setTitle(m_channelMarker.getTitle()); setTitleColor(m_settings.m_rgbColor); applySettings(); @@ -204,10 +205,11 @@ WFMDemodGUI::WFMDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Baseban m_squelchOpen(false), m_audioSampleRate(-1) { - ui->setupUi(this); + ui->setupUi(getRollupContents()); + getRollupContents()->arrangeRollups(); m_helpURL = "plugins/channelrx/demodwfm/readme.md"; setAttribute(Qt::WA_DeleteOnClose, true); - connect(this, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + connect(getRollupContents(), SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &))); connect(getInputMessageQueue(), SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages())); @@ -246,6 +248,7 @@ WFMDemodGUI::WFMDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Baseban connect(&m_channelMarker, SIGNAL(highlightedByCursor()), this, SLOT(channelMarkerHighlightedByCursor())); displaySettings(); + makeUIConnections(); applySettings(true); } @@ -279,6 +282,7 @@ void WFMDemodGUI::displaySettings() setTitleColor(m_settings.m_rgbColor); setWindowTitle(m_channelMarker.getTitle()); + setTitle(m_channelMarker.getTitle()); blockApplySettings(true); @@ -294,7 +298,7 @@ void WFMDemodGUI::displaySettings() displayStreamIndex(); - restoreState(m_rollupState); + getRollupContents()->restoreState(m_rollupState); blockApplySettings(false); } @@ -361,3 +365,13 @@ void WFMDemodGUI::tick() m_squelchOpen = squelchOpen; } } + +void WFMDemodGUI::makeUIConnections() +{ + QObject::connect(ui->deltaFrequency, &ValueDialZ::changed, this, &WFMDemodGUI::on_deltaFrequency_changed); + QObject::connect(ui->rfBW, &ValueDial::changed, this, &WFMDemodGUI::on_rfBW_changed); + QObject::connect(ui->afBW, &QSlider::valueChanged, this, &WFMDemodGUI::on_afBW_valueChanged); + QObject::connect(ui->volume, &QSlider::valueChanged, this, &WFMDemodGUI::on_volume_valueChanged); + QObject::connect(ui->squelch, &QSlider::valueChanged, this, &WFMDemodGUI::on_squelch_valueChanged); + QObject::connect(ui->audioMute, &QToolButton::toggled, this, &WFMDemodGUI::on_audioMute_toggled); +} diff --git a/plugins/channelrx/demodwfm/wfmdemodgui.h b/plugins/channelrx/demodwfm/wfmdemodgui.h index e8205df57..dfd2c3502 100644 --- a/plugins/channelrx/demodwfm/wfmdemodgui.h +++ b/plugins/channelrx/demodwfm/wfmdemodgui.h @@ -28,6 +28,12 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } + virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; }; + virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; }; + virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; }; + virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; }; + virtual QString getTitle() const { return m_settings.m_title; }; + virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; public slots: void channelMarkerChangedByCursor(); @@ -57,6 +63,7 @@ private: void displaySettings(); void displayStreamIndex(); bool handleMessage(const Message& message); + void makeUIConnections(); void leaveEvent(QEvent*); void enterEvent(QEvent*); diff --git a/plugins/channelrx/demodwfm/wfmdemodgui.ui b/plugins/channelrx/demodwfm/wfmdemodgui.ui index 05e404bed..d6ef37d7e 100644 --- a/plugins/channelrx/demodwfm/wfmdemodgui.ui +++ b/plugins/channelrx/demodwfm/wfmdemodgui.ui @@ -1,7 +1,7 @@ WFMDemodGUI - + 0 @@ -436,9 +436,9 @@ - RollupWidget + RollupContents QWidget -
gui/rollupwidget.h
+
gui/rollupcontents.h
1
diff --git a/plugins/channelrx/demodwfm/wfmdemodsettings.cpp b/plugins/channelrx/demodwfm/wfmdemodsettings.cpp index 077f4fe5a..779096e82 100644 --- a/plugins/channelrx/demodwfm/wfmdemodsettings.cpp +++ b/plugins/channelrx/demodwfm/wfmdemodsettings.cpp @@ -52,6 +52,7 @@ void WFMDemodSettings::resetToDefaults() m_reverseAPIPort = 8888; m_reverseAPIDeviceIndex = 0; m_reverseAPIChannelIndex = 0; + m_workspaceIndex = 0; } QByteArray WFMDemodSettings::serialize() const @@ -81,6 +82,9 @@ QByteArray WFMDemodSettings::serialize() const s.writeBlob(18, m_rollupState->serialize()); } + s.writeS32(19, m_workspaceIndex); + s.writeBlob(20, m_geometryBytes); + return s.final(); } @@ -143,6 +147,9 @@ bool WFMDemodSettings::deserialize(const QByteArray& data) m_rollupState->deserialize(bytetmp); } + d.readS32(19, &m_workspaceIndex, 0); + d.readBlob(20, &m_geometryBytes); + return true; } else diff --git a/plugins/channelrx/demodwfm/wfmdemodsettings.h b/plugins/channelrx/demodwfm/wfmdemodsettings.h index 3c7651f66..4c98cbb4e 100644 --- a/plugins/channelrx/demodwfm/wfmdemodsettings.h +++ b/plugins/channelrx/demodwfm/wfmdemodsettings.h @@ -40,6 +40,8 @@ struct WFMDemodSettings uint16_t m_reverseAPIPort; uint16_t m_reverseAPIDeviceIndex; uint16_t m_reverseAPIChannelIndex; + int m_workspaceIndex; + QByteArray m_geometryBytes; Serializable *m_channelMarker; Serializable *m_rollupState; diff --git a/plugins/channelrx/filesink/filesinkgui.cpp b/plugins/channelrx/filesink/filesinkgui.cpp index ab6122984..0c4b6890b 100644 --- a/plugins/channelrx/filesink/filesinkgui.cpp +++ b/plugins/channelrx/filesink/filesinkgui.cpp @@ -184,10 +184,11 @@ FileSinkGUI::FileSinkGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Baseban m_fixedPosition(false), m_tickCount(0) { - ui->setupUi(this); + ui->setupUi(getRollupContents()); + getRollupContents()->arrangeRollups(); m_helpURL = "plugins/channelrx/filesink/readme.md"; setAttribute(Qt::WA_DeleteOnClose, true); - connect(this, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + connect(getRollupContents(), SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &))); m_fileSink = (FileSink*) channelrx; @@ -222,6 +223,7 @@ FileSinkGUI::FileSinkGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Baseban connect(&(m_deviceUISet->m_deviceAPI->getMasterTimer()), SIGNAL(timeout()), this, SLOT(tick())); displaySettings(); + makeUIConnections(); applySettings(true); } @@ -257,6 +259,7 @@ void FileSinkGUI::displaySettings() setTitleColor(m_settings.m_rgbColor); setWindowTitle(m_channelMarker.getTitle()); + setTitle(m_channelMarker.getTitle()); blockApplySettings(true); @@ -280,7 +283,7 @@ void FileSinkGUI::displaySettings() displayStreamIndex(); setPosFromFrequency(); - restoreState(m_rollupState); + getRollupContents()->restoreState(m_rollupState); blockApplySettings(false); } @@ -330,7 +333,7 @@ void FileSinkGUI::channelMarkerChangedByCursor() void FileSinkGUI::channelMarkerHighlightedByCursor() { - setHighlighted(m_channelMarker.getHighlighted()); + getRollupContents()->setHighlighted(m_channelMarker.getHighlighted()); } void FileSinkGUI::handleSourceMessages() @@ -351,7 +354,7 @@ void FileSinkGUI::onWidgetRolled(QWidget* widget, bool rollDown) (void) widget; (void) rollDown; - saveState(m_rollupState); + getRollupContents()->saveState(m_rollupState); applySettings(); } @@ -378,6 +381,7 @@ void FileSinkGUI::onMenuDialogCalled(const QPoint &p) m_settings.m_reverseAPIChannelIndex = dialog.getReverseAPIChannelIndex(); setWindowTitle(m_settings.m_title); + setTitle(m_channelMarker.getTitle()); setTitleColor(m_settings.m_rgbColor); applySettings(); @@ -591,3 +595,17 @@ QString FileSinkGUI::displayScaled(uint64_t value, int precision) } } +void FileSinkGUI::makeUIConnections() +{ + QObject::connect(ui->deltaFrequency, &ValueDialZ::changed, this, &FileSinkGUI::on_deltaFrequency_changed); + QObject::connect(ui->decimationFactor, QOverload::of(&QComboBox::currentIndexChanged), this, &FileSinkGUI::on_decimationFactor_currentIndexChanged); + QObject::connect(ui->fixedPosition, &QCheckBox::toggled, this, &FileSinkGUI::on_fixedPosition_toggled); + QObject::connect(ui->position, &QSlider::valueChanged, this, &FileSinkGUI::on_position_valueChanged); + QObject::connect(ui->spectrumSquelch, &ButtonSwitch::toggled, this, &FileSinkGUI::on_spectrumSquelch_toggled); + QObject::connect(ui->squelchLevel, &QDial::valueChanged, this, &FileSinkGUI::on_squelchLevel_valueChanged); + QObject::connect(ui->preRecordTime, &QDial::valueChanged, this, &FileSinkGUI::on_preRecordTime_valueChanged); + QObject::connect(ui->postSquelchTime, &QDial::valueChanged, this, &FileSinkGUI::on_postSquelchTime_valueChanged); + QObject::connect(ui->squelchedRecording, &ButtonSwitch::toggled, this, &FileSinkGUI::on_squelchedRecording_toggled); + QObject::connect(ui->record, &ButtonSwitch::toggled, this, &FileSinkGUI::on_record_toggled); + QObject::connect(ui->showFileDialog, &QPushButton::clicked, this, &FileSinkGUI::on_showFileDialog_clicked); +} diff --git a/plugins/channelrx/filesink/filesinkgui.h b/plugins/channelrx/filesink/filesinkgui.h index 2c86a0c50..bddedd7bb 100644 --- a/plugins/channelrx/filesink/filesinkgui.h +++ b/plugins/channelrx/filesink/filesinkgui.h @@ -49,6 +49,12 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } + virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; }; + virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; }; + virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; }; + virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; }; + virtual QString getTitle() const { return m_settings.m_title; }; + virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; public slots: void channelMarkerChangedByCursor(); @@ -88,6 +94,7 @@ private: void setPosFromFrequency(); QString displayScaled(uint64_t value, int precision); bool handleMessage(const Message& message); + void makeUIConnections(); void leaveEvent(QEvent*); void enterEvent(QEvent*); diff --git a/plugins/channelrx/filesink/filesinkgui.ui b/plugins/channelrx/filesink/filesinkgui.ui index 67388a521..b109a54f9 100644 --- a/plugins/channelrx/filesink/filesinkgui.ui +++ b/plugins/channelrx/filesink/filesinkgui.ui @@ -1,7 +1,7 @@ FileSinkGUI - + 0 @@ -548,9 +548,9 @@ - RollupWidget + RollupContents QWidget -
gui/rollupwidget.h
+
gui/rollupcontents.h
1
diff --git a/plugins/channelrx/filesink/filesinksettings.cpp b/plugins/channelrx/filesink/filesinksettings.cpp index 74eb15bdf..4730701b4 100644 --- a/plugins/channelrx/filesink/filesinksettings.cpp +++ b/plugins/channelrx/filesink/filesinksettings.cpp @@ -49,6 +49,7 @@ void FileSinkSettings::resetToDefaults() m_reverseAPIPort = 8888; m_reverseAPIDeviceIndex = 0; m_reverseAPIChannelIndex = 0; + m_workspaceIndex = 0; } QByteArray FileSinkSettings::serialize() const @@ -85,6 +86,9 @@ QByteArray FileSinkSettings::serialize() const s.writeBlob(19, m_rollupState->serialize()); } + s.writeS32(20, m_workspaceIndex); + s.writeBlob(21, m_geometryBytes); + return s.final(); } @@ -153,6 +157,9 @@ bool FileSinkSettings::deserialize(const QByteArray& data) m_rollupState->deserialize(bytetmp); } + d.readS32(20, &m_workspaceIndex, 0); + d.readBlob(21, &m_geometryBytes); + return true; } else diff --git a/plugins/channelrx/filesink/filesinksettings.h b/plugins/channelrx/filesink/filesinksettings.h index aa76798f2..08bc92062 100644 --- a/plugins/channelrx/filesink/filesinksettings.h +++ b/plugins/channelrx/filesink/filesinksettings.h @@ -41,6 +41,8 @@ struct FileSinkSettings uint16_t m_reverseAPIPort; uint16_t m_reverseAPIDeviceIndex; uint16_t m_reverseAPIChannelIndex; + int m_workspaceIndex; + QByteArray m_geometryBytes; Serializable *m_spectrumGUI; Serializable *m_channelMarker; diff --git a/plugins/channelrx/freqtracker/freqtrackergui.cpp b/plugins/channelrx/freqtracker/freqtrackergui.cpp index 8afe85d48..1b91b8388 100644 --- a/plugins/channelrx/freqtracker/freqtrackergui.cpp +++ b/plugins/channelrx/freqtracker/freqtrackergui.cpp @@ -140,7 +140,7 @@ void FreqTrackerGUI::channelMarkerChangedByCursor() void FreqTrackerGUI::channelMarkerHighlightedByCursor() { - setHighlighted(m_channelMarker.getHighlighted()); + getRollupContents()->setHighlighted(m_channelMarker.getHighlighted()); } void FreqTrackerGUI::on_deltaFrequency_changed(qint64 value) @@ -251,7 +251,7 @@ void FreqTrackerGUI::onWidgetRolled(QWidget* widget, bool rollDown) (void) widget; (void) rollDown; - saveState(m_rollupState); + getRollupContents()->saveState(m_rollupState); applySettings(); } @@ -278,6 +278,7 @@ void FreqTrackerGUI::onMenuDialogCalled(const QPoint &p) m_settings.m_reverseAPIChannelIndex = dialog.getReverseAPIChannelIndex(); setWindowTitle(m_settings.m_title); + setTitle(m_channelMarker.getTitle()); setTitleColor(m_settings.m_rgbColor); applySettings(); @@ -312,10 +313,11 @@ FreqTrackerGUI::FreqTrackerGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, B m_squelchOpen(false), m_tickCount(0) { - ui->setupUi(this); + ui->setupUi(getRollupContents()); + getRollupContents()->arrangeRollups(); m_helpURL = "plugins/channelrx/freqtracker/readme.md"; setAttribute(Qt::WA_DeleteOnClose, true); - connect(this, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + connect(getRollupContents(), SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &))); m_freqTracker = reinterpret_cast(rxChannel); @@ -363,6 +365,7 @@ FreqTrackerGUI::FreqTrackerGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, B connect(getInputMessageQueue(), SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages())); displaySettings(); + makeUIConnections(); applySettings(true); } @@ -403,6 +406,7 @@ void FreqTrackerGUI::displaySettings() setTitleColor(m_settings.m_rgbColor); setWindowTitle(m_channelMarker.getTitle()); + setTitle(m_channelMarker.getTitle()); blockApplySettings(true); @@ -433,7 +437,7 @@ void FreqTrackerGUI::displaySettings() displaySpectrumBandwidth(m_settings.m_spanLog2); displayStreamIndex(); - restoreState(m_rollupState); + getRollupContents()->restoreState(m_rollupState); blockApplySettings(false); } @@ -518,3 +522,16 @@ void FreqTrackerGUI::tick() m_tickCount++; } +void FreqTrackerGUI::makeUIConnections() +{ + QObject::connect(ui->deltaFrequency, &ValueDialZ::changed, this, &FreqTrackerGUI::on_deltaFrequency_changed); + QObject::connect(ui->rfBW, &QSlider::valueChanged, this, &FreqTrackerGUI::on_rfBW_valueChanged); + QObject::connect(ui->tracking, &QToolButton::toggled, this, &FreqTrackerGUI::on_tracking_toggled); + QObject::connect(ui->alphaEMA, &QDial::valueChanged, this, &FreqTrackerGUI::on_alphaEMA_valueChanged); + QObject::connect(ui->trackerType, QOverload::of(&QComboBox::currentIndexChanged), this, &FreqTrackerGUI::on_trackerType_currentIndexChanged); + QObject::connect(ui->pllPskOrder, QOverload::of(&QComboBox::currentIndexChanged), this, &FreqTrackerGUI::on_pllPskOrder_currentIndexChanged); + QObject::connect(ui->rrc, &ButtonSwitch::toggled, this, &FreqTrackerGUI::on_rrc_toggled); + QObject::connect(ui->rrcRolloff, &QDial::valueChanged, this, &FreqTrackerGUI::on_rrcRolloff_valueChanged); + QObject::connect(ui->squelch, &QSlider::valueChanged, this, &FreqTrackerGUI::on_squelch_valueChanged); + QObject::connect(ui->squelchGate, &QDial::valueChanged, this, &FreqTrackerGUI::on_squelchGate_valueChanged); +} diff --git a/plugins/channelrx/freqtracker/freqtrackergui.h b/plugins/channelrx/freqtracker/freqtrackergui.h index 5af7178ee..310ddb19f 100644 --- a/plugins/channelrx/freqtracker/freqtrackergui.h +++ b/plugins/channelrx/freqtracker/freqtrackergui.h @@ -49,6 +49,12 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } + virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; }; + virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; }; + virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; }; + virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; }; + virtual QString getTitle() const { return m_settings.m_title; }; + virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; public slots: void channelMarkerChangedByCursor(); @@ -81,6 +87,7 @@ private: void displaySpectrumBandwidth(int spanLog2); void displayStreamIndex(); bool handleMessage(const Message& message); + void makeUIConnections(); void leaveEvent(QEvent*); void enterEvent(QEvent*); diff --git a/plugins/channelrx/freqtracker/freqtrackergui.ui b/plugins/channelrx/freqtracker/freqtrackergui.ui index 93b964f15..72ba59785 100644 --- a/plugins/channelrx/freqtracker/freqtrackergui.ui +++ b/plugins/channelrx/freqtracker/freqtrackergui.ui @@ -1,7 +1,7 @@ FreqTrackerGUI - + 0 @@ -791,9 +791,9 @@ - RollupWidget + RollupContents QWidget -
gui/rollupwidget.h
+
gui/rollupcontents.h
1
diff --git a/plugins/channelrx/freqtracker/freqtrackersettings.cpp b/plugins/channelrx/freqtracker/freqtrackersettings.cpp index 58c0338b8..9ac8e1e05 100644 --- a/plugins/channelrx/freqtracker/freqtrackersettings.cpp +++ b/plugins/channelrx/freqtracker/freqtrackersettings.cpp @@ -52,6 +52,7 @@ void FreqTrackerSettings::resetToDefaults() m_reverseAPIPort = 8888; m_reverseAPIDeviceIndex = 0; m_reverseAPIChannelIndex = 0; + m_workspaceIndex = 0; } QByteArray FreqTrackerSettings::serialize() const @@ -91,6 +92,9 @@ QByteArray FreqTrackerSettings::serialize() const s.writeBlob(23, m_rollupState->serialize()); } + s.writeS32(24, m_workspaceIndex); + s.writeBlob(25, m_geometryBytes); + return s.final(); } @@ -170,6 +174,9 @@ bool FreqTrackerSettings::deserialize(const QByteArray& data) m_rollupState->deserialize(bytetmp); } + d.readS32(24, &m_workspaceIndex, 0); + d.readBlob(25, &m_geometryBytes); + return true; } else diff --git a/plugins/channelrx/freqtracker/freqtrackersettings.h b/plugins/channelrx/freqtracker/freqtrackersettings.h index b2d4e6f95..0e5734f66 100644 --- a/plugins/channelrx/freqtracker/freqtrackersettings.h +++ b/plugins/channelrx/freqtracker/freqtrackersettings.h @@ -57,6 +57,8 @@ struct FreqTrackerSettings uint16_t m_reverseAPIDeviceIndex; uint16_t m_reverseAPIChannelIndex; Serializable *m_rollupState; + int m_workspaceIndex; + QByteArray m_geometryBytes; FreqTrackerSettings(); void resetToDefaults(); diff --git a/plugins/channelrx/localsink/localsinkgui.cpp b/plugins/channelrx/localsink/localsinkgui.cpp index 7af286f66..905dbab15 100644 --- a/plugins/channelrx/localsink/localsinkgui.cpp +++ b/plugins/channelrx/localsink/localsinkgui.cpp @@ -101,10 +101,11 @@ LocalSinkGUI::LocalSinkGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Baseb m_basebandSampleRate(0), m_tickCount(0) { - ui->setupUi(this); + ui->setupUi(getRollupContents()); + getRollupContents()->arrangeRollups(); m_helpURL = "plugins/channelrx/localsink/readme.md"; setAttribute(Qt::WA_DeleteOnClose, true); - connect(this, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + connect(getRollupContents(), SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &))); m_localSink = (LocalSink*) channelrx; @@ -127,6 +128,7 @@ LocalSinkGUI::LocalSinkGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Baseb updateLocalDevices(); displaySettings(); + makeUIConnections(); applySettings(true); } @@ -163,6 +165,7 @@ void LocalSinkGUI::displaySettings() setTitleColor(m_settings.m_rgbColor); setWindowTitle(m_channelMarker.getTitle()); + setTitle(m_channelMarker.getTitle()); blockApplySettings(true); int index = getLocalDeviceIndexInCombo(m_settings.m_localDeviceIndex); @@ -176,7 +179,7 @@ void LocalSinkGUI::displaySettings() applyDecimation(); displayStreamIndex(); - restoreState(m_rollupState); + getRollupContents()->restoreState(m_rollupState); blockApplySettings(false); } @@ -254,7 +257,7 @@ void LocalSinkGUI::onWidgetRolled(QWidget* widget, bool rollDown) (void) widget; (void) rollDown; - saveState(m_rollupState); + getRollupContents()->saveState(m_rollupState); applySettings(); } @@ -281,6 +284,7 @@ void LocalSinkGUI::onMenuDialogCalled(const QPoint &p) m_settings.m_reverseAPIChannelIndex = dialog.getReverseAPIChannelIndex(); setWindowTitle(m_settings.m_title); + setTitle(m_channelMarker.getTitle()); setTitleColor(m_settings.m_rgbColor); applySettings(); @@ -369,3 +373,12 @@ void LocalSinkGUI::tick() m_tickCount = 0; } } + +void LocalSinkGUI::makeUIConnections() +{ + QObject::connect(ui->decimationFactor, QOverload::of(&QComboBox::currentIndexChanged), this, &LocalSinkGUI::on_decimationFactor_currentIndexChanged); + QObject::connect(ui->position, &QSlider::valueChanged, this, &LocalSinkGUI::on_position_valueChanged); + QObject::connect(ui->localDevice, QOverload::of(&QComboBox::currentIndexChanged), this, &LocalSinkGUI::on_localDevice_currentIndexChanged); + QObject::connect(ui->localDevicesRefresh, &QPushButton::clicked, this, &LocalSinkGUI::on_localDevicesRefresh_clicked); + QObject::connect(ui->localDevicePlay, &ButtonSwitch::toggled, this, &LocalSinkGUI::on_localDevicePlay_toggled); +} diff --git a/plugins/channelrx/localsink/localsinkgui.h b/plugins/channelrx/localsink/localsinkgui.h index 07f6c8c09..285894aca 100644 --- a/plugins/channelrx/localsink/localsinkgui.h +++ b/plugins/channelrx/localsink/localsinkgui.h @@ -48,6 +48,12 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } + virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; }; + virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; }; + virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; }; + virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; }; + virtual QString getTitle() const { return m_settings.m_title; }; + virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; private: Ui::LocalSinkGUI* ui; @@ -76,6 +82,7 @@ private: void updateLocalDevices(); int getLocalDeviceIndexInCombo(int localDeviceIndex); bool handleMessage(const Message& message); + void makeUIConnections(); void leaveEvent(QEvent*); void enterEvent(QEvent*); diff --git a/plugins/channelrx/localsink/localsinkgui.ui b/plugins/channelrx/localsink/localsinkgui.ui index 25ed63a74..dc64076d2 100644 --- a/plugins/channelrx/localsink/localsinkgui.ui +++ b/plugins/channelrx/localsink/localsinkgui.ui @@ -1,7 +1,7 @@ LocalSinkGUI - + 0 @@ -330,9 +330,9 @@ - RollupWidget + RollupContents QWidget -
gui/rollupwidget.h
+
gui/rollupcontents.h
1
diff --git a/plugins/channelrx/localsink/localsinksettings.cpp b/plugins/channelrx/localsink/localsinksettings.cpp index dfa674910..d04577cfb 100644 --- a/plugins/channelrx/localsink/localsinksettings.cpp +++ b/plugins/channelrx/localsink/localsinksettings.cpp @@ -44,6 +44,7 @@ void LocalSinkSettings::resetToDefaults() m_reverseAPIPort = 8888; m_reverseAPIDeviceIndex = 0; m_reverseAPIChannelIndex = 0; + m_workspaceIndex = 0; } QByteArray LocalSinkSettings::serialize() const @@ -70,6 +71,9 @@ QByteArray LocalSinkSettings::serialize() const s.writeBlob(15, m_rollupState->serialize()); } + s.writeS32(16, m_workspaceIndex); + s.writeBlob(17, m_geometryBytes); + return s.final(); } @@ -124,6 +128,9 @@ bool LocalSinkSettings::deserialize(const QByteArray& data) m_rollupState->deserialize(bytetmp); } + d.readS32(16, &m_workspaceIndex, 0); + d.readBlob(17, &m_geometryBytes); + return true; } else diff --git a/plugins/channelrx/localsink/localsinksettings.h b/plugins/channelrx/localsink/localsinksettings.h index 02b9cf680..0dee85451 100644 --- a/plugins/channelrx/localsink/localsinksettings.h +++ b/plugins/channelrx/localsink/localsinksettings.h @@ -37,6 +37,8 @@ struct LocalSinkSettings uint16_t m_reverseAPIPort; uint16_t m_reverseAPIDeviceIndex; uint16_t m_reverseAPIChannelIndex; + int m_workspaceIndex; + QByteArray m_geometryBytes; Serializable *m_channelMarker; Serializable *m_rollupState; diff --git a/plugins/channelrx/noisefigure/noisefiguregui.cpp b/plugins/channelrx/noisefigure/noisefiguregui.cpp index 81235801a..f70b07ceb 100644 --- a/plugins/channelrx/noisefigure/noisefiguregui.cpp +++ b/plugins/channelrx/noisefigure/noisefiguregui.cpp @@ -316,7 +316,7 @@ void NoiseFigureGUI::channelMarkerChangedByCursor() void NoiseFigureGUI::channelMarkerHighlightedByCursor() { - setHighlighted(m_channelMarker.getHighlighted()); + getRollupContents()->setHighlighted(m_channelMarker.getHighlighted()); } void NoiseFigureGUI::on_deltaFrequency_changed(qint64 value) @@ -532,7 +532,7 @@ void NoiseFigureGUI::onWidgetRolled(QWidget* widget, bool rollDown) (void) widget; (void) rollDown; - saveState(m_rollupState); + getRollupContents()->saveState(m_rollupState); applySettings(); } @@ -558,6 +558,7 @@ void NoiseFigureGUI::onMenuDialogCalled(const QPoint &p) m_settings.m_reverseAPIChannelIndex = dialog.getReverseAPIChannelIndex(); setWindowTitle(m_settings.m_title); + setTitle(m_channelMarker.getTitle()); setTitleColor(m_settings.m_rgbColor); applySettings(); @@ -592,11 +593,12 @@ NoiseFigureGUI::NoiseFigureGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, B m_runningTest(false), m_chart(nullptr) { - ui->setupUi(this); + ui->setupUi(getRollupContents()); + getRollupContents()->arrangeRollups(); m_helpURL = "plugins/channelrx/noisefigure/readme.md"; setAttribute(Qt::WA_DeleteOnClose, true); - connect(this, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + connect(getRollupContents(), SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &))); m_noiseFigure = reinterpret_cast(rxChannel); @@ -656,6 +658,7 @@ NoiseFigureGUI::NoiseFigureGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, B ui->results->setItemDelegateForColumn(RESULTS_COL_FLOOR, new DecimalDelegate(1)); displaySettings(); + makeUIConnections(); applySettings(true); } @@ -706,6 +709,7 @@ void NoiseFigureGUI::displaySettings() setTitleColor(m_settings.m_rgbColor); setWindowTitle(m_channelMarker.getTitle()); + setTitle(m_channelMarker.getTitle()); blockApplySettings(true); @@ -742,7 +746,7 @@ void NoiseFigureGUI::displaySettings() header->moveSection(header->visualIndex(i), m_settings.m_resultsColumnIndexes[i]); } - restoreState(m_rollupState); + getRollupContents()->restoreState(m_rollupState); blockApplySettings(false); } @@ -784,3 +788,25 @@ void NoiseFigureGUI::tick() m_tickCount++; } + +void NoiseFigureGUI::makeUIConnections() +{ + QObject::connect(ui->deltaFrequency, &ValueDialZ::changed, this, &NoiseFigureGUI::on_deltaFrequency_changed); + QObject::connect(ui->fftCount, &QSlider::valueChanged, this, &NoiseFigureGUI::on_fftCount_valueChanged); + QObject::connect(ui->setting, &QComboBox::currentTextChanged, this, &NoiseFigureGUI::on_setting_currentTextChanged); + QObject::connect(ui->frequencySpec, QOverload::of(&QComboBox::currentIndexChanged), this, &NoiseFigureGUI::on_frequencySpec_currentIndexChanged); + QObject::connect(ui->start, QOverload::of(&QDoubleSpinBox::valueChanged), this, &NoiseFigureGUI::on_start_valueChanged); + QObject::connect(ui->stop, QOverload::of(&QDoubleSpinBox::valueChanged), this, &NoiseFigureGUI::on_stop_valueChanged); + QObject::connect(ui->steps, QOverload::of(&QSpinBox::valueChanged), this, &NoiseFigureGUI::on_steps_valueChanged); + QObject::connect(ui->step, QOverload::of(&QDoubleSpinBox::valueChanged), this, &NoiseFigureGUI::on_step_valueChanged); + QObject::connect(ui->list, &QLineEdit::editingFinished, this, &NoiseFigureGUI::on_list_editingFinished); + QObject::connect(ui->fftSize, QOverload::of(&QComboBox::currentIndexChanged), this, &NoiseFigureGUI::on_fftSize_currentIndexChanged); + QObject::connect(ui->startStop, &ButtonSwitch::clicked, this, &NoiseFigureGUI::on_startStop_clicked); + QObject::connect(ui->saveResults, &QToolButton::clicked, this, &NoiseFigureGUI::on_saveResults_clicked); + QObject::connect(ui->clearResults, &QToolButton::clicked, this, &NoiseFigureGUI::on_clearResults_clicked); + QObject::connect(ui->enr, &QToolButton::clicked, this, &NoiseFigureGUI::on_enr_clicked); + QObject::connect(ui->control, &QToolButton::clicked, this, &NoiseFigureGUI::on_control_clicked); + QObject::connect(ui->chartSelect, QOverload::of(&QComboBox::currentIndexChanged), this, &NoiseFigureGUI::on_chartSelect_currentIndexChanged); + QObject::connect(ui->openReference, &QToolButton::clicked, this, &NoiseFigureGUI::on_openReference_clicked); + QObject::connect(ui->clearReference, &QToolButton::clicked, this, &NoiseFigureGUI::on_clearReference_clicked); +} diff --git a/plugins/channelrx/noisefigure/noisefiguregui.h b/plugins/channelrx/noisefigure/noisefiguregui.h index 9d2e9ad8c..29b7ba556 100644 --- a/plugins/channelrx/noisefigure/noisefiguregui.h +++ b/plugins/channelrx/noisefigure/noisefiguregui.h @@ -55,6 +55,12 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } + virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; }; + virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; }; + virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; }; + virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; }; + virtual QString getTitle() const { return m_settings.m_title; }; + virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; public slots: void channelMarkerChangedByCursor(); @@ -92,6 +98,7 @@ private: void displaySettings(); void displayStreamIndex(); bool handleMessage(const Message& message); + void makeUIConnections(); void leaveEvent(QEvent*); void enterEvent(QEvent*); diff --git a/plugins/channelrx/noisefigure/noisefiguregui.ui b/plugins/channelrx/noisefigure/noisefiguregui.ui index 7125e60f0..eee1aab46 100644 --- a/plugins/channelrx/noisefigure/noisefiguregui.ui +++ b/plugins/channelrx/noisefigure/noisefiguregui.ui @@ -1,7 +1,7 @@ NoiseFigureGUI - + 0 @@ -890,9 +890,9 @@ - RollupWidget + RollupContents QWidget -
gui/rollupwidget.h
+
gui/rollupcontents.h
1
diff --git a/plugins/channelrx/noisefigure/noisefiguresettings.cpp b/plugins/channelrx/noisefigure/noisefiguresettings.cpp index 53f2b36a1..fe12e037b 100644 --- a/plugins/channelrx/noisefigure/noisefiguresettings.cpp +++ b/plugins/channelrx/noisefigure/noisefiguresettings.cpp @@ -65,6 +65,7 @@ void NoiseFigureSettings::resetToDefaults() m_reverseAPIPort = 8888; m_reverseAPIDeviceIndex = 0; m_reverseAPIChannelIndex = 0; + m_workspaceIndex = 0; for (int i = 0; i < NOISEFIGURE_COLUMNS; i++) { @@ -117,6 +118,9 @@ QByteArray NoiseFigureSettings::serialize() const s.writeBlob(28, m_rollupState->serialize()); } + s.writeS32(29, m_workspaceIndex); + s.writeBlob(30, m_geometryBytes); + for (int i = 0; i < NOISEFIGURE_COLUMNS; i++) { s.writeS32(100 + i, m_resultsColumnIndexes[i]); } @@ -197,6 +201,9 @@ bool NoiseFigureSettings::deserialize(const QByteArray& data) m_rollupState->deserialize(bytetmp); } + d.readS32(29, &m_workspaceIndex, 0); + d.readBlob(30, &m_geometryBytes); + for (int i = 0; i < NOISEFIGURE_COLUMNS; i++) { d.readS32(100 + i, &m_resultsColumnIndexes[i], i); } diff --git a/plugins/channelrx/noisefigure/noisefiguresettings.h b/plugins/channelrx/noisefigure/noisefiguresettings.h index 8ad3dbdd4..f5f121227 100644 --- a/plugins/channelrx/noisefigure/noisefiguresettings.h +++ b/plugins/channelrx/noisefigure/noisefiguresettings.h @@ -86,6 +86,8 @@ struct NoiseFigureSettings uint16_t m_reverseAPIDeviceIndex; uint16_t m_reverseAPIChannelIndex; Serializable *m_rollupState; + int m_workspaceIndex; + QByteArray m_geometryBytes; int m_resultsColumnIndexes[NOISEFIGURE_COLUMNS];//!< How the columns are ordered in the table int m_resultsColumnSizes[NOISEFIGURE_COLUMNS]; //!< Size of the columns in the table diff --git a/plugins/channelrx/radioastronomy/radioastronomygui.cpp b/plugins/channelrx/radioastronomy/radioastronomygui.cpp index 13de963f1..1fd786728 100644 --- a/plugins/channelrx/radioastronomy/radioastronomygui.cpp +++ b/plugins/channelrx/radioastronomy/radioastronomygui.cpp @@ -1105,7 +1105,7 @@ void RadioAstronomyGUI::channelMarkerChangedByCursor() void RadioAstronomyGUI::channelMarkerHighlightedByCursor() { - setHighlighted(m_channelMarker.getHighlighted()); + getRollupContents()->setHighlighted(m_channelMarker.getHighlighted()); } // Calculate Tsys0 - i.e. receiver noise temperature when there's no source signal, just unwanted noise @@ -1942,7 +1942,7 @@ void RadioAstronomyGUI::onWidgetRolled(QWidget* widget, bool rollDown) (void) widget; (void) rollDown; - saveState(m_rollupState); + getRollupContents()->saveState(m_rollupState); applySettings(); } @@ -1968,6 +1968,7 @@ void RadioAstronomyGUI::onMenuDialogCalled(const QPoint &p) m_settings.m_reverseAPIChannelIndex = dialog.getReverseAPIChannelIndex(); setWindowTitle(m_settings.m_title); + setTitle(m_channelMarker.getTitle()); setTitleColor(m_settings.m_rgbColor); applySettings(); @@ -2052,10 +2053,11 @@ RadioAstronomyGUI::RadioAstronomyGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUI m_downloadingLAB(false) { qDebug("RadioAstronomyGUI::RadioAstronomyGUI"); - ui->setupUi(this); + ui->setupUi(getRollupContents()); + getRollupContents()->arrangeRollups(); setAttribute(Qt::WA_DeleteOnClose, true); - connect(this, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + connect(getRollupContents(), SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &))); m_radioAstronomy = reinterpret_cast(rxChannel); @@ -2217,6 +2219,7 @@ RadioAstronomyGUI::RadioAstronomyGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUI ui->startStop->setStyleSheet("QToolButton { background-color : blue; }"); displaySettings(); + makeUIConnections(); applySettings(true); create2DImage(); @@ -2354,6 +2357,7 @@ void RadioAstronomyGUI::displaySettings() setTitleColor(m_settings.m_rgbColor); setWindowTitle(m_channelMarker.getTitle()); + setTitle(m_channelMarker.getTitle()); blockApplySettings(true); ui->deltaFrequency->setValue(m_channelMarker.getCenterFrequency()); @@ -2550,9 +2554,9 @@ void RadioAstronomyGUI::displaySettings() header->moveSection(header->visualIndex(i), m_settings.m_powerTableColumnIndexes[i]); } - restoreState(m_rollupState); + getRollupContents()->restoreState(m_rollupState); blockApplySettings(false); - arrangeRollups(); + getRollupContents()->arrangeRollups(); } void RadioAstronomyGUI::displayStreamIndex() @@ -2796,7 +2800,7 @@ void RadioAstronomyGUI::updatePowerChartWidgetsVisibility() ui->powerMarkerTableWidgets->setVisible(powerChart && (m_settings.m_powerPeaks || m_settings.m_powerMarkers)); ui->power2DScaleWidgets->setVisible(!powerChart); ui->power2DColourScaleWidgets->setVisible(!powerChart); - arrangeRollups(); + getRollupContents()->arrangeRollups(); } int RadioAstronomyGUI::powerYUnitsToIndex(RadioAstronomySettings::PowerYUnits units) @@ -2915,7 +2919,7 @@ void RadioAstronomyGUI::updateSpectrumChartWidgetsVisibility() ui->spectrumPeak->setVisible(fft); ui->saveSpectrumChartImages->setVisible(fft); - arrangeRollups(); + getRollupContents()->arrangeRollups(); } // Calulate mean, RMS and standard deviation @@ -5130,7 +5134,7 @@ void RadioAstronomyGUI::on_powerShowPeak_toggled(bool checked) m_powerChart->legend()->markers(m_powerPeakSeries)[0]->setVisible(false); } } - arrangeRollups(); + getRollupContents()->arrangeRollups(); } void RadioAstronomyGUI::on_spectrumPeak_toggled(bool checked) @@ -5148,7 +5152,7 @@ void RadioAstronomyGUI::on_spectrumPeak_toggled(bool checked) clearLoSMarker("Max"); } } - arrangeRollups(); + getRollupContents()->arrangeRollups(); } void RadioAstronomyGUI::on_powerShowMarker_toggled(bool checked) @@ -5164,7 +5168,7 @@ void RadioAstronomyGUI::on_powerShowMarker_toggled(bool checked) } } updatePowerSelect(); - arrangeRollups(); + getRollupContents()->arrangeRollups(); } void RadioAstronomyGUI::on_powerShowAvg_toggled(bool checked) @@ -5172,7 +5176,7 @@ void RadioAstronomyGUI::on_powerShowAvg_toggled(bool checked) m_settings.m_powerAvg = checked; applySettings(); ui->powerChartAvgWidgets->setVisible(checked); - arrangeRollups(); + getRollupContents()->arrangeRollups(); if (checked) { calcAverages(); } @@ -5291,7 +5295,7 @@ void RadioAstronomyGUI::on_spectrumMarker_toggled(bool checked) clearLoSMarker("M2"); } updateSpectrumSelect(); - arrangeRollups(); + getRollupContents()->arrangeRollups(); } void RadioAstronomyGUI::on_spectrumTemp_toggled(bool checked) @@ -5301,7 +5305,7 @@ void RadioAstronomyGUI::on_spectrumTemp_toggled(bool checked) ui->spectrumGaussianWidgets->setVisible(checked); m_fftGaussianSeries->setVisible(checked); updateSpectrumSelect(); - arrangeRollups(); + getRollupContents()->arrangeRollups(); } void RadioAstronomyGUI::on_spectrumShowLegend_toggled(bool checked) @@ -5326,7 +5330,7 @@ void RadioAstronomyGUI::on_spectrumShowRefLine_toggled(bool checked) m_fftDopplerAxis->setVisible(m_settings.m_spectrumRefLine); } updateDistanceColumns(); - arrangeRollups(); + getRollupContents()->arrangeRollups(); } void RadioAstronomyGUI::on_spectrumShowLAB_toggled(bool checked) @@ -5650,7 +5654,7 @@ void RadioAstronomyGUI::on_powerShowGaussian_clicked(bool checked) ui->powerGaussianWidgets->setVisible(checked); m_powerGaussianSeries->setVisible(checked); updatePowerSelect(); - arrangeRollups(); + getRollupContents()->arrangeRollups(); } void RadioAstronomyGUI::plotPowerGaussian() @@ -5896,7 +5900,7 @@ void RadioAstronomyGUI::displayRunModeSettings() ui->sweepStatus->setVisible(sweep); ui->runLayout->activate(); // Needed otherwise height of rollup doesn't seem to be reduced ui->statusLayout->activate(); // going from sweep to single/continuous - arrangeRollups(); + getRollupContents()->arrangeRollups(); } void RadioAstronomyGUI::on_runMode_currentIndexChanged(int index) @@ -5973,7 +5977,7 @@ void RadioAstronomyGUI::on_sweepStartAtTime_currentIndexChanged(int index) { m_settings.m_sweepStartAtTime = ui->sweepStartAtTime->currentIndex() == 1; ui->sweepStartDateTime->setVisible(index == 1); - arrangeRollups(); + getRollupContents()->arrangeRollups(); applySettings(); } @@ -6033,3 +6037,111 @@ void RadioAstronomyGUI::resizeEvent(QResizeEvent* size) calcSpectrumChartTickCount(m_calXAxis, width); ChannelGUI::resizeEvent(size); } + +void RadioAstronomyGUI::makeUIConnections() +{ + QObject::connect(ui->deltaFrequency, &ValueDialZ::changed, this, &RadioAstronomyGUI::on_deltaFrequency_changed); + QObject::connect(ui->sampleRate, &ValueDialZ::changed, this, &RadioAstronomyGUI::on_sampleRate_changed); + QObject::connect(ui->rfBW, &ValueDialZ::changed, this, &RadioAstronomyGUI::on_rfBW_changed); + QObject::connect(ui->integration, &ValueDialZ::changed, this, &RadioAstronomyGUI::on_integration_changed); + QObject::connect(ui->fftSize, QOverload::of(&QComboBox::currentIndexChanged), this, &RadioAstronomyGUI::on_fftSize_currentIndexChanged); + QObject::connect(ui->fftWindow, QOverload::of(&QComboBox::currentIndexChanged), this, &RadioAstronomyGUI::on_fftWindow_currentIndexChanged); + QObject::connect(ui->filterFreqs, &QLineEdit::editingFinished, this, &RadioAstronomyGUI::on_filterFreqs_editingFinished); + QObject::connect(ui->starTracker, &QComboBox::currentTextChanged, this, &RadioAstronomyGUI::on_starTracker_currentTextChanged); + QObject::connect(ui->rotator, &QComboBox::currentTextChanged, this, &RadioAstronomyGUI::on_rotator_currentTextChanged); + QObject::connect(ui->showSensors, &QToolButton::clicked, this, &RadioAstronomyGUI::on_showSensors_clicked); + QObject::connect(ui->tempRXSelect, QOverload::of(&QComboBox::currentIndexChanged), this, &RadioAstronomyGUI::on_tempRXSelect_currentIndexChanged); + QObject::connect(ui->tempRX, QOverload::of(&QDoubleSpinBox::valueChanged), this, &RadioAstronomyGUI::on_tempRX_valueChanged); + QObject::connect(ui->tempCMB, QOverload::of(&QDoubleSpinBox::valueChanged), this, &RadioAstronomyGUI::on_tempCMB_valueChanged); + QObject::connect(ui->tempGal, QOverload::of(&QDoubleSpinBox::valueChanged), this, &RadioAstronomyGUI::on_tempGal_valueChanged); + QObject::connect(ui->tempSP, QOverload::of(&QDoubleSpinBox::valueChanged), this, &RadioAstronomyGUI::on_tempSP_valueChanged); + QObject::connect(ui->tempAtm, QOverload::of(&QDoubleSpinBox::valueChanged), this, &RadioAstronomyGUI::on_tempAtm_valueChanged); + QObject::connect(ui->tempAir, QOverload::of(&QDoubleSpinBox::valueChanged), this, &RadioAstronomyGUI::on_tempAir_valueChanged); + QObject::connect(ui->zenithOpacity, QOverload::of(&QDoubleSpinBox::valueChanged), this, &RadioAstronomyGUI::on_zenithOpacity_valueChanged); + QObject::connect(ui->elevation, QOverload::of(&QDoubleSpinBox::valueChanged), this, &RadioAstronomyGUI::on_elevation_valueChanged); + QObject::connect(ui->tempAtmLink, &ButtonSwitch::toggled, this, &RadioAstronomyGUI::on_tempAtmLink_toggled); + QObject::connect(ui->tempAirLink, &ButtonSwitch::toggled, this, &RadioAstronomyGUI::on_tempAirLink_toggled); + QObject::connect(ui->tempGalLink, &ButtonSwitch::toggled, this, &RadioAstronomyGUI::on_tempGalLink_toggled); + QObject::connect(ui->elevationLink, &ButtonSwitch::toggled, this, &RadioAstronomyGUI::on_elevationLink_toggled); + QObject::connect(ui->gainVariation, QOverload::of(&QDoubleSpinBox::valueChanged), this, &RadioAstronomyGUI::on_gainVariation_valueChanged); + QObject::connect(ui->omegaAUnits, QOverload::of(&QComboBox::currentIndexChanged), this, &RadioAstronomyGUI::on_omegaAUnits_currentIndexChanged); + QObject::connect(ui->sourceType, QOverload::of(&QComboBox::currentIndexChanged), this, &RadioAstronomyGUI::on_sourceType_currentIndexChanged); + QObject::connect(ui->omegaS, QOverload::of(&QDoubleSpinBox::valueChanged), this, &RadioAstronomyGUI::on_omegaS_valueChanged); + QObject::connect(ui->omegaSUnits, QOverload::of(&QComboBox::currentIndexChanged), this, &RadioAstronomyGUI::on_omegaSUnits_currentIndexChanged); + QObject::connect(ui->spectrumChartSelect, QOverload::of(&QComboBox::currentIndexChanged), this, &RadioAstronomyGUI::on_spectrumChartSelect_currentIndexChanged); + QObject::connect(ui->showCalSettings, &QToolButton::clicked, this, &RadioAstronomyGUI::on_showCalSettings_clicked); + QObject::connect(ui->startCalHot, &QToolButton::clicked, this, &RadioAstronomyGUI::on_startCalHot_clicked); + QObject::connect(ui->startCalCold, &QToolButton::clicked, this, &RadioAstronomyGUI::on_startCalCold_clicked); + QObject::connect(ui->recalibrate, &ButtonSwitch::toggled, this, &RadioAstronomyGUI::on_recalibrate_toggled); + QObject::connect(ui->spectrumShowLegend, &ButtonSwitch::toggled, this, &RadioAstronomyGUI::on_spectrumShowLegend_toggled); + QObject::connect(ui->spectrumShowRefLine, &ButtonSwitch::toggled, this, &RadioAstronomyGUI::on_spectrumShowRefLine_toggled); + QObject::connect(ui->spectrumTemp, &ButtonSwitch::toggled, this, &RadioAstronomyGUI::on_spectrumTemp_toggled); + QObject::connect(ui->spectrumMarker, &ButtonSwitch::toggled, this, &RadioAstronomyGUI::on_spectrumMarker_toggled); + QObject::connect(ui->spectrumPeak, &ButtonSwitch::toggled, this, &RadioAstronomyGUI::on_spectrumPeak_toggled); + QObject::connect(ui->spectrumReverseXAxis, &ButtonSwitch::toggled, this, &RadioAstronomyGUI::on_spectrumReverseXAxis_toggled); + QObject::connect(ui->savePowerData, &QToolButton::clicked, this, &RadioAstronomyGUI::on_savePowerData_clicked); + QObject::connect(ui->savePowerChartImage, &QToolButton::clicked, this, &RadioAstronomyGUI::on_savePowerChartImage_clicked); + QObject::connect(ui->saveSpectrumData, &QToolButton::clicked, this, &RadioAstronomyGUI::on_saveSpectrumData_clicked); + QObject::connect(ui->loadSpectrumData, &QToolButton::clicked, this, &RadioAstronomyGUI::on_loadSpectrumData_clicked); + QObject::connect(ui->saveSpectrumChartImage, &QToolButton::clicked, this, &RadioAstronomyGUI::on_saveSpectrumChartImage_clicked); + QObject::connect(ui->saveSpectrumChartImages, &QToolButton::clicked, this, &RadioAstronomyGUI::on_saveSpectrumChartImages_clicked); + QObject::connect(ui->clearData, &QToolButton::clicked, this, &RadioAstronomyGUI::on_clearData_clicked); + QObject::connect(ui->clearCal, &QToolButton::clicked, this, &RadioAstronomyGUI::on_clearCal_clicked); + QObject::connect(ui->spectrumAutoscale, &QToolButton::toggled, this, &RadioAstronomyGUI::on_spectrumAutoscale_toggled); + QObject::connect(ui->spectrumAutoscaleX, &QToolButton::clicked, this, &RadioAstronomyGUI::on_spectrumAutoscaleX_clicked); + QObject::connect(ui->spectrumAutoscaleY, &QToolButton::clicked, this, &RadioAstronomyGUI::on_spectrumAutoscaleY_clicked); + QObject::connect(ui->spectrumReference, QOverload::of(&QDoubleSpinBox::valueChanged), this, &RadioAstronomyGUI::on_spectrumReference_valueChanged); + QObject::connect(ui->spectrumRange, QOverload::of(&QDoubleSpinBox::valueChanged), this, &RadioAstronomyGUI::on_spectrumRange_valueChanged); + QObject::connect(ui->spectrumCenterFreq, QOverload::of(&QDoubleSpinBox::valueChanged), this, &RadioAstronomyGUI::on_spectrumCenterFreq_valueChanged); + QObject::connect(ui->spectrumSpan, QOverload::of(&QDoubleSpinBox::valueChanged), this, &RadioAstronomyGUI::on_spectrumSpan_valueChanged); + QObject::connect(ui->spectrumYUnits, QOverload::of(&QComboBox::currentIndexChanged), this, &RadioAstronomyGUI::on_spectrumYUnits_currentIndexChanged); + QObject::connect(ui->spectrumBaseline, QOverload::of(&QComboBox::currentIndexChanged), this, &RadioAstronomyGUI::on_spectrumBaseline_currentIndexChanged); + QObject::connect(ui->spectrumIndex, &QSlider::valueChanged, this, &RadioAstronomyGUI::on_spectrumIndex_valueChanged); + QObject::connect(ui->spectrumLine, QOverload::of(&QComboBox::currentIndexChanged), this, &RadioAstronomyGUI::on_spectrumLine_currentIndexChanged); + QObject::connect(ui->spectrumLineFrequency, QOverload::of(&QDoubleSpinBox::valueChanged), this, &RadioAstronomyGUI::on_spectrumLineFrequency_valueChanged); + QObject::connect(ui->refFrame, QOverload::of(&QComboBox::currentIndexChanged), this, &RadioAstronomyGUI::on_refFrame_currentIndexChanged); + QObject::connect(ui->sunDistanceToGC, QOverload::of(&QDoubleSpinBox::valueChanged), this, &RadioAstronomyGUI::on_sunDistanceToGC_valueChanged); + QObject::connect(ui->sunOrbitalVelocity, QOverload::of(&QDoubleSpinBox::valueChanged), this, &RadioAstronomyGUI::on_sunOrbitalVelocity_valueChanged); + QObject::connect(ui->spectrumGaussianFreq, QOverload::of(&QDoubleSpinBox::valueChanged), this, &RadioAstronomyGUI::on_spectrumGaussianFreq_valueChanged); + QObject::connect(ui->spectrumGaussianAmp, QOverload::of(&QDoubleSpinBox::valueChanged), this, &RadioAstronomyGUI::on_spectrumGaussianAmp_valueChanged); + QObject::connect(ui->spectrumGaussianFloor, QOverload::of(&QDoubleSpinBox::valueChanged), this, &RadioAstronomyGUI::on_spectrumGaussianFloor_valueChanged); + QObject::connect(ui->spectrumGaussianFWHM, QOverload::of(&QDoubleSpinBox::valueChanged), this, &RadioAstronomyGUI::on_spectrumGaussianFWHM_valueChanged); + QObject::connect(ui->spectrumGaussianTurb, QOverload::of(&QDoubleSpinBox::valueChanged), this, &RadioAstronomyGUI::on_spectrumGaussianTurb_valueChanged); + QObject::connect(ui->spectrumTemperature, QOverload::of(&QDoubleSpinBox::valueChanged), this, &RadioAstronomyGUI::on_spectrumTemperature_valueChanged); + QObject::connect(ui->spectrumShowLAB, &ButtonSwitch::toggled, this, &RadioAstronomyGUI::on_spectrumShowLAB_toggled); + QObject::connect(ui->spectrumShowDistance, &ButtonSwitch::toggled, this, &RadioAstronomyGUI::on_spectrumShowDistance_toggled); + QObject::connect(ui->tCalHotSelect, QOverload::of(&QComboBox::currentIndexChanged), this, &RadioAstronomyGUI::on_tCalHotSelect_currentIndexChanged); + QObject::connect(ui->tCalHot, QOverload::of(&QDoubleSpinBox::valueChanged), this, &RadioAstronomyGUI::on_tCalHot_valueChanged); + QObject::connect(ui->tCalColdSelect, QOverload::of(&QComboBox::currentIndexChanged), this, &RadioAstronomyGUI::on_tCalColdSelect_currentIndexChanged); + QObject::connect(ui->tCalCold, QOverload::of(&QDoubleSpinBox::valueChanged), this, &RadioAstronomyGUI::on_tCalCold_valueChanged); + QObject::connect(ui->powerChartSelect, QOverload::of(&QComboBox::currentIndexChanged), this, &RadioAstronomyGUI::on_powerChartSelect_currentIndexChanged); + QObject::connect(ui->powerYUnits, QOverload::of(&QComboBox::currentIndexChanged), this, &RadioAstronomyGUI::on_powerYUnits_currentIndexChanged); + QObject::connect(ui->powerShowMarker, &ButtonSwitch::toggled, this, &RadioAstronomyGUI::on_powerShowMarker_toggled); + QObject::connect(ui->powerShowAirTemp, &ButtonSwitch::toggled, this, &RadioAstronomyGUI::on_powerShowAirTemp_toggled); + QObject::connect(ui->powerShowPeak, &ButtonSwitch::toggled, this, &RadioAstronomyGUI::on_powerShowPeak_toggled); + QObject::connect(ui->powerShowAvg, &ButtonSwitch::toggled, this, &RadioAstronomyGUI::on_powerShowAvg_toggled); + QObject::connect(ui->powerShowLegend, &ButtonSwitch::toggled, this, &RadioAstronomyGUI::on_powerShowLegend_toggled); + QObject::connect(ui->powerAutoscale, &QToolButton::toggled, this, &RadioAstronomyGUI::on_powerAutoscale_toggled); + QObject::connect(ui->powerAutoscaleY, &QToolButton::clicked, this, &RadioAstronomyGUI::on_powerAutoscaleY_clicked); + QObject::connect(ui->powerAutoscaleX, &QToolButton::clicked, this, &RadioAstronomyGUI::on_powerAutoscaleX_clicked); + QObject::connect(ui->powerReference, QOverload::of(&QDoubleSpinBox::valueChanged), this, &RadioAstronomyGUI::on_powerReference_valueChanged); + QObject::connect(ui->powerRange, QOverload::of(&QDoubleSpinBox::valueChanged), this, &RadioAstronomyGUI::on_powerRange_valueChanged); + QObject::connect(ui->powerStartTime, &WrappingDateTimeEdit::dateTimeChanged, this, &RadioAstronomyGUI::on_powerStartTime_dateTimeChanged); + QObject::connect(ui->powerEndTime, &WrappingDateTimeEdit::dateTimeChanged, this, &RadioAstronomyGUI::on_powerEndTime_dateTimeChanged); + QObject::connect(ui->powerShowGaussian, &ButtonSwitch::clicked, this, &RadioAstronomyGUI::on_powerShowGaussian_clicked); + QObject::connect(ui->powerGaussianCenter, &WrappingDateTimeEdit::dateTimeChanged, this, &RadioAstronomyGUI::on_powerGaussianCenter_dateTimeChanged); + QObject::connect(ui->powerGaussianAmp, QOverload::of(&QDoubleSpinBox::valueChanged), this, &RadioAstronomyGUI::on_powerGaussianAmp_valueChanged); + QObject::connect(ui->powerGaussianFloor, QOverload::of(&QDoubleSpinBox::valueChanged), this, &RadioAstronomyGUI::on_powerGaussianFloor_valueChanged); + QObject::connect(ui->powerGaussianFWHM, QOverload::of(&QDoubleSpinBox::valueChanged), this, &RadioAstronomyGUI::on_powerGaussianFWHM_valueChanged); + QObject::connect(ui->powerGaussianHPBW, QOverload::of(&QDoubleSpinBox::valueChanged), this, &RadioAstronomyGUI::on_powerGaussianHPBW_valueChanged); + QObject::connect(ui->runMode, QOverload::of(&QComboBox::currentIndexChanged), this, &RadioAstronomyGUI::on_runMode_currentIndexChanged); + QObject::connect(ui->sweepType, QOverload::of(&QComboBox::currentIndexChanged), this, &RadioAstronomyGUI::on_sweepType_currentIndexChanged); + QObject::connect(ui->startStop, &ButtonSwitch::clicked, this, &RadioAstronomyGUI::on_startStop_clicked); + QObject::connect(ui->sweepStartAtTime, QOverload::of(&QComboBox::currentIndexChanged), this, &RadioAstronomyGUI::on_sweepStartAtTime_currentIndexChanged); + QObject::connect(ui->sweepStartDateTime, &QDateTimeEdit::dateTimeChanged, this, &RadioAstronomyGUI::on_sweepStartDateTime_dateTimeChanged); + QObject::connect(ui->powerColourAutoscale, &QToolButton::toggled, this, &RadioAstronomyGUI::on_powerColourAutoscale_toggled); + QObject::connect(ui->powerColourScaleMin, QOverload::of(&QDoubleSpinBox::valueChanged), this, &RadioAstronomyGUI::on_powerColourScaleMin_valueChanged); + QObject::connect(ui->powerColourScaleMax, QOverload::of(&QDoubleSpinBox::valueChanged), this, &RadioAstronomyGUI::on_powerColourScaleMax_valueChanged); + QObject::connect(ui->powerColourPalette, QOverload::of(&QComboBox::currentIndexChanged), this, &RadioAstronomyGUI::on_powerColourPalette_currentIndexChanged); + QObject::connect(ui->powerTable, &QTableWidget::cellDoubleClicked, this, &RadioAstronomyGUI::on_powerTable_cellDoubleClicked); +} diff --git a/plugins/channelrx/radioastronomy/radioastronomygui.h b/plugins/channelrx/radioastronomy/radioastronomygui.h index 8007378b2..0c7e664d3 100644 --- a/plugins/channelrx/radioastronomy/radioastronomygui.h +++ b/plugins/channelrx/radioastronomy/radioastronomygui.h @@ -198,6 +198,12 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } + virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; }; + virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; }; + virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; }; + virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; }; + virtual QString getTitle() const { return m_settings.m_title; }; + virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; public slots: void channelMarkerChangedByCursor(); @@ -324,6 +330,8 @@ private: void updateAvailableFeatures(); void updateRotatorList(const QList& rotators); bool handleMessage(const Message& message); + void makeUIConnections(); + double degreesToSteradian(double deg) const; double hpbwToSteradians(double hpbw) const; double calcOmegaA() const; diff --git a/plugins/channelrx/radioastronomy/radioastronomygui.ui b/plugins/channelrx/radioastronomy/radioastronomygui.ui index ff821830a..d95f34841 100644 --- a/plugins/channelrx/radioastronomy/radioastronomygui.ui +++ b/plugins/channelrx/radioastronomy/radioastronomygui.ui @@ -1,7 +1,7 @@ RadioAstronomyGUI - + 0 @@ -5010,9 +5010,9 @@ This should be close to the expected difference in power between hot and cold ca - RollupWidget + RollupContents QWidget -
gui/rollupwidget.h
+
gui/rollupcontents.h
1
diff --git a/plugins/channelrx/radioastronomy/radioastronomysettings.cpp b/plugins/channelrx/radioastronomy/radioastronomysettings.cpp index 19c2802d2..db2dfd132 100644 --- a/plugins/channelrx/radioastronomy/radioastronomysettings.cpp +++ b/plugins/channelrx/radioastronomy/radioastronomysettings.cpp @@ -163,6 +163,7 @@ void RadioAstronomySettings::resetToDefaults() m_reverseAPIPort = 8888; m_reverseAPIDeviceIndex = 0; m_reverseAPIChannelIndex = 0; + m_workspaceIndex = 0; for (int i = 0; i < RADIOASTRONOMY_POWERTABLE_COLUMNS; i++) { @@ -305,6 +306,8 @@ QByteArray RadioAstronomySettings::serialize() const s.writeU32(186, m_reverseAPIPort); s.writeU32(187, m_reverseAPIDeviceIndex); s.writeU32(188, m_reverseAPIChannelIndex); + s.writeS32(189, m_workspaceIndex); + s.writeBlob(190, m_geometryBytes); for (int i = 0; i < RADIOASTRONOMY_POWERTABLE_COLUMNS; i++) { s.writeS32(400 + i, m_powerTableColumnIndexes[i]); @@ -472,6 +475,8 @@ bool RadioAstronomySettings::deserialize(const QByteArray& data) m_reverseAPIDeviceIndex = utmp > 99 ? 99 : utmp; d.readU32(188, &utmp, 0); m_reverseAPIChannelIndex = utmp > 99 ? 99 : utmp; + d.readS32(189, &m_workspaceIndex, 0); + d.readBlob(190, &m_geometryBytes); for (int i = 0; i < RADIOASTRONOMY_POWERTABLE_COLUMNS; i++) { d.readS32(400 + i, &m_powerTableColumnIndexes[i], i); diff --git a/plugins/channelrx/radioastronomy/radioastronomysettings.h b/plugins/channelrx/radioastronomy/radioastronomysettings.h index 82b3b193e..cf222b5eb 100644 --- a/plugins/channelrx/radioastronomy/radioastronomysettings.h +++ b/plugins/channelrx/radioastronomy/radioastronomysettings.h @@ -223,6 +223,8 @@ struct RadioAstronomySettings uint16_t m_reverseAPIPort; uint16_t m_reverseAPIDeviceIndex; uint16_t m_reverseAPIChannelIndex; + int m_workspaceIndex; + QByteArray m_geometryBytes; int m_powerTableColumnIndexes[RADIOASTRONOMY_POWERTABLE_COLUMNS];//!< How the columns are ordered in the table int m_powerTableColumnSizes[RADIOASTRONOMY_POWERTABLE_COLUMNS]; //!< Size of the columns in the table diff --git a/plugins/channelrx/radioclock/radioclockgui.cpp b/plugins/channelrx/radioclock/radioclockgui.cpp index 5e544cff7..19000cd14 100644 --- a/plugins/channelrx/radioclock/radioclockgui.cpp +++ b/plugins/channelrx/radioclock/radioclockgui.cpp @@ -158,7 +158,7 @@ void RadioClockGUI::channelMarkerChangedByCursor() void RadioClockGUI::channelMarkerHighlightedByCursor() { - setHighlighted(m_channelMarker.getHighlighted()); + getRollupContents()->setHighlighted(m_channelMarker.getHighlighted()); } void RadioClockGUI::on_deltaFrequency_changed(qint64 value) @@ -211,7 +211,7 @@ void RadioClockGUI::onWidgetRolled(QWidget* widget, bool rollDown) } } - saveState(m_rollupState); + getRollupContents()->saveState(m_rollupState); applySettings(); } @@ -237,6 +237,7 @@ void RadioClockGUI::onMenuDialogCalled(const QPoint &p) m_settings.m_reverseAPIChannelIndex = dialog.getReverseAPIChannelIndex(); setWindowTitle(m_settings.m_title); + setTitle(m_channelMarker.getTitle()); setTitleColor(m_settings.m_rgbColor); applySettings(); @@ -268,11 +269,12 @@ RadioClockGUI::RadioClockGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Bas m_doApplySettings(true), m_tickCount(0) { - ui->setupUi(this); + ui->setupUi(getRollupContents()); + getRollupContents()->arrangeRollups(); m_helpURL = "plugins/channelrx/radioclock/readme.md"; setAttribute(Qt::WA_DeleteOnClose, true); - connect(this, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + connect(getRollupContents(), SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); m_radioClock = reinterpret_cast(rxChannel); m_radioClock->setMessageQueueToGUI(getInputMessageQueue()); @@ -318,6 +320,7 @@ RadioClockGUI::RadioClockGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Bas ui->scopeContainer->setVisible(false); displaySettings(); + makeUIConnections(); applySettings(true); } @@ -351,6 +354,7 @@ void RadioClockGUI::displaySettings() setTitleColor(m_settings.m_rgbColor); setWindowTitle(m_channelMarker.getTitle()); + setTitle(m_channelMarker.getTitle()); blockApplySettings(true); @@ -367,7 +371,7 @@ void RadioClockGUI::displaySettings() displayStreamIndex(); - restoreState(m_rollupState); + getRollupContents()->restoreState(m_rollupState); blockApplySettings(false); } @@ -409,3 +413,12 @@ void RadioClockGUI::tick() m_tickCount++; } + +void RadioClockGUI::makeUIConnections() +{ + QObject::connect(ui->deltaFrequency, &ValueDialZ::changed, this, &RadioClockGUI::on_deltaFrequency_changed); + QObject::connect(ui->rfBW, &QSlider::valueChanged, this, &RadioClockGUI::on_rfBW_valueChanged); + QObject::connect(ui->threshold, &QDial::valueChanged, this, &RadioClockGUI::on_threshold_valueChanged); + QObject::connect(ui->modulation, QOverload::of(&QComboBox::currentIndexChanged), this, &RadioClockGUI::on_modulation_currentIndexChanged); + QObject::connect(ui->timezone, QOverload::of(&QComboBox::currentIndexChanged), this, &RadioClockGUI::on_timezone_currentIndexChanged); +} diff --git a/plugins/channelrx/radioclock/radioclockgui.h b/plugins/channelrx/radioclock/radioclockgui.h index 827a6340c..8d1956559 100644 --- a/plugins/channelrx/radioclock/radioclockgui.h +++ b/plugins/channelrx/radioclock/radioclockgui.h @@ -51,6 +51,12 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } + virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; }; + virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; }; + virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; }; + virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; }; + virtual QString getTitle() const { return m_settings.m_title; }; + virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; public slots: void channelMarkerChangedByCursor(); @@ -80,6 +86,8 @@ private: void displaySettings(); void displayStreamIndex(); bool handleMessage(const Message& message); + void makeUIConnections(); + void displayDateTime(); void leaveEvent(QEvent*); diff --git a/plugins/channelrx/radioclock/radioclockgui.ui b/plugins/channelrx/radioclock/radioclockgui.ui index 82c5952ef..4f3d6cd2d 100644 --- a/plugins/channelrx/radioclock/radioclockgui.ui +++ b/plugins/channelrx/radioclock/radioclockgui.ui @@ -1,7 +1,7 @@ RadioClockGUI - + 0 @@ -638,9 +638,9 @@ - RollupWidget + RollupContents QWidget -
gui/rollupwidget.h
+
gui/rollupcontents.h
1
diff --git a/plugins/channelrx/radioclock/radioclocksettings.cpp b/plugins/channelrx/radioclock/radioclocksettings.cpp index 1cca135e8..5af35acf0 100644 --- a/plugins/channelrx/radioclock/radioclocksettings.cpp +++ b/plugins/channelrx/radioclock/radioclocksettings.cpp @@ -46,6 +46,7 @@ void RadioClockSettings::resetToDefaults() m_reverseAPIPort = 8888; m_reverseAPIDeviceIndex = 0; m_reverseAPIChannelIndex = 0; + m_workspaceIndex = 0; } QByteArray RadioClockSettings::serialize() const @@ -79,6 +80,9 @@ QByteArray RadioClockSettings::serialize() const s.writeBlob(22, m_rollupState->serialize()); } + s.writeS32(23, m_workspaceIndex); + s.writeBlob(24, m_geometryBytes); + return s.final(); } @@ -140,6 +144,9 @@ bool RadioClockSettings::deserialize(const QByteArray& data) m_rollupState->deserialize(bytetmp); } + d.readS32(23, &m_workspaceIndex, 0); + d.readBlob(24, &m_geometryBytes); + return true; } else diff --git a/plugins/channelrx/radioclock/radioclocksettings.h b/plugins/channelrx/radioclock/radioclocksettings.h index 317be4503..80f01ada1 100644 --- a/plugins/channelrx/radioclock/radioclocksettings.h +++ b/plugins/channelrx/radioclock/radioclocksettings.h @@ -54,6 +54,9 @@ struct RadioClockSettings uint16_t m_reverseAPIChannelIndex; Serializable *m_scopeGUI; Serializable *m_rollupState; + int m_workspaceIndex; + QByteArray m_geometryBytes; + static const int RADIOCLOCK_CHANNEL_SAMPLE_RATE = 1000; static const int m_scopeStreams = 8; diff --git a/plugins/channelrx/remotesink/remotesinkgui.cpp b/plugins/channelrx/remotesink/remotesinkgui.cpp index 01e5f4600..fee089eb3 100644 --- a/plugins/channelrx/remotesink/remotesinkgui.cpp +++ b/plugins/channelrx/remotesink/remotesinkgui.cpp @@ -99,10 +99,11 @@ RemoteSinkGUI::RemoteSinkGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Bas m_basebandSampleRate(0), m_tickCount(0) { - ui->setupUi(this); + ui->setupUi(getRollupContents()); + getRollupContents()->arrangeRollups(); m_helpURL = "plugins/channelrx/remotesink/readme.md"; setAttribute(Qt::WA_DeleteOnClose, true); - connect(this, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + connect(getRollupContents(), SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &))); m_remoteSink = (RemoteSink*) channelrx; @@ -125,6 +126,7 @@ RemoteSinkGUI::RemoteSinkGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Bas connect(getInputMessageQueue(), SIGNAL(messageEnqueued()), this, SLOT(handleSourceMessages())); displaySettings(); + makeUIConnections(); applySettings(true); } @@ -161,6 +163,7 @@ void RemoteSinkGUI::displaySettings() setTitleColor(m_settings.m_rgbColor); setWindowTitle(m_channelMarker.getTitle()); + setTitle(m_channelMarker.getTitle()); blockApplySettings(true); ui->decimationFactor->setCurrentIndex(m_settings.m_log2Decim); @@ -172,7 +175,7 @@ void RemoteSinkGUI::displaySettings() ui->nbTxBytes->setCurrentIndex(log2(m_settings.m_nbTxBytes)); applyDecimation(); displayStreamIndex(); - restoreState(m_rollupState); + getRollupContents()->restoreState(m_rollupState); blockApplySettings(false); } @@ -224,7 +227,7 @@ void RemoteSinkGUI::onWidgetRolled(QWidget* widget, bool rollDown) (void) widget; (void) rollDown; - saveState(m_rollupState); + getRollupContents()->saveState(m_rollupState); applySettings(); } @@ -251,6 +254,7 @@ void RemoteSinkGUI::onMenuDialogCalled(const QPoint &p) m_settings.m_reverseAPIChannelIndex = dialog.getReverseAPIChannelIndex(); setWindowTitle(m_settings.m_title); + setTitle(m_channelMarker.getTitle()); setTitleColor(m_settings.m_rgbColor); applySettings(); @@ -372,3 +376,14 @@ void RemoteSinkGUI::tick() m_tickCount = 0; } } + +void RemoteSinkGUI::makeUIConnections() +{ + QObject::connect(ui->decimationFactor, QOverload::of(&QComboBox::currentIndexChanged), this, &RemoteSinkGUI::on_decimationFactor_currentIndexChanged); + QObject::connect(ui->position, &QSlider::valueChanged, this, &RemoteSinkGUI::on_position_valueChanged); + QObject::connect(ui->dataAddress, &QLineEdit::returnPressed, this, &RemoteSinkGUI::on_dataAddress_returnPressed); + QObject::connect(ui->dataPort, &QLineEdit::returnPressed, this, &RemoteSinkGUI::on_dataPort_returnPressed); + QObject::connect(ui->dataApplyButton, &QPushButton::clicked, this, &RemoteSinkGUI::on_dataApplyButton_clicked); + QObject::connect(ui->nbFECBlocks, &QDial::valueChanged, this, &RemoteSinkGUI::on_nbFECBlocks_valueChanged); + QObject::connect(ui->nbTxBytes, QOverload::of(&QComboBox::currentIndexChanged), this, &RemoteSinkGUI::on_nbTxBytes_currentIndexChanged); +} diff --git a/plugins/channelrx/remotesink/remotesinkgui.h b/plugins/channelrx/remotesink/remotesinkgui.h index b0361fa82..5c5104a48 100644 --- a/plugins/channelrx/remotesink/remotesinkgui.h +++ b/plugins/channelrx/remotesink/remotesinkgui.h @@ -48,6 +48,12 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } + virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; } + virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; } + virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; } + virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; } + virtual QString getTitle() const { return m_settings.m_title; } + virtual QColor getTitleColor() const { return m_settings.m_rgbColor; } private: Ui::RemoteSinkGUI* ui; @@ -75,6 +81,7 @@ private: void displayStreamIndex(); void displayRateAndShift(); bool handleMessage(const Message& message); + void makeUIConnections(); void leaveEvent(QEvent*); void enterEvent(QEvent*); diff --git a/plugins/channelrx/remotesink/remotesinkgui.ui b/plugins/channelrx/remotesink/remotesinkgui.ui index ec163ad0b..9f9d3526f 100644 --- a/plugins/channelrx/remotesink/remotesinkgui.ui +++ b/plugins/channelrx/remotesink/remotesinkgui.ui @@ -1,7 +1,7 @@ RemoteSinkGUI - + 0 @@ -450,9 +450,9 @@ - RollupWidget + RollupContents QWidget -
gui/rollupwidget.h
+
gui/rollupcontents.h
1
diff --git a/plugins/channelrx/remotesink/remotesinksettings.cpp b/plugins/channelrx/remotesink/remotesinksettings.cpp index 1181a41ce..5cacc97b0 100644 --- a/plugins/channelrx/remotesink/remotesinksettings.cpp +++ b/plugins/channelrx/remotesink/remotesinksettings.cpp @@ -53,6 +53,7 @@ void RemoteSinkSettings::resetToDefaults() m_reverseAPIPort = 8888; m_reverseAPIDeviceIndex = 0; m_reverseAPIChannelIndex = 0; + m_workspaceIndex = 0; } QByteArray RemoteSinkSettings::serialize() const @@ -83,6 +84,9 @@ QByteArray RemoteSinkSettings::serialize() const s.writeBlob(17, m_channelMarker->serialize()); } + s.writeS32(18, m_workspaceIndex); + s.writeBlob(19, m_geometryBytes); + return s.final(); } @@ -155,6 +159,9 @@ bool RemoteSinkSettings::deserialize(const QByteArray& data) m_channelMarker->deserialize(bytetmp); } + d.readS32(18, &m_workspaceIndex, 0); + d.readBlob(19, &m_geometryBytes); + return true; } else diff --git a/plugins/channelrx/remotesink/remotesinksettings.h b/plugins/channelrx/remotesink/remotesinksettings.h index 2828ba48d..0804097c1 100644 --- a/plugins/channelrx/remotesink/remotesinksettings.h +++ b/plugins/channelrx/remotesink/remotesinksettings.h @@ -46,6 +46,8 @@ struct RemoteSinkSettings uint16_t m_reverseAPIPort; uint16_t m_reverseAPIDeviceIndex; uint16_t m_reverseAPIChannelIndex; + int m_workspaceIndex; + QByteArray m_geometryBytes; Serializable *m_channelMarker; Serializable *m_rollupState; diff --git a/plugins/channelrx/sigmffilesink/sigmffilesinkgui.cpp b/plugins/channelrx/sigmffilesink/sigmffilesinkgui.cpp index 0a279d12f..f7fda943b 100644 --- a/plugins/channelrx/sigmffilesink/sigmffilesinkgui.cpp +++ b/plugins/channelrx/sigmffilesink/sigmffilesinkgui.cpp @@ -171,10 +171,11 @@ SigMFFileSinkGUI::SigMFFileSinkGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISe m_fixedPosition(false), m_tickCount(0) { - ui->setupUi(this); + ui->setupUi(getRollupContents()); + getRollupContents()->arrangeRollups(); m_helpURL = "plugins/channelrx/sigmffilesink/readme.md"; setAttribute(Qt::WA_DeleteOnClose, true); - connect(this, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + connect(getRollupContents(), SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &))); m_sigMFFileSink = (SigMFFileSink*) channelrx; @@ -209,6 +210,7 @@ SigMFFileSinkGUI::SigMFFileSinkGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISe connect(&(m_deviceUISet->m_deviceAPI->getMasterTimer()), SIGNAL(timeout()), this, SLOT(tick())); displaySettings(); + makeUIConnections(); applySettings(true); } @@ -244,6 +246,7 @@ void SigMFFileSinkGUI::displaySettings() setTitleColor(m_settings.m_rgbColor); setWindowTitle(m_channelMarker.getTitle()); + setTitle(m_channelMarker.getTitle()); blockApplySettings(true); @@ -273,7 +276,7 @@ void SigMFFileSinkGUI::displaySettings() displayStreamIndex(); setPosFromFrequency(); - restoreState(m_rollupState); + getRollupContents()->restoreState(m_rollupState); blockApplySettings(false); } @@ -323,7 +326,7 @@ void SigMFFileSinkGUI::channelMarkerChangedByCursor() void SigMFFileSinkGUI::channelMarkerHighlightedByCursor() { - setHighlighted(m_channelMarker.getHighlighted()); + getRollupContents()->setHighlighted(m_channelMarker.getHighlighted()); } void SigMFFileSinkGUI::handleSourceMessages() @@ -344,7 +347,7 @@ void SigMFFileSinkGUI::onWidgetRolled(QWidget* widget, bool rollDown) (void) widget; (void) rollDown; - saveState(m_rollupState); + getRollupContents()->saveState(m_rollupState); applySettings(); } @@ -371,6 +374,7 @@ void SigMFFileSinkGUI::onMenuDialogCalled(const QPoint &p) m_settings.m_reverseAPIChannelIndex = dialog.getReverseAPIChannelIndex(); setWindowTitle(m_settings.m_title); + setTitle(m_channelMarker.getTitle()); setTitleColor(m_settings.m_rgbColor); applySettings(); @@ -586,3 +590,17 @@ QString SigMFFileSinkGUI::displayScaled(uint64_t value, int precision) } } +void SigMFFileSinkGUI::makeUIConnections() +{ + QObject::connect(ui->deltaFrequency, &ValueDialZ::changed, this, &SigMFFileSinkGUI::on_deltaFrequency_changed); + QObject::connect(ui->decimationFactor, QOverload::of(&QComboBox::currentIndexChanged), this, &SigMFFileSinkGUI::on_decimationFactor_currentIndexChanged); + QObject::connect(ui->fixedPosition, &QCheckBox::toggled, this, &SigMFFileSinkGUI::on_fixedPosition_toggled); + QObject::connect(ui->position, &QSlider::valueChanged, this, &SigMFFileSinkGUI::on_position_valueChanged); + QObject::connect(ui->spectrumSquelch, &ButtonSwitch::toggled, this, &SigMFFileSinkGUI::on_spectrumSquelch_toggled); + QObject::connect(ui->squelchLevel, &QDial::valueChanged, this, &SigMFFileSinkGUI::on_squelchLevel_valueChanged); + QObject::connect(ui->preRecordTime, &QDial::valueChanged, this, &SigMFFileSinkGUI::on_preRecordTime_valueChanged); + QObject::connect(ui->postSquelchTime, &QDial::valueChanged, this, &SigMFFileSinkGUI::on_postSquelchTime_valueChanged); + QObject::connect(ui->squelchedRecording, &ButtonSwitch::toggled, this, &SigMFFileSinkGUI::on_squelchedRecording_toggled); + QObject::connect(ui->record, &ButtonSwitch::toggled, this, &SigMFFileSinkGUI::on_record_toggled); + QObject::connect(ui->showFileDialog, &QPushButton::clicked, this, &SigMFFileSinkGUI::on_showFileDialog_clicked); +} diff --git a/plugins/channelrx/sigmffilesink/sigmffilesinkgui.h b/plugins/channelrx/sigmffilesink/sigmffilesinkgui.h index 654b53124..8aadef404 100644 --- a/plugins/channelrx/sigmffilesink/sigmffilesinkgui.h +++ b/plugins/channelrx/sigmffilesink/sigmffilesinkgui.h @@ -49,6 +49,12 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } + virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; }; + virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; }; + virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; }; + virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; }; + virtual QString getTitle() const { return m_settings.m_title; }; + virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; public slots: void channelMarkerChangedByCursor(); @@ -88,6 +94,7 @@ private: void setPosFromFrequency(); QString displayScaled(uint64_t value, int precision); bool handleMessage(const Message& message); + void makeUIConnections(); void leaveEvent(QEvent*); void enterEvent(QEvent*); diff --git a/plugins/channelrx/sigmffilesink/sigmffilesinkgui.ui b/plugins/channelrx/sigmffilesink/sigmffilesinkgui.ui index dcba5229a..b2e55f758 100644 --- a/plugins/channelrx/sigmffilesink/sigmffilesinkgui.ui +++ b/plugins/channelrx/sigmffilesink/sigmffilesinkgui.ui @@ -1,7 +1,7 @@ SigMFFileSinkGUI - + 0 @@ -545,9 +545,9 @@ - RollupWidget + RollupContents QWidget -
gui/rollupwidget.h
+
gui/rollupcontents.h
1
diff --git a/plugins/channelrx/sigmffilesink/sigmffilesinksettings.cpp b/plugins/channelrx/sigmffilesink/sigmffilesinksettings.cpp index aa785a33b..0ea2a2b39 100644 --- a/plugins/channelrx/sigmffilesink/sigmffilesinksettings.cpp +++ b/plugins/channelrx/sigmffilesink/sigmffilesinksettings.cpp @@ -49,6 +49,7 @@ void SigMFFileSinkSettings::resetToDefaults() m_reverseAPIPort = 8888; m_reverseAPIDeviceIndex = 0; m_reverseAPIChannelIndex = 0; + m_workspaceIndex = 0; } QByteArray SigMFFileSinkSettings::serialize() const @@ -85,6 +86,9 @@ QByteArray SigMFFileSinkSettings::serialize() const s.writeBlob(20, m_channelMarker->serialize()); } + s.writeS32(21, m_workspaceIndex); + s.writeBlob(22, m_geometryBytes); + return s.final(); } @@ -153,6 +157,9 @@ bool SigMFFileSinkSettings::deserialize(const QByteArray& data) m_channelMarker->deserialize(bytetmp); } + d.readS32(21, &m_workspaceIndex, 0); + d.readBlob(22, &m_geometryBytes); + return true; } else diff --git a/plugins/channelrx/sigmffilesink/sigmffilesinksettings.h b/plugins/channelrx/sigmffilesink/sigmffilesinksettings.h index dea649745..470f00b15 100644 --- a/plugins/channelrx/sigmffilesink/sigmffilesinksettings.h +++ b/plugins/channelrx/sigmffilesink/sigmffilesinksettings.h @@ -42,6 +42,8 @@ struct SigMFFileSinkSettings uint16_t m_reverseAPIPort; uint16_t m_reverseAPIDeviceIndex; uint16_t m_reverseAPIChannelIndex; + int m_workspaceIndex; + QByteArray m_geometryBytes; Serializable *m_spectrumGUI; Serializable *m_channelMarker; diff --git a/plugins/channelrx/udpsink/udpsinkgui.cpp b/plugins/channelrx/udpsink/udpsinkgui.cpp index f64f517c3..8010b2d41 100644 --- a/plugins/channelrx/udpsink/udpsinkgui.cpp +++ b/plugins/channelrx/udpsink/udpsinkgui.cpp @@ -109,7 +109,7 @@ void UDPSinkGUI::channelMarkerChangedByCursor() void UDPSinkGUI::channelMarkerHighlightedByCursor() { - setHighlighted(m_channelMarker.getHighlighted()); + getRollupContents()->setHighlighted(m_channelMarker.getHighlighted()); } void UDPSinkGUI::tick() @@ -147,9 +147,10 @@ UDPSinkGUI::UDPSinkGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandS m_doApplySettings(true), m_rfBandwidthChanged(false) { - ui->setupUi(this); + ui->setupUi(getRollupContents()); + getRollupContents()->arrangeRollups(); m_helpURL = "plugins/channelrx/udpsink/readme.md"; - connect(this, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + connect(getRollupContents(), SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &))); setAttribute(Qt::WA_DeleteOnClose, true); @@ -196,6 +197,7 @@ UDPSinkGUI::UDPSinkGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandS ui->spectrumGUI->setBuddies(m_spectrumVis, ui->glSpectrum); displaySettings(); + makeUIConnections(); applySettingsImmediate(true); applySettings(true); } @@ -221,6 +223,7 @@ void UDPSinkGUI::displaySettings() setTitleColor(m_settings.m_rgbColor); setWindowTitle(m_channelMarker.getTitle()); + setTitle(m_channelMarker.getTitle()); blockApplySettings(true); @@ -258,7 +261,7 @@ void UDPSinkGUI::displaySettings() displayStreamIndex(); - restoreState(m_rollupState); + getRollupContents()->restoreState(m_rollupState); blockApplySettings(false); ui->glSpectrum->setSampleRate(m_settings.m_outputSampleRate); @@ -590,7 +593,7 @@ void UDPSinkGUI::onWidgetRolled(QWidget* widget, bool rollDown) m_udpSink->enableSpectrum(rollDown); } - saveState(m_rollupState); + getRollupContents()->saveState(m_rollupState); applySettings(); } @@ -618,6 +621,7 @@ void UDPSinkGUI::onMenuDialogCalled(const QPoint &p) m_settings.m_reverseAPIChannelIndex = dialog.getReverseAPIChannelIndex(); setWindowTitle(m_settings.m_title); + setTitle(m_channelMarker.getTitle()); setTitleColor(m_settings.m_rgbColor); applySettingsImmediate(); @@ -650,4 +654,22 @@ void UDPSinkGUI::enterEvent(QEvent*) m_channelMarker.setHighlighted(true); } - +void UDPSinkGUI::makeUIConnections() +{ + QObject::connect(ui->deltaFrequency, &ValueDialZ::changed, this, &UDPSinkGUI::on_deltaFrequency_changed); + QObject::connect(ui->sampleFormat, QOverload::of(&QComboBox::currentIndexChanged), this, &UDPSinkGUI::on_sampleFormat_currentIndexChanged); + QObject::connect(ui->outputUDPAddress, &QLineEdit::editingFinished, this, &UDPSinkGUI::on_outputUDPAddress_editingFinished); + QObject::connect(ui->outputUDPPort, &QLineEdit::editingFinished, this, &UDPSinkGUI::on_outputUDPPort_editingFinished); + QObject::connect(ui->inputUDPAudioPort, &QLineEdit::editingFinished, this, &UDPSinkGUI::on_inputUDPAudioPort_editingFinished); + QObject::connect(ui->sampleRate, &QLineEdit::textEdited, this, &UDPSinkGUI::on_sampleRate_textEdited); + QObject::connect(ui->rfBandwidth, &QLineEdit::textEdited, this, &UDPSinkGUI::on_rfBandwidth_textEdited); + QObject::connect(ui->fmDeviation, &QLineEdit::textEdited, this, &UDPSinkGUI::on_fmDeviation_textEdited); + QObject::connect(ui->audioActive, &QToolButton::toggled, this, &UDPSinkGUI::on_audioActive_toggled); + QObject::connect(ui->audioStereo, &QToolButton::toggled, this, &UDPSinkGUI::on_audioStereo_toggled); + QObject::connect(ui->applyBtn, &QPushButton::clicked, this, &UDPSinkGUI::on_applyBtn_clicked); + QObject::connect(ui->gain, &QSlider::valueChanged, this, &UDPSinkGUI::on_gain_valueChanged); + QObject::connect(ui->volume, &QSlider::valueChanged, this, &UDPSinkGUI::on_volume_valueChanged); + QObject::connect(ui->squelch, &QSlider::valueChanged, this, &UDPSinkGUI::on_squelch_valueChanged); + QObject::connect(ui->squelchGate, &QDial::valueChanged, this, &UDPSinkGUI::on_squelchGate_valueChanged); + QObject::connect(ui->agc, &ButtonSwitch::toggled, this, &UDPSinkGUI::on_agc_toggled); +} diff --git a/plugins/channelrx/udpsink/udpsinkgui.h b/plugins/channelrx/udpsink/udpsinkgui.h index dd2fd6828..c06e0f99b 100644 --- a/plugins/channelrx/udpsink/udpsinkgui.h +++ b/plugins/channelrx/udpsink/udpsinkgui.h @@ -49,6 +49,12 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } + virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; }; + virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; }; + virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; }; + virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; }; + virtual QString getTitle() const { return m_settings.m_title; }; + virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; public slots: void channelMarkerChangedByCursor(); @@ -85,6 +91,7 @@ private: void setSampleFormat(int index); void setSampleFormatIndex(const UDPSinkSettings::SampleFormat& sampleFormat); bool handleMessage(const Message& message); + void makeUIConnections(); void leaveEvent(QEvent*); void enterEvent(QEvent*); diff --git a/plugins/channelrx/udpsink/udpsinkgui.ui b/plugins/channelrx/udpsink/udpsinkgui.ui index 1bd129907..ea438e23c 100644 --- a/plugins/channelrx/udpsink/udpsinkgui.ui +++ b/plugins/channelrx/udpsink/udpsinkgui.ui @@ -1,7 +1,7 @@ UDPSinkGUI - + 0 @@ -862,9 +862,9 @@ - RollupWidget + RollupContents QWidget -
gui/rollupwidget.h
+
gui/rollupcontents.h
1
diff --git a/plugins/channelrx/udpsink/udpsinksettings.cpp b/plugins/channelrx/udpsink/udpsinksettings.cpp index 169fc129f..385db5077 100644 --- a/plugins/channelrx/udpsink/udpsinksettings.cpp +++ b/plugins/channelrx/udpsink/udpsinksettings.cpp @@ -57,6 +57,7 @@ void UDPSinkSettings::resetToDefaults() m_reverseAPIPort = 8888; m_reverseAPIDeviceIndex = 0; m_reverseAPIChannelIndex = 0; + m_workspaceIndex = 0; } QByteArray UDPSinkSettings::serialize() const @@ -99,6 +100,9 @@ QByteArray UDPSinkSettings::serialize() const s.writeBlob(29, m_rollupState->serialize()); } + s.writeS32(30, m_workspaceIndex); + s.writeBlob(31, m_geometryBytes); + return s.final(); } @@ -197,6 +201,9 @@ bool UDPSinkSettings::deserialize(const QByteArray& data) m_rollupState->deserialize(bytetmp); } + d.readS32(29, &m_workspaceIndex, 0); + d.readBlob(30, &m_geometryBytes); + return true; } else diff --git a/plugins/channelrx/udpsink/udpsinksettings.h b/plugins/channelrx/udpsink/udpsinksettings.h index b24e23385..72196f7ba 100644 --- a/plugins/channelrx/udpsink/udpsinksettings.h +++ b/plugins/channelrx/udpsink/udpsinksettings.h @@ -69,6 +69,8 @@ struct UDPSinkSettings uint16_t m_reverseAPIPort; uint16_t m_reverseAPIDeviceIndex; uint16_t m_reverseAPIChannelIndex; + int m_workspaceIndex; + QByteArray m_geometryBytes; Serializable *m_channelMarker; Serializable *m_spectrumGUI; diff --git a/plugins/channeltx/filesource/filesourcegui.cpp b/plugins/channeltx/filesource/filesourcegui.cpp index fd2cf66f6..06e0217d1 100644 --- a/plugins/channeltx/filesource/filesourcegui.cpp +++ b/plugins/channeltx/filesource/filesourcegui.cpp @@ -179,12 +179,13 @@ FileSourceGUI::FileSourceGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Bas { (void) channelTx; - ui->setupUi(this); + ui->setupUi(getRollupContents()); + getRollupContents()->arrangeRollups(); m_helpURL = "plugins/channeltx/filesource/readme.md"; ui->channelPowerMeter->setColorTheme(LevelMeterSignalDB::ColorGreenAndBlue); setAttribute(Qt::WA_DeleteOnClose, true); - connect(this, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + connect(getRollupContents(), SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &))); m_fileSource = (FileSource*) channelTx; @@ -211,6 +212,7 @@ FileSourceGUI::FileSourceGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Bas connect(getInputMessageQueue(), SIGNAL(messageEnqueued()), this, SLOT(handleSourceMessages())); displaySettings(); + makeUIConnections(); applySettings(true); } @@ -302,6 +304,8 @@ void FileSourceGUI::displaySettings() setTitleColor(m_settings.m_rgbColor); setWindowTitle(m_channelMarker.getTitle()); + setTitle(m_channelMarker.getTitle()); + displayStreamIndex(); blockApplySettings(true); @@ -310,7 +314,7 @@ void FileSourceGUI::displaySettings() ui->gainText->setText(tr("%1 dB").arg(m_settings.m_gainDB)); ui->interpolationFactor->setCurrentIndex(m_settings.m_log2Interp); applyInterpolation(); - restoreState(m_rollupState); + getRollupContents()->restoreState(m_rollupState); blockApplySettings(false); } @@ -362,7 +366,7 @@ void FileSourceGUI::onWidgetRolled(QWidget* widget, bool rollDown) (void) widget; (void) rollDown; - saveState(m_rollupState); + getRollupContents()->saveState(m_rollupState); applySettings(); } @@ -525,3 +529,14 @@ void FileSourceGUI::tick() void FileSourceGUI::channelMarkerChangedByCursor() { } + +void FileSourceGUI::makeUIConnections() +{ + QObject::connect(ui->interpolationFactor, QOverload::of(&QComboBox::currentIndexChanged), this, &FileSourceGUI::on_interpolationFactor_currentIndexChanged); + QObject::connect(ui->position, &QSlider::valueChanged, this, &FileSourceGUI::on_position_valueChanged); + QObject::connect(ui->gain, &QSlider::valueChanged, this, &FileSourceGUI::on_gain_valueChanged); + QObject::connect(ui->showFileDialog, &QPushButton::clicked, this, &FileSourceGUI::on_showFileDialog_clicked); + QObject::connect(ui->playLoop, &ButtonSwitch::toggled, this, &FileSourceGUI::on_playLoop_toggled); + QObject::connect(ui->play, &ButtonSwitch::toggled, this, &FileSourceGUI::on_play_toggled); + QObject::connect(ui->navTime, &QSlider::valueChanged, this, &FileSourceGUI::on_navTime_valueChanged); +} diff --git a/plugins/channeltx/filesource/filesourcegui.h b/plugins/channeltx/filesource/filesourcegui.h index 2e03655ca..c748afd31 100644 --- a/plugins/channeltx/filesource/filesourcegui.h +++ b/plugins/channeltx/filesource/filesourcegui.h @@ -45,6 +45,12 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } + virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; }; + virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; }; + virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; }; + virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; }; + virtual QString getTitle() const { return m_settings.m_title; }; + virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; public slots: void channelMarkerChangedByCursor(); @@ -85,6 +91,7 @@ private: void displayRateAndShift(); void displayStreamIndex(); bool handleMessage(const Message& message); + void makeUIConnections(); void leaveEvent(QEvent*); void enterEvent(QEvent*); diff --git a/plugins/channeltx/filesource/filesourcegui.ui b/plugins/channeltx/filesource/filesourcegui.ui index eaaeee84a..8ae7b0701 100644 --- a/plugins/channeltx/filesource/filesourcegui.ui +++ b/plugins/channeltx/filesource/filesourcegui.ui @@ -1,7 +1,7 @@ FileSourceGUI - + 0 @@ -696,9 +696,9 @@ - RollupWidget + RollupContents QWidget -
gui/rollupwidget.h
+
gui/rollupcontents.h
1
diff --git a/plugins/channeltx/filesource/filesourcesettings.cpp b/plugins/channeltx/filesource/filesourcesettings.cpp index 019e4bd1e..b249ae363 100644 --- a/plugins/channeltx/filesource/filesourcesettings.cpp +++ b/plugins/channeltx/filesource/filesourcesettings.cpp @@ -45,6 +45,7 @@ void FileSourceSettings::resetToDefaults() m_reverseAPIPort = 8888; m_reverseAPIDeviceIndex = 0; m_reverseAPIChannelIndex = 0; + m_workspaceIndex = 0; } QByteArray FileSourceSettings::serialize() const @@ -72,6 +73,9 @@ QByteArray FileSourceSettings::serialize() const s.writeBlob(15, m_channelMarker->serialize()); } + s.writeS32(16, m_workspaceIndex); + s.writeBlob(17, m_geometryBytes); + return s.final(); } @@ -129,6 +133,9 @@ bool FileSourceSettings::deserialize(const QByteArray& data) m_channelMarker->deserialize(bytetmp); } + d.readS32(16, &m_workspaceIndex, 0); + d.readBlob(17, &m_geometryBytes); + return true; } else diff --git a/plugins/channeltx/filesource/filesourcesettings.h b/plugins/channeltx/filesource/filesourcesettings.h index 8225ce4c9..f20a0377f 100644 --- a/plugins/channeltx/filesource/filesourcesettings.h +++ b/plugins/channeltx/filesource/filesourcesettings.h @@ -38,6 +38,8 @@ struct FileSourceSettings uint16_t m_reverseAPIPort; uint16_t m_reverseAPIDeviceIndex; uint16_t m_reverseAPIChannelIndex; + int m_workspaceIndex; + QByteArray m_geometryBytes; Serializable *m_channelMarker; Serializable *m_rollupState; diff --git a/plugins/channeltx/localsource/localsourcegui.cpp b/plugins/channeltx/localsource/localsourcegui.cpp index f6a7a7fd1..00866ccea 100644 --- a/plugins/channeltx/localsource/localsourcegui.cpp +++ b/plugins/channeltx/localsource/localsourcegui.cpp @@ -95,10 +95,11 @@ LocalSourceGUI::LocalSourceGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, B m_basebandSampleRate(0), m_tickCount(0) { - ui->setupUi(this); + ui->setupUi(getRollupContents()); + getRollupContents()->arrangeRollups(); m_helpURL = "plugins/channeltx/localsource/readme.md"; setAttribute(Qt::WA_DeleteOnClose, true); - connect(this, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + connect(getRollupContents(), SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &))); m_localSource = (LocalSource*) channeltx; @@ -122,6 +123,7 @@ LocalSourceGUI::LocalSourceGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, B updateLocalDevices(); displaySettings(); + makeUIConnections(); applySettings(true); } @@ -158,13 +160,14 @@ void LocalSourceGUI::displaySettings() setTitleColor(m_settings.m_rgbColor); setWindowTitle(m_channelMarker.getTitle()); + setTitle(m_channelMarker.getTitle()); displayStreamIndex(); blockApplySettings(true); ui->interpolationFactor->setCurrentIndex(m_settings.m_log2Interp); ui->localDevicePlay->setChecked(m_settings.m_play); applyInterpolation(); - restoreState(m_rollupState); + getRollupContents()->restoreState(m_rollupState); blockApplySettings(false); } @@ -228,7 +231,7 @@ void LocalSourceGUI::onWidgetRolled(QWidget* widget, bool rollDown) (void) widget; (void) rollDown; - saveState(m_rollupState); + getRollupContents()->saveState(m_rollupState); applySettings(); } @@ -255,6 +258,7 @@ void LocalSourceGUI::onMenuDialogCalled(const QPoint &p) m_settings.m_reverseAPIChannelIndex = dialog.getReverseAPIChannelIndex(); setWindowTitle(m_settings.m_title); + setTitle(m_channelMarker.getTitle()); setTitleColor(m_settings.m_rgbColor); applySettings(); @@ -338,3 +342,12 @@ void LocalSourceGUI::tick() m_tickCount = 0; } } + +void LocalSourceGUI::makeUIConnections() +{ + QObject::connect(ui->interpolationFactor, QOverload::of(&QComboBox::currentIndexChanged), this, &LocalSourceGUI::on_interpolationFactor_currentIndexChanged); + QObject::connect(ui->position, &QSlider::valueChanged, this, &LocalSourceGUI::on_position_valueChanged); + QObject::connect(ui->localDevice, QOverload::of(&QComboBox::currentIndexChanged), this, &LocalSourceGUI::on_localDevice_currentIndexChanged); + QObject::connect(ui->localDevicesRefresh, &QPushButton::clicked, this, &LocalSourceGUI::on_localDevicesRefresh_clicked); + QObject::connect(ui->localDevicePlay, &ButtonSwitch::toggled, this, &LocalSourceGUI::on_localDevicePlay_toggled); +} diff --git a/plugins/channeltx/localsource/localsourcegui.h b/plugins/channeltx/localsource/localsourcegui.h index c106998ac..69584295e 100644 --- a/plugins/channeltx/localsource/localsourcegui.h +++ b/plugins/channeltx/localsource/localsourcegui.h @@ -48,6 +48,12 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } + virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; }; + virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; }; + virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; }; + virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; }; + virtual QString getTitle() const { return m_settings.m_title; }; + virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; private: Ui::LocalSourceGUI* ui; @@ -76,6 +82,7 @@ private: void displayStreamIndex(); void updateLocalDevices(); bool handleMessage(const Message& message); + void makeUIConnections(); void leaveEvent(QEvent*); void enterEvent(QEvent*); diff --git a/plugins/channeltx/localsource/localsourcegui.ui b/plugins/channeltx/localsource/localsourcegui.ui index b62c51e3b..a4e0aa877 100644 --- a/plugins/channeltx/localsource/localsourcegui.ui +++ b/plugins/channeltx/localsource/localsourcegui.ui @@ -1,7 +1,7 @@ LocalSourceGUI - + 0 @@ -330,9 +330,9 @@ - RollupWidget + RollupContents QWidget -
gui/rollupwidget.h
+
gui/rollupcontents.h
1
diff --git a/plugins/channeltx/localsource/localsourcesettings.cpp b/plugins/channeltx/localsource/localsourcesettings.cpp index a5d7dea51..5e33c589d 100644 --- a/plugins/channeltx/localsource/localsourcesettings.cpp +++ b/plugins/channeltx/localsource/localsourcesettings.cpp @@ -44,6 +44,7 @@ void LocalSourceSettings::resetToDefaults() m_reverseAPIPort = 8888; m_reverseAPIDeviceIndex = 0; m_reverseAPIChannelIndex = 0; + m_workspaceIndex = 0; } QByteArray LocalSourceSettings::serialize() const @@ -69,6 +70,9 @@ QByteArray LocalSourceSettings::serialize() const s.writeBlob(16, m_channelMarker->serialize()); } + s.writeS32(17, m_workspaceIndex); + s.writeBlob(18, m_geometryBytes); + return s.final(); } @@ -122,6 +126,9 @@ bool LocalSourceSettings::deserialize(const QByteArray& data) m_channelMarker->deserialize(bytetmp); } + d.readS32(27, &m_workspaceIndex, 0); + d.readBlob(28, &m_geometryBytes); + return true; } else diff --git a/plugins/channeltx/localsource/localsourcesettings.h b/plugins/channeltx/localsource/localsourcesettings.h index 81740e0bd..b0b044d6b 100644 --- a/plugins/channeltx/localsource/localsourcesettings.h +++ b/plugins/channeltx/localsource/localsourcesettings.h @@ -37,6 +37,8 @@ struct LocalSourceSettings uint16_t m_reverseAPIPort; uint16_t m_reverseAPIDeviceIndex; uint16_t m_reverseAPIChannelIndex; + int m_workspaceIndex; + QByteArray m_geometryBytes; Serializable *m_channelMarker; Serializable *m_rollupState; diff --git a/plugins/channeltx/mod802.15.4/ieee_802_15_4_modgui.cpp b/plugins/channeltx/mod802.15.4/ieee_802_15_4_modgui.cpp index 773a8fcf1..b97f63e6f 100644 --- a/plugins/channeltx/mod802.15.4/ieee_802_15_4_modgui.cpp +++ b/plugins/channeltx/mod802.15.4/ieee_802_15_4_modgui.cpp @@ -310,18 +310,12 @@ void IEEE_802_15_4_ModGUI::on_udpPort_editingFinished() applySettings(); } -void IEEE_802_15_4_ModGUI::on_udpBytesFormat_clicked(bool checked) -{ - m_settings.m_udpBytesFormat = checked; - applySettings(); -} - void IEEE_802_15_4_ModGUI::onWidgetRolled(QWidget* widget, bool rollDown) { (void) widget; (void) rollDown; - saveState(m_rollupState); + getRollupContents()->saveState(m_rollupState); applySettings(); } @@ -348,6 +342,7 @@ void IEEE_802_15_4_ModGUI::onMenuDialogCalled(const QPoint &p) m_settings.m_reverseAPIChannelIndex = dialog.getReverseAPIChannelIndex(); setWindowTitle(m_settings.m_title); + setTitle(m_channelMarker.getTitle()); setTitleColor(m_settings.m_rgbColor); applySettings(); @@ -379,11 +374,12 @@ IEEE_802_15_4_ModGUI::IEEE_802_15_4_ModGUI(PluginAPI* pluginAPI, DeviceUISet *de m_doApplySettings(true), m_basebandSampleRate(12000000) { - ui->setupUi(this); + ui->setupUi(getRollupContents()); + getRollupContents()->arrangeRollups(); m_helpURL = "plugins/channeltx/mod802.15.4/readme.md"; setAttribute(Qt::WA_DeleteOnClose, true); - connect(this, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + connect(getRollupContents(), SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &))); m_IEEE_802_15_4_Mod = (IEEE_802_15_4_Mod*) channelTx; @@ -466,6 +462,7 @@ IEEE_802_15_4_ModGUI::IEEE_802_15_4_ModGUI(PluginAPI* pluginAPI, DeviceUISet *de ui->spectrumGUI->setBuddies(m_spectrumVis, ui->glSpectrum); displaySettings(); + makeUIConnections(); applySettings(); } @@ -507,6 +504,7 @@ void IEEE_802_15_4_ModGUI::displaySettings() setTitleColor(m_settings.m_rgbColor); setWindowTitle(m_channelMarker.getTitle()); + setTitle(m_channelMarker.getTitle()); displayStreamIndex(); blockApplySettings(true); @@ -567,7 +565,7 @@ void IEEE_802_15_4_ModGUI::displaySettings() ui->udpAddress->setText(m_settings.m_udpAddress); ui->udpPort->setText(QString::number(m_settings.m_udpPort)); - restoreState(m_rollupState); + getRollupContents()->restoreState(m_rollupState); blockApplySettings(false); } @@ -625,3 +623,19 @@ void IEEE_802_15_4_ModGUI::tick() m_channelPowerDbAvg(powDb); ui->channelPower->setText(tr("%1 dB").arg(m_channelPowerDbAvg.asDouble(), 0, 'f', 1)); } + +void IEEE_802_15_4_ModGUI::makeUIConnections() +{ + QObject::connect(ui->deltaFrequency, &ValueDialZ::changed, this, &IEEE_802_15_4_ModGUI::on_deltaFrequency_changed); + QObject::connect(ui->phy, QOverload::of(&QComboBox::currentIndexChanged), this, &IEEE_802_15_4_ModGUI::on_phy_currentIndexChanged); + QObject::connect(ui->rfBW, &QSlider::valueChanged, this, &IEEE_802_15_4_ModGUI::on_rfBW_valueChanged); + QObject::connect(ui->gain, &QDial::valueChanged, this, &IEEE_802_15_4_ModGUI::on_gain_valueChanged); + QObject::connect(ui->channelMute, &QToolButton::toggled, this, &IEEE_802_15_4_ModGUI::on_channelMute_toggled); + QObject::connect(ui->txButton, &QToolButton::clicked, this, &IEEE_802_15_4_ModGUI::on_txButton_clicked); + QObject::connect(ui->frame, &QLineEdit::editingFinished, this, &IEEE_802_15_4_ModGUI::on_frame_editingFinished); + QObject::connect(ui->frame, &QLineEdit::returnPressed, this, &IEEE_802_15_4_ModGUI::on_frame_returnPressed); + QObject::connect(ui->repeat, &ButtonSwitch::toggled, this, &IEEE_802_15_4_ModGUI::on_repeat_toggled); + QObject::connect(ui->udpEnabled, &QCheckBox::clicked, this, &IEEE_802_15_4_ModGUI::on_udpEnabled_clicked); + QObject::connect(ui->udpAddress, &QLineEdit::editingFinished, this, &IEEE_802_15_4_ModGUI::on_udpAddress_editingFinished); + QObject::connect(ui->udpPort, &QLineEdit::editingFinished, this, &IEEE_802_15_4_ModGUI::on_udpPort_editingFinished); +} diff --git a/plugins/channeltx/mod802.15.4/ieee_802_15_4_modgui.h b/plugins/channeltx/mod802.15.4/ieee_802_15_4_modgui.h index 041928bc4..1f96f2920 100644 --- a/plugins/channeltx/mod802.15.4/ieee_802_15_4_modgui.h +++ b/plugins/channeltx/mod802.15.4/ieee_802_15_4_modgui.h @@ -55,7 +55,13 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } - virtual bool handleMessage(const Message& message); + virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; }; + virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; }; + virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; }; + virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; }; + virtual QString getTitle() const { return m_settings.m_title; }; + virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; + public slots: void channelMarkerChangedByCursor(); @@ -89,6 +95,8 @@ private: void displayRFBandwidth(int bandwidth); void displayChipRate(const IEEE_802_15_4_ModSettings& settings); QString getDisplayValueWithMultiplier(int value); + bool handleMessage(const Message& message); + void makeUIConnections(); void leaveEvent(QEvent*); void enterEvent(QEvent*); @@ -110,7 +118,6 @@ private slots: void on_udpEnabled_clicked(bool checked); void on_udpAddress_editingFinished(); void on_udpPort_editingFinished(); - void on_udpBytesFormat_clicked(bool checked); void onWidgetRolled(QWidget* widget, bool rollDown); void onMenuDialogCalled(const QPoint& p); diff --git a/plugins/channeltx/mod802.15.4/ieee_802_15_4_modgui.ui b/plugins/channeltx/mod802.15.4/ieee_802_15_4_modgui.ui index 2cb001a38..6e565edc6 100644 --- a/plugins/channeltx/mod802.15.4/ieee_802_15_4_modgui.ui +++ b/plugins/channeltx/mod802.15.4/ieee_802_15_4_modgui.ui @@ -1,7 +1,7 @@ IEEE_802_15_4_ModGUI - + 0 @@ -738,9 +738,9 @@ - RollupWidget + RollupContents QWidget -
gui/rollupwidget.h
+
gui/rollupcontents.h
1
diff --git a/plugins/channeltx/mod802.15.4/ieee_802_15_4_modsettings.cpp b/plugins/channeltx/mod802.15.4/ieee_802_15_4_modsettings.cpp index 3540274ab..9d71d6c9a 100644 --- a/plugins/channeltx/mod802.15.4/ieee_802_15_4_modsettings.cpp +++ b/plugins/channeltx/mod802.15.4/ieee_802_15_4_modsettings.cpp @@ -75,6 +75,7 @@ void IEEE_802_15_4_ModSettings::resetToDefaults() m_udpBytesFormat = false; m_udpAddress = "127.0.0.1"; m_udpPort = 9998; + m_workspaceIndex = 0; } bool IEEE_802_15_4_ModSettings::setPHY(QString phy) @@ -202,6 +203,9 @@ QByteArray IEEE_802_15_4_ModSettings::serialize() const s.writeBlob(38, m_rollupState->serialize()); } + s.writeS32(39, m_workspaceIndex); + s.writeBlob(40, m_geometryBytes); + return s.final(); } @@ -288,6 +292,9 @@ bool IEEE_802_15_4_ModSettings::deserialize(const QByteArray& data) m_rollupState->deserialize(bytetmp); } + d.readS32(39, &m_workspaceIndex, 0); + d.readBlob(40, &m_geometryBytes); + return true; } else diff --git a/plugins/channeltx/mod802.15.4/ieee_802_15_4_modsettings.h b/plugins/channeltx/mod802.15.4/ieee_802_15_4_modsettings.h index 367570869..b50b69d6c 100644 --- a/plugins/channeltx/mod802.15.4/ieee_802_15_4_modsettings.h +++ b/plugins/channeltx/mod802.15.4/ieee_802_15_4_modsettings.h @@ -78,6 +78,8 @@ struct IEEE_802_15_4_ModSettings uint16_t m_udpPort; Serializable *m_rollupState; static const int m_udpBufferSize = 100000; + int m_workspaceIndex; + QByteArray m_geometryBytes; IEEE_802_15_4_ModSettings(); void resetToDefaults(); diff --git a/plugins/channeltx/modais/aismodgui.cpp b/plugins/channeltx/modais/aismodgui.cpp index bc616e74e..3c21a40fa 100644 --- a/plugins/channeltx/modais/aismodgui.cpp +++ b/plugins/channeltx/modais/aismodgui.cpp @@ -331,7 +331,7 @@ void AISModGUI::onWidgetRolled(QWidget* widget, bool rollDown) (void) widget; (void) rollDown; - saveState(m_rollupState); + getRollupContents()->saveState(m_rollupState); applySettings(); } @@ -358,6 +358,7 @@ void AISModGUI::onMenuDialogCalled(const QPoint &p) m_settings.m_reverseAPIChannelIndex = dialog.getReverseAPIChannelIndex(); setWindowTitle(m_settings.m_title); + setTitle(m_channelMarker.getTitle()); setTitleColor(m_settings.m_rgbColor); applySettings(); @@ -388,11 +389,12 @@ AISModGUI::AISModGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSam m_channelMarker(this), m_doApplySettings(true) { - ui->setupUi(this); + ui->setupUi(getRollupContents()); + getRollupContents()->arrangeRollups(); m_helpURL = "plugins/channeltx/modais/readme.md"; setAttribute(Qt::WA_DeleteOnClose, true); - connect(this, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + connect(getRollupContents(), SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &))); m_aisMod = (AISMod*) channelTx; @@ -478,6 +480,7 @@ AISModGUI::AISModGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSam ui->spectrumContainer->setVisible(false); displaySettings(); + makeUIConnections(); applySettings(); } @@ -518,6 +521,7 @@ void AISModGUI::displaySettings() setTitleColor(m_settings.m_rgbColor); setWindowTitle(m_channelMarker.getTitle()); + setTitle(m_channelMarker.getTitle()); displayStreamIndex(); blockApplySettings(true); @@ -552,7 +556,7 @@ void AISModGUI::displaySettings() ui->heading->setValue(m_settings.m_heading); ui->message->setText(m_settings.m_data); - restoreState(m_rollupState); + getRollupContents()->restoreState(m_rollupState); blockApplySettings(false); } @@ -581,3 +585,31 @@ void AISModGUI::tick() m_channelPowerDbAvg(powDb); ui->channelPower->setText(tr("%1 dB").arg(m_channelPowerDbAvg.asDouble(), 0, 'f', 1)); } + +void AISModGUI::makeUIConnections() +{ + QObject::connect(ui->deltaFrequency, &ValueDialZ::changed, this, &AISModGUI::on_deltaFrequency_changed); + QObject::connect(ui->mode, QOverload::of(&QComboBox::currentIndexChanged), this, &AISModGUI::on_mode_currentIndexChanged); + QObject::connect(ui->rfBW, &QSlider::valueChanged, this, &AISModGUI::on_rfBW_valueChanged); + QObject::connect(ui->fmDev, &QSlider::valueChanged, this, &AISModGUI::on_fmDev_valueChanged); + QObject::connect(ui->bt, &QSlider::valueChanged, this, &AISModGUI::on_bt_valueChanged); + QObject::connect(ui->gain, &QDial::valueChanged, this, &AISModGUI::on_gain_valueChanged); + QObject::connect(ui->channelMute, &QToolButton::toggled, this, &AISModGUI::on_channelMute_toggled); + QObject::connect(ui->txButton, &QPushButton::clicked, this, &AISModGUI::on_txButton_clicked); + QObject::connect(ui->encode, &QToolButton::clicked, this, &AISModGUI::on_encode_clicked); + QObject::connect(ui->msgId, QOverload::of(&QComboBox::currentIndexChanged), this, &AISModGUI::on_msgId_currentIndexChanged); + QObject::connect(ui->mmsi, &QLineEdit::editingFinished, this, &AISModGUI::on_mmsi_editingFinished); + QObject::connect(ui->status, QOverload::of(&QComboBox::currentIndexChanged), this, &AISModGUI::on_status_currentIndexChanged); + QObject::connect(ui->latitude, QOverload::of(&QDoubleSpinBox::valueChanged), this, &AISModGUI::on_latitude_valueChanged); + QObject::connect(ui->longitude, QOverload::of(&QDoubleSpinBox::valueChanged), this, &AISModGUI::on_longitude_valueChanged); + QObject::connect(ui->insertPosition, &QToolButton::clicked, this, &AISModGUI::on_insertPosition_clicked); + QObject::connect(ui->course, QOverload::of(&QDoubleSpinBox::valueChanged), this, &AISModGUI::on_course_valueChanged); + QObject::connect(ui->speed, QOverload::of(&QDoubleSpinBox::valueChanged), this, &AISModGUI::on_speed_valueChanged); + QObject::connect(ui->heading, QOverload::of(&QSpinBox::valueChanged), this, &AISModGUI::on_heading_valueChanged); + QObject::connect(ui->message, &QLineEdit::editingFinished, this, &AISModGUI::on_message_editingFinished); + QObject::connect(ui->message, &QLineEdit::returnPressed, this, &AISModGUI::on_message_returnPressed); + QObject::connect(ui->repeat, &ButtonSwitch::toggled, this, &AISModGUI::on_repeat_toggled); + QObject::connect(ui->udpEnabled, &QCheckBox::clicked, this, &AISModGUI::on_udpEnabled_clicked); + QObject::connect(ui->udpAddress, &QLineEdit::editingFinished, this, &AISModGUI::on_udpAddress_editingFinished); + QObject::connect(ui->udpPort, &QLineEdit::editingFinished, this, &AISModGUI::on_udpPort_editingFinished); +} diff --git a/plugins/channeltx/modais/aismodgui.h b/plugins/channeltx/modais/aismodgui.h index 86081b287..4deff0459 100644 --- a/plugins/channeltx/modais/aismodgui.h +++ b/plugins/channeltx/modais/aismodgui.h @@ -49,6 +49,12 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } + virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; }; + virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; }; + virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; }; + virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; }; + virtual QString getTitle() const { return m_settings.m_title; }; + virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; public slots: void channelMarkerChangedByCursor(); @@ -78,6 +84,7 @@ private: void displaySettings(); void displayStreamIndex(); bool handleMessage(const Message& message); + void makeUIConnections(); void leaveEvent(QEvent*); void enterEvent(QEvent*); diff --git a/plugins/channeltx/modais/aismodgui.ui b/plugins/channeltx/modais/aismodgui.ui index 16b260bf4..342e69e97 100644 --- a/plugins/channeltx/modais/aismodgui.ui +++ b/plugins/channeltx/modais/aismodgui.ui @@ -1,7 +1,7 @@ AISModGUI - + 0 @@ -1148,9 +1148,9 @@ - RollupWidget + RollupContents QWidget -
gui/rollupwidget.h
+
gui/rollupcontents.h
1
diff --git a/plugins/channeltx/modais/aismodsettings.cpp b/plugins/channeltx/modais/aismodsettings.cpp index 91d71ca44..f489b715c 100644 --- a/plugins/channeltx/modais/aismodsettings.cpp +++ b/plugins/channeltx/modais/aismodsettings.cpp @@ -69,6 +69,7 @@ void AISModSettings::resetToDefaults() m_udpEnabled = false; m_udpAddress = "127.0.0.1"; m_udpPort = 9998; + m_workspaceIndex = 0; } bool AISModSettings::setMode(QString mode) @@ -180,6 +181,9 @@ QByteArray AISModSettings::serialize() const s.writeBlob(40, m_rollupState->serialize()); } + s.writeS32(41, m_workspaceIndex); + s.writeBlob(42, m_geometryBytes); + return s.final(); } @@ -267,6 +271,9 @@ bool AISModSettings::deserialize(const QByteArray& data) m_rollupState->deserialize(bytetmp); } + d.readS32(41, &m_workspaceIndex, 0); + d.readBlob(42, &m_geometryBytes); + return true; } else diff --git a/plugins/channeltx/modais/aismodsettings.h b/plugins/channeltx/modais/aismodsettings.h index 2be80462e..91e03293d 100644 --- a/plugins/channeltx/modais/aismodsettings.h +++ b/plugins/channeltx/modais/aismodsettings.h @@ -87,6 +87,8 @@ struct AISModSettings QString m_udpAddress; uint16_t m_udpPort; Serializable *m_rollupState; + int m_workspaceIndex; + QByteArray m_geometryBytes; // Sample rate is multiple of 9600 baud rate (use even multiple so Gausian filter has odd number of taps) // Is there any benefit to having this higher? diff --git a/plugins/channeltx/modam/ammodgui.cpp b/plugins/channeltx/modam/ammodgui.cpp index 7f0bc8c8f..f80e21bfb 100644 --- a/plugins/channeltx/modam/ammodgui.cpp +++ b/plugins/channeltx/modam/ammodgui.cpp @@ -271,7 +271,7 @@ void AMModGUI::onWidgetRolled(QWidget* widget, bool rollDown) (void) widget; (void) rollDown; - saveState(m_rollupState); + getRollupContents()->saveState(m_rollupState); applySettings(); } @@ -298,6 +298,7 @@ void AMModGUI::onMenuDialogCalled(const QPoint &p) m_settings.m_reverseAPIChannelIndex = dialog.getReverseAPIChannelIndex(); setWindowTitle(m_settings.m_title); + setTitle(m_channelMarker.getTitle()); setTitleColor(m_settings.m_rgbColor); applySettings(); @@ -335,10 +336,11 @@ AMModGUI::AMModGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSampl m_tickCount(0), m_enableNavTime(false) { - ui->setupUi(this); + ui->setupUi(getRollupContents()); + getRollupContents()->arrangeRollups(); m_helpURL = "plugins/channeltx/modam/readme.md"; setAttribute(Qt::WA_DeleteOnClose, true); - connect(this, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + connect(getRollupContents(), SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &))); m_amMod = (AMMod*) channelTx; @@ -386,6 +388,7 @@ AMModGUI::AMModGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSampl m_amMod->setLevelMeter(ui->volumeMeter); displaySettings(); + makeUIConnections(); applySettings(true); } @@ -420,6 +423,7 @@ void AMModGUI::displaySettings() setTitleColor(m_settings.m_rgbColor); setWindowTitle(m_channelMarker.getTitle()); + setTitle(m_channelMarker.getTitle()); blockApplySettings(true); @@ -457,7 +461,7 @@ void AMModGUI::displaySettings() displayStreamIndex(); - restoreState(m_rollupState); + getRollupContents()->restoreState(m_rollupState); blockApplySettings(false); } @@ -578,3 +582,22 @@ void AMModGUI::updateWithStreamTime() ui->navTimeSlider->setValue((int) (posRatio * 100.0)); } } + +void AMModGUI::makeUIConnections() +{ + QObject::connect(ui->deltaFrequency, &ValueDialZ::changed, this, &AMModGUI::on_deltaFrequency_changed); + QObject::connect(ui->rfBW, &QSlider::valueChanged, this, &AMModGUI::on_rfBW_valueChanged); + QObject::connect(ui->modPercent, &QSlider::valueChanged, this, &AMModGUI::on_modPercent_valueChanged); + QObject::connect(ui->volume, &QDial::valueChanged, this, &AMModGUI::on_volume_valueChanged); + QObject::connect(ui->channelMute, &QToolButton::toggled, this, &AMModGUI::on_channelMute_toggled); + QObject::connect(ui->tone, &ButtonSwitch::toggled, this, &AMModGUI::on_tone_toggled); + QObject::connect(ui->toneFrequency, &QDial::valueChanged, this, &AMModGUI::on_toneFrequency_valueChanged); + QObject::connect(ui->mic, &ButtonSwitch::toggled, this, &AMModGUI::on_mic_toggled); + QObject::connect(ui->play, &ButtonSwitch::toggled, this, &AMModGUI::on_play_toggled); + QObject::connect(ui->morseKeyer, &ButtonSwitch::toggled, this, &AMModGUI::on_morseKeyer_toggled); + QObject::connect(ui->playLoop, &ButtonSwitch::toggled, this, &AMModGUI::on_playLoop_toggled); + QObject::connect(ui->navTimeSlider, &QSlider::valueChanged, this, &AMModGUI::on_navTimeSlider_valueChanged); + QObject::connect(ui->showFileDialog, &QPushButton::clicked, this, &AMModGUI::on_showFileDialog_clicked); + QObject::connect(ui->feedbackEnable, &QToolButton::toggled, this, &AMModGUI::on_feedbackEnable_toggled); + QObject::connect(ui->feedbackVolume, &QDial::valueChanged, this, &AMModGUI::on_feedbackVolume_valueChanged); +} diff --git a/plugins/channeltx/modam/ammodgui.h b/plugins/channeltx/modam/ammodgui.h index ba7ff7d77..36d79ccbd 100644 --- a/plugins/channeltx/modam/ammodgui.h +++ b/plugins/channeltx/modam/ammodgui.h @@ -48,6 +48,12 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } + virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; }; + virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; }; + virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; }; + virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; }; + virtual QString getTitle() const { return m_settings.m_title; }; + virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; public slots: void channelMarkerChangedByCursor(); @@ -84,6 +90,7 @@ private: void updateWithStreamData(); void updateWithStreamTime(); bool handleMessage(const Message& message); + void makeUIConnections(); void leaveEvent(QEvent*); void enterEvent(QEvent*); diff --git a/plugins/channeltx/modam/ammodgui.ui b/plugins/channeltx/modam/ammodgui.ui index fd0974534..0e57ea350 100644 --- a/plugins/channeltx/modam/ammodgui.ui +++ b/plugins/channeltx/modam/ammodgui.ui @@ -1,7 +1,7 @@ AMModGUI - + 0 @@ -725,9 +725,9 @@ - RollupWidget + RollupContents QWidget -
gui/rollupwidget.h
+
gui/rollupcontents.h
1
diff --git a/plugins/channeltx/modam/ammodsettings.cpp b/plugins/channeltx/modam/ammodsettings.cpp index 909dc75d6..981690912 100644 --- a/plugins/channeltx/modam/ammodsettings.cpp +++ b/plugins/channeltx/modam/ammodsettings.cpp @@ -52,6 +52,7 @@ void AMModSettings::resetToDefaults() m_reverseAPIPort = 8888; m_reverseAPIDeviceIndex = 0; m_reverseAPIChannelIndex = 0; + m_workspaceIndex = 0; } QByteArray AMModSettings::serialize() const @@ -92,6 +93,9 @@ QByteArray AMModSettings::serialize() const s.writeBlob(21, m_rollupState->serialize()); } + s.writeS32(22, m_workspaceIndex); + s.writeBlob(23, m_geometryBytes); + return s.final(); } @@ -166,6 +170,9 @@ bool AMModSettings::deserialize(const QByteArray& data) m_rollupState->deserialize(bytetmp); } + d.readS32(22, &m_workspaceIndex, 0); + d.readBlob(23, &m_geometryBytes); + return true; } else diff --git a/plugins/channeltx/modam/ammodsettings.h b/plugins/channeltx/modam/ammodsettings.h index 2bc8bfe47..4eb1120f5 100644 --- a/plugins/channeltx/modam/ammodsettings.h +++ b/plugins/channeltx/modam/ammodsettings.h @@ -55,6 +55,8 @@ struct AMModSettings uint16_t m_reverseAPIPort; uint16_t m_reverseAPIDeviceIndex; uint16_t m_reverseAPIChannelIndex; + int m_workspaceIndex; + QByteArray m_geometryBytes; Serializable *m_channelMarker; Serializable *m_cwKeyerGUI; diff --git a/plugins/channeltx/modatv/atvmodgui.cpp b/plugins/channeltx/modatv/atvmodgui.cpp index 49caab00a..460d68cad 100644 --- a/plugins/channeltx/modatv/atvmodgui.cpp +++ b/plugins/channeltx/modatv/atvmodgui.cpp @@ -63,10 +63,11 @@ ATVModGUI::ATVModGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSam m_camBusyFPSMessageBox(0), m_rfSliderDivisor(100000) { - ui->setupUi(this); + ui->setupUi(getRollupContents()); + getRollupContents()->arrangeRollups(); m_helpURL = "plugins/channeltx/modatv/readme.md"; setAttribute(Qt::WA_DeleteOnClose, true); - connect(this, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + connect(getRollupContents(), SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &))); m_atvMod = (ATVMod*) channelTx; @@ -112,6 +113,7 @@ ATVModGUI::ATVModGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSam ui->fmExcursionLabel->setText(delta); displaySettings(); + makeUIConnections(); applySettings(true); } @@ -671,7 +673,7 @@ void ATVModGUI::onWidgetRolled(QWidget* widget, bool rollDown) (void) widget; (void) rollDown; - saveState(m_rollupState); + getRollupContents()->saveState(m_rollupState); applySettings(); } @@ -699,6 +701,7 @@ void ATVModGUI::onMenuDialogCalled(const QPoint &p) m_settings.m_reverseAPIChannelIndex = dialog.getReverseAPIChannelIndex(); setWindowTitle(m_settings.m_title); + setTitle(m_channelMarker.getTitle()); setTitleColor(m_settings.m_rgbColor); applySettings(); @@ -750,6 +753,7 @@ void ATVModGUI::displaySettings() setTitleColor(m_settings.m_rgbColor); setWindowTitle(m_channelMarker.getTitle()); + setTitle(m_channelMarker.getTitle()); displayStreamIndex(); blockApplySettings(true); @@ -796,7 +800,7 @@ void ATVModGUI::displaySettings() ui->playVideo->setChecked(m_settings.m_videoPlay); ui->playLoop->setChecked(m_settings.m_videoPlayLoop); - restoreState(m_rollupState); + getRollupContents()->restoreState(m_rollupState); blockApplySettings(false); } @@ -867,3 +871,31 @@ void ATVModGUI::updateWithStreamTime() } } +void ATVModGUI::makeUIConnections() +{ + QObject::connect(ui->deltaFrequency, &ValueDialZ::changed, this, &ATVModGUI::on_deltaFrequency_changed); + QObject::connect(ui->channelMute, &QToolButton::toggled, this, &ATVModGUI::on_channelMute_toggled); + QObject::connect(ui->forceDecimator, &ButtonSwitch::toggled, this, &ATVModGUI::on_forceDecimator_toggled); + QObject::connect(ui->modulation, QOverload::of(&QComboBox::currentIndexChanged), this, &ATVModGUI::on_modulation_currentIndexChanged); + QObject::connect(ui->rfScaling, &QDial::valueChanged, this, &ATVModGUI::on_rfScaling_valueChanged); + QObject::connect(ui->fmExcursion, &QDial::valueChanged, this, &ATVModGUI::on_fmExcursion_valueChanged); + QObject::connect(ui->rfBW, &QSlider::valueChanged, this, &ATVModGUI::on_rfBW_valueChanged); + QObject::connect(ui->rfOppBW, &QSlider::valueChanged, this, &ATVModGUI::on_rfOppBW_valueChanged); + QObject::connect(ui->nbLines, QOverload::of(&QComboBox::currentIndexChanged), this, &ATVModGUI::on_nbLines_currentIndexChanged); + QObject::connect(ui->fps, QOverload::of(&QComboBox::currentIndexChanged), this, &ATVModGUI::on_fps_currentIndexChanged); + QObject::connect(ui->standard, QOverload::of(&QComboBox::currentIndexChanged), this, &ATVModGUI::on_standard_currentIndexChanged); + QObject::connect(ui->invertVideo, &QCheckBox::clicked, this, &ATVModGUI::on_invertVideo_clicked); + QObject::connect(ui->uniformLevel, &QDial::valueChanged, this, &ATVModGUI::on_uniformLevel_valueChanged); + QObject::connect(ui->inputSelect, QOverload::of(&QComboBox::currentIndexChanged), this, &ATVModGUI::on_inputSelect_currentIndexChanged); + QObject::connect(ui->imageFileDialog, &QPushButton::clicked, this, &ATVModGUI::on_imageFileDialog_clicked); + QObject::connect(ui->videoFileDialog, &QPushButton::clicked, this, &ATVModGUI::on_videoFileDialog_clicked); + QObject::connect(ui->playVideo, &ButtonSwitch::toggled, this, &ATVModGUI::on_playVideo_toggled); + QObject::connect(ui->playLoop, &ButtonSwitch::toggled, this, &ATVModGUI::on_playLoop_toggled); + QObject::connect(ui->navTimeSlider, &QSlider::valueChanged, this, &ATVModGUI::on_navTimeSlider_valueChanged); + QObject::connect(ui->playCamera, &ButtonSwitch::toggled, this, &ATVModGUI::on_playCamera_toggled); + QObject::connect(ui->camSelect, QOverload::of(&QComboBox::currentIndexChanged), this, &ATVModGUI::on_camSelect_currentIndexChanged); + QObject::connect(ui->cameraManualFPSEnable, &ButtonSwitch::toggled, this, &ATVModGUI::on_cameraManualFPSEnable_toggled); + QObject::connect(ui->cameraManualFPS, &QDial::valueChanged, this, &ATVModGUI::on_cameraManualFPS_valueChanged); + QObject::connect(ui->overlayTextShow, &ButtonSwitch::toggled, this, &ATVModGUI::on_overlayTextShow_toggled); + QObject::connect(ui->overlayText, &QLineEdit::textEdited, this, &ATVModGUI::on_overlayText_textEdited); +} diff --git a/plugins/channeltx/modatv/atvmodgui.h b/plugins/channeltx/modatv/atvmodgui.h index cf22f35d6..2a81c73d1 100644 --- a/plugins/channeltx/modatv/atvmodgui.h +++ b/plugins/channeltx/modatv/atvmodgui.h @@ -47,6 +47,12 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } + virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; }; + virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; }; + virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; }; + virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; }; + virtual QString getTitle() const { return m_settings.m_title; }; + virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; public slots: void channelMarkerChangedByCursor(); @@ -90,6 +96,7 @@ private: int getNbLinesIndex(int nbLines); int getFPSIndex(int fps); bool handleMessage(const Message& message); + void makeUIConnections(); void leaveEvent(QEvent*); void enterEvent(QEvent*); diff --git a/plugins/channeltx/modatv/atvmodgui.ui b/plugins/channeltx/modatv/atvmodgui.ui index 86af812c5..7155fd5cb 100644 --- a/plugins/channeltx/modatv/atvmodgui.ui +++ b/plugins/channeltx/modatv/atvmodgui.ui @@ -1,7 +1,7 @@ ATVModGUI - + 0 @@ -1304,9 +1304,9 @@ - RollupWidget + RollupContents QWidget -
gui/rollupwidget.h
+
gui/rollupcontents.h
1
diff --git a/plugins/channeltx/modatv/atvmodsettings.cpp b/plugins/channeltx/modatv/atvmodsettings.cpp index 060f4237e..3f5bc0393 100644 --- a/plugins/channeltx/modatv/atvmodsettings.cpp +++ b/plugins/channeltx/modatv/atvmodsettings.cpp @@ -58,6 +58,7 @@ void ATVModSettings::resetToDefaults() m_reverseAPIPort = 8888; m_reverseAPIDeviceIndex = 0; m_reverseAPIChannelIndex = 0; + m_workspaceIndex = 0; } QByteArray ATVModSettings::serialize() const @@ -97,6 +98,9 @@ QByteArray ATVModSettings::serialize() const s.writeBlob(25, m_rollupState->serialize()); } + s.writeS32(26, m_workspaceIndex); + s.writeBlob(27, m_geometryBytes); + return s.final(); } @@ -169,6 +173,9 @@ bool ATVModSettings::deserialize(const QByteArray& data) m_rollupState->deserialize(bytetmp); } + d.readS32(26, &m_workspaceIndex, 0); + d.readBlob(27, &m_geometryBytes); + return true; } else diff --git a/plugins/channeltx/modatv/atvmodsettings.h b/plugins/channeltx/modatv/atvmodsettings.h index 8c4d66bf5..95173938c 100644 --- a/plugins/channeltx/modatv/atvmodsettings.h +++ b/plugins/channeltx/modatv/atvmodsettings.h @@ -89,6 +89,8 @@ struct ATVModSettings uint16_t m_reverseAPIPort; uint16_t m_reverseAPIDeviceIndex; uint16_t m_reverseAPIChannelIndex; + int m_workspaceIndex; + QByteArray m_geometryBytes; Serializable *m_channelMarker; Serializable *m_rollupState; diff --git a/plugins/channeltx/modchirpchat/chirpchatmodgui.cpp b/plugins/channeltx/modchirpchat/chirpchatmodgui.cpp index d7987b6d7..6390a39ec 100644 --- a/plugins/channeltx/modchirpchat/chirpchatmodgui.cpp +++ b/plugins/channeltx/modchirpchat/chirpchatmodgui.cpp @@ -357,7 +357,7 @@ void ChirpChatModGUI::onWidgetRolled(QWidget* widget, bool rollDown) (void) widget; (void) rollDown; - saveState(m_rollupState); + getRollupContents()->saveState(m_rollupState); applySettings(); } @@ -384,6 +384,7 @@ void ChirpChatModGUI::onMenuDialogCalled(const QPoint &p) m_settings.m_reverseAPIChannelIndex = dialog.getReverseAPIChannelIndex(); setWindowTitle(m_settings.m_title); + setTitle(m_channelMarker.getTitle()); setTitleColor(m_settings.m_rgbColor); applySettings(); @@ -416,11 +417,12 @@ ChirpChatModGUI::ChirpChatModGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, m_doApplySettings(true), m_tickCount(0) { - ui->setupUi(this); + ui->setupUi(getRollupContents()); + getRollupContents()->arrangeRollups(); m_helpURL = "plugins/channeltx/modchirpchat/readme.md"; setAttribute(Qt::WA_DeleteOnClose, true); - connect(this, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + connect(getRollupContents(), SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &))); m_chirpChatMod = (ChirpChatMod*) channelTx; @@ -453,6 +455,7 @@ ChirpChatModGUI::ChirpChatModGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, setBandwidths(); displaySettings(); + makeUIConnections(); applySettings(); } @@ -488,6 +491,7 @@ void ChirpChatModGUI::displaySettings() setTitleColor(m_settings.m_rgbColor); setWindowTitle(m_channelMarker.getTitle()); + setTitle(m_channelMarker.getTitle()); displayStreamIndex(); displayCurrentPayloadMessage(); displayBinaryMessage(); @@ -525,7 +529,7 @@ void ChirpChatModGUI::displaySettings() ui->udpEnabled->setChecked(m_settings.m_udpEnabled); ui->udpAddress->setText(m_settings.m_udpAddress); ui->udpPort->setText(QString::number(m_settings.m_udpPort)); - restoreState(m_rollupState); + getRollupContents()->restoreState(m_rollupState); blockApplySettings(false); } @@ -619,3 +623,33 @@ void ChirpChatModGUI::tick() } } } + +void ChirpChatModGUI::makeUIConnections() +{ + QObject::connect(ui->deltaFrequency, &ValueDialZ::changed, this, &ChirpChatModGUI::on_deltaFrequency_changed); + QObject::connect(ui->bw, &QSlider::valueChanged, this, &ChirpChatModGUI::on_bw_valueChanged); + QObject::connect(ui->spread, &QSlider::valueChanged, this, &ChirpChatModGUI::on_spread_valueChanged); + QObject::connect(ui->deBits, &QSlider::valueChanged, this, &ChirpChatModGUI::on_deBits_valueChanged); + QObject::connect(ui->preambleChirps, &QSlider::valueChanged, this, &ChirpChatModGUI::on_preambleChirps_valueChanged); + QObject::connect(ui->idleTime, &QSlider::valueChanged, this, &ChirpChatModGUI::on_idleTime_valueChanged); + QObject::connect(ui->syncWord, &QLineEdit::editingFinished, this, &ChirpChatModGUI::on_syncWord_editingFinished); + QObject::connect(ui->channelMute, &QToolButton::toggled, this, &ChirpChatModGUI::on_channelMute_toggled); + QObject::connect(ui->scheme, QOverload::of(&QComboBox::currentIndexChanged), this, &ChirpChatModGUI::on_scheme_currentIndexChanged); + QObject::connect(ui->fecParity, &QDial::valueChanged, this, &ChirpChatModGUI::on_fecParity_valueChanged); + QObject::connect(ui->crc, &QCheckBox::stateChanged, this, &ChirpChatModGUI::on_crc_stateChanged); + QObject::connect(ui->header, &QCheckBox::stateChanged, this, &ChirpChatModGUI::on_header_stateChanged); + QObject::connect(ui->myCall, &QLineEdit::editingFinished, this, &ChirpChatModGUI::on_myCall_editingFinished); + QObject::connect(ui->urCall, &QLineEdit::editingFinished, this, &ChirpChatModGUI::on_urCall_editingFinished); + QObject::connect(ui->myLocator, &QLineEdit::editingFinished, this, &ChirpChatModGUI::on_myLocator_editingFinished); + QObject::connect(ui->report, &QLineEdit::editingFinished, this, &ChirpChatModGUI::on_report_editingFinished); + QObject::connect(ui->msgType, QOverload::of(&QComboBox::currentIndexChanged), this, &ChirpChatModGUI::on_msgType_currentIndexChanged); + QObject::connect(ui->resetMessages, &QPushButton::clicked, this, &ChirpChatModGUI::on_resetMessages_clicked); + QObject::connect(ui->playMessage, &QPushButton::clicked, this, &ChirpChatModGUI::on_playMessage_clicked); + QObject::connect(ui->repeatMessage, &QDial::valueChanged, this, &ChirpChatModGUI::on_repeatMessage_valueChanged); + QObject::connect(ui->generateMessages, &QPushButton::clicked, this, &ChirpChatModGUI::on_generateMessages_clicked); + QObject::connect(ui->messageText, &CustomTextEdit::editingFinished, this, &ChirpChatModGUI::on_messageText_editingFinished); + QObject::connect(ui->hexText, &QLineEdit::editingFinished, this, &ChirpChatModGUI::on_hexText_editingFinished); + QObject::connect(ui->udpEnabled, &QCheckBox::clicked, this, &ChirpChatModGUI::on_udpEnabled_clicked); + QObject::connect(ui->udpAddress, &QLineEdit::editingFinished, this, &ChirpChatModGUI::on_udpAddress_editingFinished); + QObject::connect(ui->udpPort, &QLineEdit::editingFinished, this, &ChirpChatModGUI::on_udpPort_editingFinished); +} diff --git a/plugins/channeltx/modchirpchat/chirpchatmodgui.h b/plugins/channeltx/modchirpchat/chirpchatmodgui.h index 38d776cf5..5a7ae7fa2 100644 --- a/plugins/channeltx/modchirpchat/chirpchatmodgui.h +++ b/plugins/channeltx/modchirpchat/chirpchatmodgui.h @@ -46,6 +46,12 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } + virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; }; + virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; }; + virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; }; + virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; }; + virtual QString getTitle() const { return m_settings.m_title; }; + virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; public slots: void channelMarkerChangedByCursor(); @@ -77,6 +83,7 @@ private: void displayBinaryMessage(); void setBandwidths(); bool handleMessage(const Message& message); + void makeUIConnections(); void leaveEvent(QEvent*); void enterEvent(QEvent*); diff --git a/plugins/channeltx/modchirpchat/chirpchatmodgui.ui b/plugins/channeltx/modchirpchat/chirpchatmodgui.ui index 96eee61b2..f6b917abf 100644 --- a/plugins/channeltx/modchirpchat/chirpchatmodgui.ui +++ b/plugins/channeltx/modchirpchat/chirpchatmodgui.ui @@ -1,7 +1,7 @@ ChirpChatModGUI - + 0 @@ -1198,9 +1198,9 @@ - RollupWidget + RollupContents QWidget -
gui/rollupwidget.h
+
gui/rollupcontents.h
1
diff --git a/plugins/channeltx/modchirpchat/chirpchatmodsettings.cpp b/plugins/channeltx/modchirpchat/chirpchatmodsettings.cpp index ad5669490..6d3c28ee6 100644 --- a/plugins/channeltx/modchirpchat/chirpchatmodsettings.cpp +++ b/plugins/channeltx/modchirpchat/chirpchatmodsettings.cpp @@ -93,6 +93,7 @@ void ChirpChatModSettings::resetToDefaults() m_reverseAPIPort = 8888; m_reverseAPIDeviceIndex = 0; m_reverseAPIChannelIndex = 0; + m_workspaceIndex = 0; setDefaultTemplates(); } @@ -201,6 +202,9 @@ QByteArray ChirpChatModSettings::serialize() const s.writeBlob(59, m_rollupState->serialize()); } + s.writeS32(60, m_workspaceIndex); + s.writeBlob(61, m_geometryBytes); + return s.final(); } @@ -305,6 +309,9 @@ bool ChirpChatModSettings::deserialize(const QByteArray& data) m_rollupState->deserialize(bytetmp); } + d.readS32(60, &m_workspaceIndex, 0); + d.readBlob(61, &m_geometryBytes); + return true; } else diff --git a/plugins/channeltx/modchirpchat/chirpchatmodsettings.h b/plugins/channeltx/modchirpchat/chirpchatmodsettings.h index aee95ce76..710055d42 100644 --- a/plugins/channeltx/modchirpchat/chirpchatmodsettings.h +++ b/plugins/channeltx/modchirpchat/chirpchatmodsettings.h @@ -88,6 +88,8 @@ struct ChirpChatModSettings uint16_t m_reverseAPIPort; uint16_t m_reverseAPIDeviceIndex; uint16_t m_reverseAPIChannelIndex; + int m_workspaceIndex; + QByteArray m_geometryBytes; Serializable *m_channelMarker; Serializable *m_rollupState; diff --git a/plugins/channeltx/moddatv/datvmodgui.cpp b/plugins/channeltx/moddatv/datvmodgui.cpp index 52cdb9753..3d83a96b6 100644 --- a/plugins/channeltx/moddatv/datvmodgui.cpp +++ b/plugins/channeltx/moddatv/datvmodgui.cpp @@ -63,10 +63,11 @@ DATVModGUI::DATVModGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandS m_tickCount(0), m_enableNavTime(false) { - ui->setupUi(this); + ui->setupUi(getRollupContents()); + getRollupContents()->arrangeRollups(); m_helpURL = "plugins/channeltx/moddatv/readme.md"; setAttribute(Qt::WA_DeleteOnClose, true); - connect(this, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + connect(getRollupContents(), SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &))); m_datvMod = (DATVMod*) channelTx; @@ -107,6 +108,7 @@ DATVModGUI::DATVModGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandS #endif displaySettings(); + makeUIConnections(); applySettings(true); if (!m_settings.m_tsFileName.isEmpty()) configureTsFileName(); @@ -473,7 +475,7 @@ void DATVModGUI::onWidgetRolled(QWidget* widget, bool rollDown) (void) widget; (void) rollDown; - saveState(m_rollupState); + getRollupContents()->saveState(m_rollupState); applySettings(); } @@ -501,6 +503,7 @@ void DATVModGUI::onMenuDialogCalled(const QPoint &p) m_settings.m_reverseAPIChannelIndex = dialog.getReverseAPIChannelIndex(); setWindowTitle(m_settings.m_title); + setTitle(m_channelMarker.getTitle()); setTitleColor(m_settings.m_rgbColor); applySettings(); @@ -552,6 +555,7 @@ void DATVModGUI::displaySettings() setTitleColor(m_settings.m_rgbColor); setWindowTitle(m_channelMarker.getTitle()); + setTitle(m_channelMarker.getTitle()); displayStreamIndex(); blockApplySettings(true); @@ -581,7 +585,7 @@ void DATVModGUI::displaySettings() ui->udpAddress->setText(m_settings.m_udpAddress); ui->udpPort->setValue(m_settings.m_udpPort); - restoreState(m_rollupState); + getRollupContents()->restoreState(m_rollupState); blockApplySettings(false); } @@ -662,3 +666,21 @@ void DATVModGUI::updateWithStreamTime() } } +void DATVModGUI::makeUIConnections() +{ + QObject::connect(ui->deltaFrequency, &ValueDialZ::changed, this, &DATVModGUI::on_deltaFrequency_changed); + QObject::connect(ui->channelMute, &QToolButton::toggled, this, &DATVModGUI::on_channelMute_toggled); + QObject::connect(ui->dvbStandard, QOverload::of(&QComboBox::currentIndexChanged), this, &DATVModGUI::on_dvbStandard_currentIndexChanged); + QObject::connect(ui->symbolRate, QOverload::of(&QSpinBox::valueChanged), this, &DATVModGUI::on_symbolRate_valueChanged); + QObject::connect(ui->rfBW, &QSlider::valueChanged, this, &DATVModGUI::on_rfBW_valueChanged); + QObject::connect(ui->fec, QOverload::of(&QComboBox::currentIndexChanged), this, &DATVModGUI::on_fec_currentIndexChanged); + QObject::connect(ui->modulation, QOverload::of(&QComboBox::currentIndexChanged), this, &DATVModGUI::on_modulation_currentIndexChanged); + QObject::connect(ui->rollOff, QOverload::of(&QComboBox::currentIndexChanged), this, &DATVModGUI::on_rollOff_currentIndexChanged); + QObject::connect(ui->inputSelect, QOverload::of(&QComboBox::currentIndexChanged), this, &DATVModGUI::on_inputSelect_currentIndexChanged); + QObject::connect(ui->tsFileDialog, &QPushButton::clicked, this, &DATVModGUI::on_tsFileDialog_clicked); + QObject::connect(ui->playFile, &ButtonSwitch::toggled, this, &DATVModGUI::on_playFile_toggled); + QObject::connect(ui->playLoop, &ButtonSwitch::toggled, this, &DATVModGUI::on_playLoop_toggled); + QObject::connect(ui->navTimeSlider, &QSlider::valueChanged, this, &DATVModGUI::on_navTimeSlider_valueChanged); + QObject::connect(ui->udpAddress, &QLineEdit::editingFinished, this, &DATVModGUI::on_udpAddress_editingFinished); + QObject::connect(ui->udpPort, QOverload::of(&QSpinBox::valueChanged), this, &DATVModGUI::on_udpPort_valueChanged); +} diff --git a/plugins/channeltx/moddatv/datvmodgui.h b/plugins/channeltx/moddatv/datvmodgui.h index 5ddc6f102..0259aafa8 100644 --- a/plugins/channeltx/moddatv/datvmodgui.h +++ b/plugins/channeltx/moddatv/datvmodgui.h @@ -48,6 +48,12 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } + virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; }; + virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; }; + virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; }; + virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; }; + virtual QString getTitle() const { return m_settings.m_title; }; + virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; public slots: void channelMarkerChangedByCursor(); @@ -86,6 +92,8 @@ private: void updateWithStreamTime(); void setChannelMarkerBandwidth(); bool handleMessage(const Message& message); + void makeUIConnections(); + void updateFEC(); void leaveEvent(QEvent*); diff --git a/plugins/channeltx/moddatv/datvmodgui.ui b/plugins/channeltx/moddatv/datvmodgui.ui index f2d92648c..1d69e20b9 100644 --- a/plugins/channeltx/moddatv/datvmodgui.ui +++ b/plugins/channeltx/moddatv/datvmodgui.ui @@ -1,7 +1,7 @@ DATVModGUI - + 0 @@ -794,9 +794,9 @@ - RollupWidget + RollupContents QWidget -
gui/rollupwidget.h
+
gui/rollupcontents.h
1
diff --git a/plugins/channeltx/moddatv/datvmodsettings.cpp b/plugins/channeltx/moddatv/datvmodsettings.cpp index ee5fda37c..9428bbbd5 100644 --- a/plugins/channeltx/moddatv/datvmodsettings.cpp +++ b/plugins/channeltx/moddatv/datvmodsettings.cpp @@ -57,6 +57,7 @@ void DATVModSettings::resetToDefaults() m_reverseAPIPort = 8888; m_reverseAPIDeviceIndex = 0; m_reverseAPIChannelIndex = 0; + m_workspaceIndex = 0; } QByteArray DATVModSettings::serialize() const @@ -93,6 +94,9 @@ QByteArray DATVModSettings::serialize() const s.writeBlob(29, m_rollupState->serialize()); } + s.writeS32(30, m_workspaceIndex); + s.writeBlob(31, m_geometryBytes); + return s.final(); } @@ -163,6 +167,9 @@ bool DATVModSettings::deserialize(const QByteArray& data) m_rollupState->deserialize(bytetmp); } + d.readS32(30, &m_workspaceIndex, 0); + d.readBlob(31, &m_geometryBytes); + return true; } else diff --git a/plugins/channeltx/moddatv/datvmodsettings.h b/plugins/channeltx/moddatv/datvmodsettings.h index 9fe609747..985558e48 100644 --- a/plugins/channeltx/moddatv/datvmodsettings.h +++ b/plugins/channeltx/moddatv/datvmodsettings.h @@ -97,6 +97,8 @@ struct DATVModSettings uint16_t m_reverseAPIDeviceIndex; uint16_t m_reverseAPIChannelIndex; Serializable *m_rollupState; + int m_workspaceIndex; + QByteArray m_geometryBytes; DATVModSettings(); void resetToDefaults(); diff --git a/plugins/channeltx/modfreedv/freedvmodgui.cpp b/plugins/channeltx/modfreedv/freedvmodgui.cpp index e0de4cd92..be987aa4b 100644 --- a/plugins/channeltx/modfreedv/freedvmodgui.cpp +++ b/plugins/channeltx/modfreedv/freedvmodgui.cpp @@ -284,7 +284,7 @@ void FreeDVModGUI::onWidgetRolled(QWidget* widget, bool rollDown) (void) widget; (void) rollDown; - saveState(m_rollupState); + getRollupContents()->saveState(m_rollupState); applySettings(); } @@ -312,6 +312,7 @@ void FreeDVModGUI::onMenuDialogCalled(const QPoint &p) m_settings.m_reverseAPIChannelIndex = dialog.getReverseAPIChannelIndex(); setWindowTitle(m_settings.m_title); + setTitle(m_channelMarker.getTitle()); setTitleColor(m_settings.m_rgbColor); applySettings(); @@ -349,10 +350,11 @@ FreeDVModGUI::FreeDVModGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Baseb m_tickCount(0), m_enableNavTime(false) { - ui->setupUi(this); + ui->setupUi(getRollupContents()); + getRollupContents()->arrangeRollups(); m_helpURL = "plugins/channeltx/modfreedv/readme.md"; setAttribute(Qt::WA_DeleteOnClose, true); - connect(this, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + connect(getRollupContents(), SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &))); m_freeDVMod = (FreeDVMod*) channelTx; @@ -397,6 +399,7 @@ FreeDVModGUI::FreeDVModGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Baseb m_freeDVMod->setLevelMeter(ui->volumeMeter); displaySettings(); + makeUIConnections(); applyBandwidths(5 - ui->spanLog2->value(), true); // does applySettings(true) } @@ -460,6 +463,7 @@ void FreeDVModGUI::displaySettings() setTitleColor(m_settings.m_rgbColor); setWindowTitle(m_channelMarker.getTitle()); + setTitle(m_channelMarker.getTitle()); displayStreamIndex(); blockApplySettings(true); @@ -501,7 +505,7 @@ void FreeDVModGUI::displaySettings() ui->play->setChecked(m_settings.m_modAFInput == FreeDVModSettings::FreeDVModInputAF::FreeDVModInputFile); ui->morseKeyer->setChecked(m_settings.m_modAFInput == FreeDVModSettings::FreeDVModInputAF::FreeDVModInputCWTone); - restoreState(m_rollupState); + getRollupContents()->restoreState(m_rollupState); blockApplySettings(false); } @@ -596,3 +600,20 @@ void FreeDVModGUI::updateWithStreamTime() ui->navTimeSlider->setValue((int) (posRatio * 100.0)); } } + +void FreeDVModGUI::makeUIConnections() +{ + QObject::connect(ui->deltaFrequency, &ValueDialZ::changed, this, &FreeDVModGUI::on_deltaFrequency_changed); + QObject::connect(ui->gaugeInput, &QCheckBox::toggled, this, &FreeDVModGUI::on_gaugeInput_toggled); + QObject::connect(ui->volume, &QDial::valueChanged, this, &FreeDVModGUI::on_volume_valueChanged); + QObject::connect(ui->audioMute, &QToolButton::toggled, this, &FreeDVModGUI::on_audioMute_toggled); + QObject::connect(ui->freeDVMode, QOverload::of(&QComboBox::currentIndexChanged), this, &FreeDVModGUI::on_freeDVMode_currentIndexChanged); + QObject::connect(ui->tone, &ButtonSwitch::toggled, this, &FreeDVModGUI::on_tone_toggled); + QObject::connect(ui->toneFrequency, &QDial::valueChanged, this, &FreeDVModGUI::on_toneFrequency_valueChanged); + QObject::connect(ui->mic, &ButtonSwitch::toggled, this, &FreeDVModGUI::on_mic_toggled); + QObject::connect(ui->play, &ButtonSwitch::toggled, this, &FreeDVModGUI::on_play_toggled); + QObject::connect(ui->playLoop, &ButtonSwitch::toggled, this, &FreeDVModGUI::on_playLoop_toggled); + QObject::connect(ui->morseKeyer, &ButtonSwitch::toggled, this, &FreeDVModGUI::on_morseKeyer_toggled); + QObject::connect(ui->navTimeSlider, &QSlider::valueChanged, this, &FreeDVModGUI::on_navTimeSlider_valueChanged); + QObject::connect(ui->showFileDialog, &QPushButton::clicked, this, &FreeDVModGUI::on_showFileDialog_clicked); +} diff --git a/plugins/channeltx/modfreedv/freedvmodgui.h b/plugins/channeltx/modfreedv/freedvmodgui.h index 09fbe3dc9..ed7277ed9 100644 --- a/plugins/channeltx/modfreedv/freedvmodgui.h +++ b/plugins/channeltx/modfreedv/freedvmodgui.h @@ -49,6 +49,12 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } + virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; }; + virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; }; + virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; }; + virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; }; + virtual QString getTitle() const { return m_settings.m_title; }; + virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; public slots: void channelMarkerChangedByCursor(); @@ -89,6 +95,7 @@ private: void updateWithStreamTime(); void channelMarkerUpdate(); bool handleMessage(const Message& message); + void makeUIConnections(); void leaveEvent(QEvent*); void enterEvent(QEvent*); diff --git a/plugins/channeltx/modfreedv/freedvmodgui.ui b/plugins/channeltx/modfreedv/freedvmodgui.ui index bd4dc6062..cd10b32e3 100644 --- a/plugins/channeltx/modfreedv/freedvmodgui.ui +++ b/plugins/channeltx/modfreedv/freedvmodgui.ui @@ -1,7 +1,7 @@ FreeDVModGUI - + 0 @@ -718,9 +718,9 @@ - RollupWidget + RollupContents QWidget -
gui/rollupwidget.h
+
gui/rollupcontents.h
1
diff --git a/plugins/channeltx/modfreedv/freedvmodsettings.cpp b/plugins/channeltx/modfreedv/freedvmodsettings.cpp index 051a52309..1514fd198 100644 --- a/plugins/channeltx/modfreedv/freedvmodsettings.cpp +++ b/plugins/channeltx/modfreedv/freedvmodsettings.cpp @@ -51,6 +51,7 @@ void FreeDVModSettings::resetToDefaults() m_reverseAPIPort = 8888; m_reverseAPIDeviceIndex = 0; m_reverseAPIChannelIndex = 0; + m_workspaceIndex = 0; } QByteArray FreeDVModSettings::serialize() const @@ -94,6 +95,9 @@ QByteArray FreeDVModSettings::serialize() const s.writeBlob(28, m_rollupState->serialize()); } + s.writeS32(29, m_workspaceIndex); + s.writeBlob(30, m_geometryBytes); + return s.final(); } @@ -181,6 +185,9 @@ bool FreeDVModSettings::deserialize(const QByteArray& data) m_rollupState->deserialize(bytetmp); } + d.readS32(29, &m_workspaceIndex, 0); + d.readBlob(30, &m_geometryBytes); + return true; } else diff --git a/plugins/channeltx/modfreedv/freedvmodsettings.h b/plugins/channeltx/modfreedv/freedvmodsettings.h index a58635433..1195c894f 100644 --- a/plugins/channeltx/modfreedv/freedvmodsettings.h +++ b/plugins/channeltx/modfreedv/freedvmodsettings.h @@ -66,6 +66,8 @@ struct FreeDVModSettings uint16_t m_reverseAPIPort; uint16_t m_reverseAPIDeviceIndex; uint16_t m_reverseAPIChannelIndex; + int m_workspaceIndex; + QByteArray m_geometryBytes; Serializable *m_channelMarker; Serializable *m_spectrumGUI; diff --git a/plugins/channeltx/modnfm/nfmmodgui.cpp b/plugins/channeltx/modnfm/nfmmodgui.cpp index 267f0882a..9aacf2b30 100644 --- a/plugins/channeltx/modnfm/nfmmodgui.cpp +++ b/plugins/channeltx/modnfm/nfmmodgui.cpp @@ -343,7 +343,7 @@ void NFMModGUI::onWidgetRolled(QWidget* widget, bool rollDown) (void) widget; (void) rollDown; - saveState(m_rollupState); + getRollupContents()->saveState(m_rollupState); applySettings(); } @@ -370,6 +370,7 @@ void NFMModGUI::onMenuDialogCalled(const QPoint &p) m_settings.m_reverseAPIChannelIndex = dialog.getReverseAPIChannelIndex(); setWindowTitle(m_settings.m_title); + setTitle(m_channelMarker.getTitle()); setTitleColor(m_settings.m_rgbColor); applySettings(); @@ -408,7 +409,8 @@ NFMModGUI::NFMModGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSam m_enableNavTime(false), m_dcsCodeValidator(QRegExp("[0-7]{1,3}")) { - ui->setupUi(this); + ui->setupUi(getRollupContents()); + getRollupContents()->arrangeRollups(); m_helpURL = "plugins/channeltx/modnfm/readme.md"; setAttribute(Qt::WA_DeleteOnClose, true); @@ -422,7 +424,7 @@ NFMModGUI::NFMModGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSam ui->channelSpacing->setCurrentIndex(NFMModSettings::getChannelSpacingIndex(25000)); ui->channelSpacing->blockSignals(false); - connect(this, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + connect(getRollupContents(), SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &))); m_nfmMod = (NFMMod*) channelTx; @@ -474,6 +476,7 @@ NFMModGUI::NFMModGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSam m_settings.setRollupState(&m_rollupState); displaySettings(); + makeUIConnections(); applySettings(); } @@ -506,6 +509,7 @@ void NFMModGUI::displaySettings() setTitleColor(m_settings.m_rgbColor); setWindowTitle(m_channelMarker.getTitle()); + setTitle(m_channelMarker.getTitle()); displayStreamIndex(); blockApplySettings(true); @@ -556,7 +560,7 @@ void NFMModGUI::displaySettings() ui->feedbackVolume->setValue(roundf(m_settings.m_feedbackVolumeFactor * 100.0)); ui->feedbackVolumeText->setText(QString("%1").arg(m_settings.m_feedbackVolumeFactor, 0, 'f', 2)); - restoreState(m_rollupState); + getRollupContents()->restoreState(m_rollupState); blockApplySettings(false); } @@ -677,3 +681,29 @@ void NFMModGUI::updateWithStreamTime() ui->navTimeSlider->setValue((int) (posRatio * 100.0)); } } + +void NFMModGUI::makeUIConnections() +{ + QObject::connect(ui->deltaFrequency, &ValueDialZ::changed, this, &NFMModGUI::on_deltaFrequency_changed); + QObject::connect(ui->channelSpacingApply, &QPushButton::clicked, this, &NFMModGUI::on_channelSpacingApply_clicked); + QObject::connect(ui->rfBW, &QSlider::valueChanged, this, &NFMModGUI::on_rfBW_valueChanged); + QObject::connect(ui->afBW, &QSlider::valueChanged, this, &NFMModGUI::on_afBW_valueChanged); + QObject::connect(ui->fmDev, &QSlider::valueChanged, this, &NFMModGUI::on_fmDev_valueChanged); + QObject::connect(ui->toneFrequency, &QDial::valueChanged, this, &NFMModGUI::on_toneFrequency_valueChanged); + QObject::connect(ui->volume, &QDial::valueChanged, this, &NFMModGUI::on_volume_valueChanged); + QObject::connect(ui->channelMute, &QToolButton::toggled, this, &NFMModGUI::on_channelMute_toggled); + QObject::connect(ui->tone, &ButtonSwitch::toggled, this, &NFMModGUI::on_tone_toggled); + QObject::connect(ui->morseKeyer, &ButtonSwitch::toggled, this, &NFMModGUI::on_morseKeyer_toggled); + QObject::connect(ui->mic, &ButtonSwitch::toggled, this, &NFMModGUI::on_mic_toggled); + QObject::connect(ui->play, &ButtonSwitch::toggled, this, &NFMModGUI::on_play_toggled); + QObject::connect(ui->playLoop, &ButtonSwitch::toggled, this, &NFMModGUI::on_playLoop_toggled); + QObject::connect(ui->navTimeSlider, &QSlider::valueChanged, this, &NFMModGUI::on_navTimeSlider_valueChanged); + QObject::connect(ui->showFileDialog, &QPushButton::clicked, this, &NFMModGUI::on_showFileDialog_clicked); + QObject::connect(ui->ctcss, QOverload::of(&QComboBox::currentIndexChanged), this, &NFMModGUI::on_ctcss_currentIndexChanged); + QObject::connect(ui->ctcssOn, &QCheckBox::toggled, this, &NFMModGUI::on_ctcssOn_toggled); + QObject::connect(ui->dcsOn, &QCheckBox::toggled, this, &NFMModGUI::on_dcsOn_toggled); + QObject::connect(ui->dcsCode, &QLineEdit::editingFinished, this, &NFMModGUI::on_dcsCode_editingFinished); + QObject::connect(ui->dcsPositive, &QCheckBox::toggled, this, &NFMModGUI::on_dcsPositive_toggled); + QObject::connect(ui->feedbackEnable, &QToolButton::toggled, this, &NFMModGUI::on_feedbackEnable_toggled); + QObject::connect(ui->feedbackVolume, &QDial::valueChanged, this, &NFMModGUI::on_feedbackVolume_valueChanged); +} diff --git a/plugins/channeltx/modnfm/nfmmodgui.h b/plugins/channeltx/modnfm/nfmmodgui.h index d0c3f1a26..5d386b285 100644 --- a/plugins/channeltx/modnfm/nfmmodgui.h +++ b/plugins/channeltx/modnfm/nfmmodgui.h @@ -48,6 +48,12 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } + virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; }; + virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; }; + virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; }; + virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; }; + virtual QString getTitle() const { return m_settings.m_title; }; + virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; public slots: void channelMarkerChangedByCursor(); @@ -86,6 +92,7 @@ private: void updateWithStreamData(); void updateWithStreamTime(); bool handleMessage(const Message& message); + void makeUIConnections(); void leaveEvent(QEvent*); void enterEvent(QEvent*); diff --git a/plugins/channeltx/modnfm/nfmmodgui.ui b/plugins/channeltx/modnfm/nfmmodgui.ui index 732e4b8f7..925309b4d 100644 --- a/plugins/channeltx/modnfm/nfmmodgui.ui +++ b/plugins/channeltx/modnfm/nfmmodgui.ui @@ -1,7 +1,7 @@ NFMModGUI - + 0 @@ -928,9 +928,9 @@ - RollupWidget + RollupContents QWidget -
gui/rollupwidget.h
+
gui/rollupcontents.h
1
diff --git a/plugins/channeltx/modnfm/nfmmodsettings.cpp b/plugins/channeltx/modnfm/nfmmodsettings.cpp index a60ddc885..8344c59bf 100644 --- a/plugins/channeltx/modnfm/nfmmodsettings.cpp +++ b/plugins/channeltx/modnfm/nfmmodsettings.cpp @@ -79,6 +79,7 @@ void NFMModSettings::resetToDefaults() m_reverseAPIPort = 8888; m_reverseAPIDeviceIndex = 0; m_reverseAPIChannelIndex = 0; + m_workspaceIndex = 0; } QByteArray NFMModSettings::serialize() const @@ -125,6 +126,9 @@ QByteArray NFMModSettings::serialize() const s.writeBlob(27, m_rollupState->serialize()); } + s.writeS32(28, m_workspaceIndex); + s.writeBlob(29, m_geometryBytes); + return s.final(); } @@ -208,6 +212,9 @@ bool NFMModSettings::deserialize(const QByteArray& data) m_rollupState->deserialize(bytetmp); } + d.readS32(28, &m_workspaceIndex, 0); + d.readBlob(29, &m_geometryBytes); + return true; } else diff --git a/plugins/channeltx/modnfm/nfmmodsettings.h b/plugins/channeltx/modnfm/nfmmodsettings.h index 82c60ece5..bc74e4c99 100644 --- a/plugins/channeltx/modnfm/nfmmodsettings.h +++ b/plugins/channeltx/modnfm/nfmmodsettings.h @@ -67,6 +67,8 @@ struct NFMModSettings uint16_t m_reverseAPIPort; uint16_t m_reverseAPIDeviceIndex; uint16_t m_reverseAPIChannelIndex; + int m_workspaceIndex; + QByteArray m_geometryBytes; Serializable *m_channelMarker; Serializable *m_cwKeyerGUI; diff --git a/plugins/channeltx/modpacket/packetmodgui.cpp b/plugins/channeltx/modpacket/packetmodgui.cpp index 368782df5..2c309a80f 100644 --- a/plugins/channeltx/modpacket/packetmodgui.cpp +++ b/plugins/channeltx/modpacket/packetmodgui.cpp @@ -368,7 +368,7 @@ void PacketModGUI::onWidgetRolled(QWidget* widget, bool rollDown) (void) widget; (void) rollDown; - saveState(m_rollupState); + getRollupContents()->saveState(m_rollupState); applySettings(); } @@ -395,6 +395,7 @@ void PacketModGUI::onMenuDialogCalled(const QPoint &p) m_settings.m_reverseAPIChannelIndex = dialog.getReverseAPIChannelIndex(); setWindowTitle(m_settings.m_title); + setTitle(m_channelMarker.getTitle()); setTitleColor(m_settings.m_rgbColor); applySettings(); @@ -425,11 +426,12 @@ PacketModGUI::PacketModGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Baseb m_channelMarker(this), m_doApplySettings(true) { - ui->setupUi(this); + ui->setupUi(getRollupContents()); + getRollupContents()->arrangeRollups(); m_helpURL = "plugins/channeltx/modpacket/readme.md"; setAttribute(Qt::WA_DeleteOnClose, true); - connect(this, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + connect(getRollupContents(), SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &))); m_packetMod = (PacketMod*) channelTx; @@ -491,6 +493,7 @@ PacketModGUI::PacketModGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Baseb ui->spectrumContainer->setVisible(false); displaySettings(); + makeUIConnections(); applySettings(); } @@ -533,6 +536,7 @@ void PacketModGUI::displaySettings() setTitleColor(m_settings.m_rgbColor); setWindowTitle(m_channelMarker.getTitle()); + setTitle(m_channelMarker.getTitle()); displayStreamIndex(); blockApplySettings(true); @@ -572,7 +576,7 @@ void PacketModGUI::displaySettings() ui->via->lineEdit()->setText(m_settings.m_via); ui->packet->setText(m_settings.m_data); - restoreState(m_rollupState); + getRollupContents()->restoreState(m_rollupState); blockApplySettings(false); } @@ -601,3 +605,26 @@ void PacketModGUI::tick() m_channelPowerDbAvg(powDb); ui->channelPower->setText(tr("%1 dB").arg(m_channelPowerDbAvg.asDouble(), 0, 'f', 1)); } + +void PacketModGUI::makeUIConnections() +{ + QObject::connect(ui->deltaFrequency, &ValueDialZ::changed, this, &PacketModGUI::on_deltaFrequency_changed); + QObject::connect(ui->mode, QOverload::of(&QComboBox::currentIndexChanged), this, &PacketModGUI::on_mode_currentIndexChanged); + QObject::connect(ui->rfBW, &QSlider::valueChanged, this, &PacketModGUI::on_rfBW_valueChanged); + QObject::connect(ui->fmDev, &QSlider::valueChanged, this, &PacketModGUI::on_fmDev_valueChanged); + QObject::connect(ui->gain, &QDial::valueChanged, this, &PacketModGUI::on_gain_valueChanged); + QObject::connect(ui->channelMute, &QToolButton::toggled, this, &PacketModGUI::on_channelMute_toggled); + QObject::connect(ui->txButton, &QToolButton::clicked, this, &PacketModGUI::on_txButton_clicked); + QObject::connect(ui->callsign, &QLineEdit::editingFinished, this, &PacketModGUI::on_callsign_editingFinished); + QObject::connect(ui->to, &QComboBox::currentTextChanged, this, &PacketModGUI::on_to_currentTextChanged); + QObject::connect(ui->via, &QComboBox::currentTextChanged, this, &PacketModGUI::on_via_currentTextChanged); + QObject::connect(ui->packet, &QLineEdit::editingFinished, this, &PacketModGUI::on_packet_editingFinished); + QObject::connect(ui->insertPosition, &QToolButton::clicked, this, &PacketModGUI::on_insertPosition_clicked); + QObject::connect(ui->packet, &QLineEdit::returnPressed, this, &PacketModGUI::on_packet_returnPressed); + QObject::connect(ui->repeat, &ButtonSwitch::toggled, this, &PacketModGUI::on_repeat_toggled); + QObject::connect(ui->preEmphasis, &ButtonSwitch::toggled, this, &PacketModGUI::on_preEmphasis_toggled); + QObject::connect(ui->bpf, &ButtonSwitch::toggled, this, &PacketModGUI::on_bpf_toggled); + QObject::connect(ui->udpEnabled, &QCheckBox::clicked, this, &PacketModGUI::on_udpEnabled_clicked); + QObject::connect(ui->udpAddress, &QLineEdit::editingFinished, this, &PacketModGUI::on_udpAddress_editingFinished); + QObject::connect(ui->udpPort, &QLineEdit::editingFinished, this, &PacketModGUI::on_udpPort_editingFinished); +} diff --git a/plugins/channeltx/modpacket/packetmodgui.h b/plugins/channeltx/modpacket/packetmodgui.h index 336039d8d..a875801e7 100644 --- a/plugins/channeltx/modpacket/packetmodgui.h +++ b/plugins/channeltx/modpacket/packetmodgui.h @@ -48,6 +48,12 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } + virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; }; + virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; }; + virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; }; + virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; }; + virtual QString getTitle() const { return m_settings.m_title; }; + virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; public slots: void channelMarkerChangedByCursor(); @@ -76,6 +82,7 @@ private: void displaySettings(); void displayStreamIndex(); bool handleMessage(const Message& message); + void makeUIConnections(); void leaveEvent(QEvent*); void enterEvent(QEvent*); diff --git a/plugins/channeltx/modpacket/packetmodgui.ui b/plugins/channeltx/modpacket/packetmodgui.ui index 34fea6897..b6d77bc32 100644 --- a/plugins/channeltx/modpacket/packetmodgui.ui +++ b/plugins/channeltx/modpacket/packetmodgui.ui @@ -1,7 +1,7 @@ PacketModGUI - + 0 @@ -871,9 +871,9 @@ APRS examples: - RollupWidget + RollupContents QWidget -
gui/rollupwidget.h
+
gui/rollupcontents.h
1
diff --git a/plugins/channeltx/modpacket/packetmodsettings.cpp b/plugins/channeltx/modpacket/packetmodsettings.cpp index 3c4bc8d8b..4ddb2a678 100644 --- a/plugins/channeltx/modpacket/packetmodsettings.cpp +++ b/plugins/channeltx/modpacket/packetmodsettings.cpp @@ -85,6 +85,7 @@ void PacketModSettings::resetToDefaults() m_udpEnabled = false; m_udpAddress = "127.0.0.1"; m_udpPort = 9998; + m_workspaceIndex = 0; } bool PacketModSettings::setMode(QString mode) @@ -231,6 +232,9 @@ QByteArray PacketModSettings::serialize() const s.writeBlob(54, m_rollupState->serialize()); } + s.writeS32(55, m_workspaceIndex); + s.writeBlob(56, m_geometryBytes); + return s.final(); } @@ -332,6 +336,9 @@ bool PacketModSettings::deserialize(const QByteArray& data) m_rollupState->deserialize(bytetmp); } + d.readS32(55, &m_workspaceIndex, 0); + d.readBlob(56, &m_geometryBytes); + return true; } else diff --git a/plugins/channeltx/modpacket/packetmodsettings.h b/plugins/channeltx/modpacket/packetmodsettings.h index 805ae22f7..c4dad2789 100644 --- a/plugins/channeltx/modpacket/packetmodsettings.h +++ b/plugins/channeltx/modpacket/packetmodsettings.h @@ -83,6 +83,8 @@ struct PacketModSettings QString m_udpAddress; uint16_t m_udpPort; Serializable *m_rollupState; + int m_workspaceIndex; + QByteArray m_geometryBytes; PacketModSettings(); void resetToDefaults(); diff --git a/plugins/channeltx/modssb/ssbmodgui.cpp b/plugins/channeltx/modssb/ssbmodgui.cpp index 4c183a16f..eb1dc3cb7 100644 --- a/plugins/channeltx/modssb/ssbmodgui.cpp +++ b/plugins/channeltx/modssb/ssbmodgui.cpp @@ -348,7 +348,7 @@ void SSBModGUI::onWidgetRolled(QWidget* widget, bool rollDown) (void) widget; (void) rollDown; - saveState(m_rollupState); + getRollupContents()->saveState(m_rollupState); applySettings(); } @@ -376,6 +376,7 @@ void SSBModGUI::onMenuDialogCalled(const QPoint &p) m_settings.m_reverseAPIChannelIndex = dialog.getReverseAPIChannelIndex(); setWindowTitle(m_settings.m_title); + setTitle(m_channelMarker.getTitle()); setTitleColor(m_settings.m_rgbColor); applySettings(); @@ -414,10 +415,11 @@ SSBModGUI::SSBModGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSam m_tickCount(0), m_enableNavTime(false) { - ui->setupUi(this); + ui->setupUi(getRollupContents()); + getRollupContents()->arrangeRollups(); m_helpURL = "plugins/channeltx/modssb/readme.md"; setAttribute(Qt::WA_DeleteOnClose, true); - connect(this, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + connect(getRollupContents(), SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &))); m_ssbMod = (SSBMod*) channelTx; @@ -479,6 +481,7 @@ SSBModGUI::SSBModGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSam m_iconDSBLSB.addPixmap(QPixmap("://lsb.png"), QIcon::Normal, QIcon::Off); displaySettings(); + makeUIConnections(); applyBandwidths(5 - ui->spanLog2->value(), true); // does applySettings(true) } @@ -648,6 +651,7 @@ void SSBModGUI::displaySettings() setTitleColor(m_settings.m_rgbColor); setWindowTitle(m_channelMarker.getTitle()); + setTitle(m_channelMarker.getTitle()); displayStreamIndex(); blockApplySettings(true); @@ -715,7 +719,7 @@ void SSBModGUI::displaySettings() ui->feedbackVolume->setValue(roundf(m_settings.m_feedbackVolumeFactor * 100.0)); ui->feedbackVolumeText->setText(QString("%1").arg(m_settings.m_feedbackVolumeFactor, 0, 'f', 2)); - restoreState(m_rollupState); + getRollupContents()->restoreState(m_rollupState); blockApplySettings(false); } @@ -836,3 +840,29 @@ void SSBModGUI::updateWithStreamTime() ui->navTimeSlider->setValue((int) (posRatio * 100.0)); } } + +void SSBModGUI::makeUIConnections() +{ + QObject::connect(ui->deltaFrequency, &ValueDialZ::changed, this, &SSBModGUI::on_deltaFrequency_changed); + QObject::connect(ui->flipSidebands, &QPushButton::clicked, this, &SSBModGUI::on_flipSidebands_clicked); + QObject::connect(ui->dsb, &QToolButton::toggled, this, &SSBModGUI::on_dsb_toggled); + QObject::connect(ui->audioBinaural, &QToolButton::toggled, this, &SSBModGUI::on_audioBinaural_toggled); + QObject::connect(ui->audioFlipChannels, &QToolButton::toggled, this, &SSBModGUI::on_audioFlipChannels_toggled); + QObject::connect(ui->BW, &TickedSlider::valueChanged, this, &SSBModGUI::on_BW_valueChanged); + QObject::connect(ui->lowCut, &TickedSlider::valueChanged, this, &SSBModGUI::on_lowCut_valueChanged); + QObject::connect(ui->volume, &QDial::valueChanged, this, &SSBModGUI::on_volume_valueChanged); + QObject::connect(ui->audioMute, &QToolButton::toggled, this, &SSBModGUI::on_audioMute_toggled); + QObject::connect(ui->tone, &ButtonSwitch::toggled, this, &SSBModGUI::on_tone_toggled); + QObject::connect(ui->toneFrequency, &QDial::valueChanged, this, &SSBModGUI::on_toneFrequency_valueChanged); + QObject::connect(ui->mic, &ButtonSwitch::toggled, this, &SSBModGUI::on_mic_toggled); + QObject::connect(ui->agc, &ButtonSwitch::toggled, this, &SSBModGUI::on_agc_toggled); + QObject::connect(ui->cmpPreGain, &QDial::valueChanged, this, &SSBModGUI::on_cmpPreGain_valueChanged); + QObject::connect(ui->cmpThreshold, &QDial::valueChanged, this, &SSBModGUI::on_cmpThreshold_valueChanged); + QObject::connect(ui->play, &ButtonSwitch::toggled, this, &SSBModGUI::on_play_toggled); + QObject::connect(ui->playLoop, &ButtonSwitch::toggled, this, &SSBModGUI::on_playLoop_toggled); + QObject::connect(ui->morseKeyer, &ButtonSwitch::toggled, this, &SSBModGUI::on_morseKeyer_toggled); + QObject::connect(ui->navTimeSlider, &QSlider::valueChanged, this, &SSBModGUI::on_navTimeSlider_valueChanged); + QObject::connect(ui->showFileDialog, &QPushButton::clicked, this, &SSBModGUI::on_showFileDialog_clicked); + QObject::connect(ui->feedbackEnable, &QToolButton::toggled, this, &SSBModGUI::on_feedbackEnable_toggled); + QObject::connect(ui->feedbackVolume, &QDial::valueChanged, this, &SSBModGUI::on_feedbackVolume_valueChanged); +} diff --git a/plugins/channeltx/modssb/ssbmodgui.h b/plugins/channeltx/modssb/ssbmodgui.h index f5159ba7f..3f4e21b8a 100644 --- a/plugins/channeltx/modssb/ssbmodgui.h +++ b/plugins/channeltx/modssb/ssbmodgui.h @@ -49,6 +49,12 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } + virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; }; + virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; }; + virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; }; + virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; }; + virtual QString getTitle() const { return m_settings.m_title; }; + virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; public slots: void channelMarkerChangedByCursor(); @@ -92,6 +98,7 @@ private: void updateWithStreamTime(); void channelMarkerUpdate(); bool handleMessage(const Message& message); + void makeUIConnections(); void leaveEvent(QEvent*); void enterEvent(QEvent*); diff --git a/plugins/channeltx/modssb/ssbmodgui.ui b/plugins/channeltx/modssb/ssbmodgui.ui index 035a6dbf7..0465fac0a 100644 --- a/plugins/channeltx/modssb/ssbmodgui.ui +++ b/plugins/channeltx/modssb/ssbmodgui.ui @@ -1,7 +1,7 @@ SSBModGUI - + 0 @@ -1245,9 +1245,9 @@ - RollupWidget + RollupContents QWidget -
gui/rollupwidget.h
+
gui/rollupcontents.h
1
diff --git a/plugins/channeltx/modssb/ssbmodsettings.cpp b/plugins/channeltx/modssb/ssbmodsettings.cpp index 7ddfa67ba..14961ea31 100644 --- a/plugins/channeltx/modssb/ssbmodsettings.cpp +++ b/plugins/channeltx/modssb/ssbmodsettings.cpp @@ -75,6 +75,7 @@ void SSBModSettings::resetToDefaults() m_reverseAPIPort = 8888; m_reverseAPIDeviceIndex = 0; m_reverseAPIChannelIndex = 0; + m_workspaceIndex = 0; } QByteArray SSBModSettings::serialize() const @@ -127,6 +128,9 @@ QByteArray SSBModSettings::serialize() const s.writeBlob(31, m_rollupState->serialize()); } + s.writeS32(32, m_workspaceIndex); + s.writeBlob(33, m_geometryBytes); + return s.final(); } @@ -219,6 +223,9 @@ bool SSBModSettings::deserialize(const QByteArray& data) m_rollupState->deserialize(bytetmp); } + d.readS32(32, &m_workspaceIndex, 0); + d.readBlob(33, &m_geometryBytes); + return true; } else diff --git a/plugins/channeltx/modssb/ssbmodsettings.h b/plugins/channeltx/modssb/ssbmodsettings.h index dbe67f68a..d1e40115b 100644 --- a/plugins/channeltx/modssb/ssbmodsettings.h +++ b/plugins/channeltx/modssb/ssbmodsettings.h @@ -70,6 +70,8 @@ struct SSBModSettings uint16_t m_reverseAPIPort; uint16_t m_reverseAPIDeviceIndex; uint16_t m_reverseAPIChannelIndex; + int m_workspaceIndex; + QByteArray m_geometryBytes; Serializable *m_channelMarker; Serializable *m_spectrumGUI; diff --git a/plugins/channeltx/modwfm/wfmmodgui.cpp b/plugins/channeltx/modwfm/wfmmodgui.cpp index 2039b56d6..0ecca32d1 100644 --- a/plugins/channeltx/modwfm/wfmmodgui.cpp +++ b/plugins/channeltx/modwfm/wfmmodgui.cpp @@ -277,7 +277,7 @@ void WFMModGUI::onWidgetRolled(QWidget* widget, bool rollDown) (void) widget; (void) rollDown; - saveState(m_rollupState); + getRollupContents()->saveState(m_rollupState); applySettings(); } @@ -305,6 +305,7 @@ void WFMModGUI::onMenuDialogCalled(const QPoint &p) m_settings.m_reverseAPIChannelIndex = dialog.getReverseAPIChannelIndex(); setWindowTitle(m_settings.m_title); + setTitle(m_channelMarker.getTitle()); setTitleColor(m_settings.m_rgbColor); applySettings(); @@ -342,7 +343,8 @@ WFMModGUI::WFMModGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSam m_tickCount(0), m_enableNavTime(false) { - ui->setupUi(this); + ui->setupUi(getRollupContents()); + getRollupContents()->arrangeRollups(); m_helpURL = "plugins/channeltx/modwfm/readme.md"; setAttribute(Qt::WA_DeleteOnClose, true); @@ -356,7 +358,7 @@ WFMModGUI::WFMModGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSam blockApplySettings(false); - connect(this, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + connect(getRollupContents(), SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &))); m_wfmMod = (WFMMod*) channelTx; @@ -403,6 +405,7 @@ WFMModGUI::WFMModGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSam m_wfmMod->setLevelMeter(ui->volumeMeter); displaySettings(); + makeUIConnections(); applySettings(true); } @@ -438,6 +441,7 @@ void WFMModGUI::displaySettings() setTitleColor(m_settings.m_rgbColor); setWindowTitle(m_channelMarker.getTitle()); + setTitle(m_channelMarker.getTitle()); displayStreamIndex(); blockApplySettings(true); @@ -475,7 +479,7 @@ void WFMModGUI::displaySettings() ui->feedbackVolume->setValue(roundf(m_settings.m_feedbackVolumeFactor * 100.0)); ui->feedbackVolumeText->setText(QString("%1").arg(m_settings.m_feedbackVolumeFactor, 0, 'f', 2)); - restoreState(m_rollupState); + getRollupContents()->restoreState(m_rollupState); blockApplySettings(false); } @@ -596,3 +600,23 @@ void WFMModGUI::updateWithStreamTime() ui->navTimeSlider->setValue((int) (posRatio * 100.0)); } } + +void WFMModGUI::makeUIConnections() +{ + QObject::connect(ui->deltaFrequency, &ValueDialZ::changed, this, &WFMModGUI::on_deltaFrequency_changed); + QObject::connect(ui->rfBW, QOverload::of(&QComboBox::currentIndexChanged), this, &WFMModGUI::on_rfBW_currentIndexChanged); + QObject::connect(ui->afBW, &QSlider::valueChanged, this, &WFMModGUI::on_afBW_valueChanged); + QObject::connect(ui->fmDev, &QSlider::valueChanged, this, &WFMModGUI::on_fmDev_valueChanged); + QObject::connect(ui->toneFrequency, &QDial::valueChanged, this, &WFMModGUI::on_toneFrequency_valueChanged); + QObject::connect(ui->volume, &QDial::valueChanged, this, &WFMModGUI::on_volume_valueChanged); + QObject::connect(ui->channelMute, &QToolButton::toggled, this, &WFMModGUI::on_channelMute_toggled); + QObject::connect(ui->tone, &ButtonSwitch::toggled, this, &WFMModGUI::on_tone_toggled); + QObject::connect(ui->morseKeyer, &ButtonSwitch::toggled, this, &WFMModGUI::on_morseKeyer_toggled); + QObject::connect(ui->mic, &ButtonSwitch::toggled, this, &WFMModGUI::on_mic_toggled); + QObject::connect(ui->play, &ButtonSwitch::toggled, this, &WFMModGUI::on_play_toggled); + QObject::connect(ui->playLoop, &ButtonSwitch::toggled, this, &WFMModGUI::on_playLoop_toggled); + QObject::connect(ui->navTimeSlider, &QSlider::valueChanged, this, &WFMModGUI::on_navTimeSlider_valueChanged); + QObject::connect(ui->showFileDialog, &QPushButton::clicked, this, &WFMModGUI::on_showFileDialog_clicked); + QObject::connect(ui->feedbackEnable, &QToolButton::toggled, this, &WFMModGUI::on_feedbackEnable_toggled); + QObject::connect(ui->feedbackVolume, &QDial::valueChanged, this, &WFMModGUI::on_feedbackVolume_valueChanged); +} diff --git a/plugins/channeltx/modwfm/wfmmodgui.h b/plugins/channeltx/modwfm/wfmmodgui.h index f85465ab5..8188452dc 100644 --- a/plugins/channeltx/modwfm/wfmmodgui.h +++ b/plugins/channeltx/modwfm/wfmmodgui.h @@ -46,6 +46,12 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } + virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; }; + virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; }; + virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; }; + virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; }; + virtual QString getTitle() const { return m_settings.m_title; }; + virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; public slots: void channelMarkerChangedByCursor(); @@ -82,6 +88,7 @@ private: void updateWithStreamData(); void updateWithStreamTime(); bool handleMessage(const Message& message); + void makeUIConnections(); void leaveEvent(QEvent*); void enterEvent(QEvent*); diff --git a/plugins/channeltx/modwfm/wfmmodgui.ui b/plugins/channeltx/modwfm/wfmmodgui.ui index 919a97c37..9183b2492 100644 --- a/plugins/channeltx/modwfm/wfmmodgui.ui +++ b/plugins/channeltx/modwfm/wfmmodgui.ui @@ -1,7 +1,7 @@ WFMModGUI - + 0 @@ -765,9 +765,9 @@ - RollupWidget + RollupContents QWidget -
gui/rollupwidget.h
+
gui/rollupcontents.h
1
diff --git a/plugins/channeltx/modwfm/wfmmodsettings.cpp b/plugins/channeltx/modwfm/wfmmodsettings.cpp index 3bebd733b..bff6853a9 100644 --- a/plugins/channeltx/modwfm/wfmmodsettings.cpp +++ b/plugins/channeltx/modwfm/wfmmodsettings.cpp @@ -60,6 +60,7 @@ void WFMModSettings::resetToDefaults() m_reverseAPIPort = 8888; m_reverseAPIDeviceIndex = 0; m_reverseAPIChannelIndex = 0; + m_workspaceIndex = 0; } QByteArray WFMModSettings::serialize() const @@ -101,6 +102,9 @@ QByteArray WFMModSettings::serialize() const s.writeBlob(22, m_rollupState->serialize()); } + s.writeS32(23, m_workspaceIndex); + s.writeBlob(24, m_geometryBytes); + return s.final(); } @@ -177,6 +181,9 @@ bool WFMModSettings::deserialize(const QByteArray& data) m_rollupState->deserialize(bytetmp); } + d.readS32(23, &m_workspaceIndex, 0); + d.readBlob(24, &m_geometryBytes); + return true; } else diff --git a/plugins/channeltx/modwfm/wfmmodsettings.h b/plugins/channeltx/modwfm/wfmmodsettings.h index 6d6dbb457..c17fff830 100644 --- a/plugins/channeltx/modwfm/wfmmodsettings.h +++ b/plugins/channeltx/modwfm/wfmmodsettings.h @@ -59,6 +59,8 @@ struct WFMModSettings uint16_t m_reverseAPIPort; uint16_t m_reverseAPIDeviceIndex; uint16_t m_reverseAPIChannelIndex; + int m_workspaceIndex; + QByteArray m_geometryBytes; Serializable *m_channelMarker; Serializable *m_cwKeyerGUI; diff --git a/plugins/channeltx/remotesource/remotesourcegui.cpp b/plugins/channeltx/remotesource/remotesourcegui.cpp index 998d2110f..a8e5a0461 100644 --- a/plugins/channeltx/remotesource/remotesourcegui.cpp +++ b/plugins/channeltx/remotesource/remotesourcegui.cpp @@ -168,11 +168,11 @@ RemoteSourceGUI::RemoteSourceGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, m_resetCounts(true), m_tickCount(0) { - (void) channelTx; - ui->setupUi(this); + ui->setupUi(getRollupContents()); + getRollupContents()->arrangeRollups(); m_helpURL = "plugins/channeltx/remotesource/readme.md"; setAttribute(Qt::WA_DeleteOnClose, true); - connect(this, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + connect(getRollupContents(), SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &))); m_remoteSrc = (RemoteSource*) channelTx; @@ -200,6 +200,7 @@ RemoteSourceGUI::RemoteSourceGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, m_time.start(); displaySettings(); + makeUIConnections(); displayPosition(); displayRateAndShift(); applySettings(true); @@ -237,12 +238,13 @@ void RemoteSourceGUI::displaySettings() setTitleColor(m_settings.m_rgbColor); setWindowTitle(m_channelMarker.getTitle()); + setTitle(m_channelMarker.getTitle()); displayStreamIndex(); blockApplySettings(true); ui->dataAddress->setText(m_settings.m_dataAddress); ui->dataPort->setText(tr("%1").arg(m_settings.m_dataPort)); - restoreState(m_rollupState); + getRollupContents()->restoreState(m_rollupState); blockApplySettings(false); } @@ -302,7 +304,7 @@ void RemoteSourceGUI::onWidgetRolled(QWidget* widget, bool rollDown) (void) widget; (void) rollDown; - saveState(m_rollupState); + getRollupContents()->saveState(m_rollupState); applySettings(); } @@ -329,6 +331,7 @@ void RemoteSourceGUI::onMenuDialogCalled(const QPoint &p) m_settings.m_reverseAPIChannelIndex = dialog.getReverseAPIChannelIndex(); setWindowTitle(m_settings.m_title); + setTitle(m_channelMarker.getTitle()); setTitleColor(m_settings.m_rgbColor); applySettings(); @@ -483,3 +486,13 @@ void RemoteSourceGUI::tick() void RemoteSourceGUI::channelMarkerChangedByCursor() { } + +void RemoteSourceGUI::makeUIConnections() +{ + QObject::connect(ui->interpolationFactor, QOverload::of(&QComboBox::currentIndexChanged), this, &RemoteSourceGUI::on_interpolationFactor_currentIndexChanged); + QObject::connect(ui->position, &QSlider::valueChanged, this, &RemoteSourceGUI::on_position_valueChanged); + QObject::connect(ui->dataAddress, &QLineEdit::returnPressed, this, &RemoteSourceGUI::on_dataAddress_returnPressed); + QObject::connect(ui->dataPort, &QLineEdit::returnPressed, this, &RemoteSourceGUI::on_dataPort_returnPressed); + QObject::connect(ui->dataApplyButton, &QPushButton::clicked, this, &RemoteSourceGUI::on_dataApplyButton_clicked); + QObject::connect(ui->eventCountsReset, &QPushButton::clicked, this, &RemoteSourceGUI::on_eventCountsReset_clicked); +} diff --git a/plugins/channeltx/remotesource/remotesourcegui.h b/plugins/channeltx/remotesource/remotesourcegui.h index daca3cd25..340d7ad86 100644 --- a/plugins/channeltx/remotesource/remotesourcegui.h +++ b/plugins/channeltx/remotesource/remotesourcegui.h @@ -47,6 +47,12 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } + virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; }; + virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; }; + virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; }; + virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; }; + virtual QString getTitle() const { return m_settings.m_title; }; + virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; public slots: void channelMarkerChangedByCursor(); @@ -86,6 +92,7 @@ private: void displayPosition(); void displayStreamIndex(); bool handleMessage(const Message& message); + void makeUIConnections(); void leaveEvent(QEvent*); void enterEvent(QEvent*); diff --git a/plugins/channeltx/remotesource/remotesourcegui.ui b/plugins/channeltx/remotesource/remotesourcegui.ui index 81d1463f1..7409712b6 100644 --- a/plugins/channeltx/remotesource/remotesourcegui.ui +++ b/plugins/channeltx/remotesource/remotesourcegui.ui @@ -1,7 +1,7 @@ RemoteSourceGUI - + 0 @@ -589,9 +589,9 @@ - RollupWidget + RollupContents QWidget -
gui/rollupwidget.h
+
gui/rollupcontents.h
1
diff --git a/plugins/channeltx/remotesource/remotesourcesettings.cpp b/plugins/channeltx/remotesource/remotesourcesettings.cpp index d0fcbfe21..d8a5d7b96 100644 --- a/plugins/channeltx/remotesource/remotesourcesettings.cpp +++ b/plugins/channeltx/remotesource/remotesourcesettings.cpp @@ -44,6 +44,7 @@ void RemoteSourceSettings::resetToDefaults() m_reverseAPIPort = 8888; m_reverseAPIDeviceIndex = 0; m_reverseAPIChannelIndex = 0; + m_workspaceIndex = 0; } QByteArray RemoteSourceSettings::serialize() const @@ -71,6 +72,9 @@ QByteArray RemoteSourceSettings::serialize() const s.writeBlob(14, m_channelMarker->serialize()); } + s.writeS32(15, m_workspaceIndex); + s.writeBlob(16, m_geometryBytes); + return s.final(); } @@ -132,6 +136,9 @@ bool RemoteSourceSettings::deserialize(const QByteArray& data) m_channelMarker->deserialize(bytetmp); } + d.readS32(15, &m_workspaceIndex, 0); + d.readBlob(16, &m_geometryBytes); + return true; } else diff --git a/plugins/channeltx/remotesource/remotesourcesettings.h b/plugins/channeltx/remotesource/remotesourcesettings.h index aff6a0426..1bb40ffdd 100644 --- a/plugins/channeltx/remotesource/remotesourcesettings.h +++ b/plugins/channeltx/remotesource/remotesourcesettings.h @@ -37,6 +37,8 @@ struct RemoteSourceSettings uint16_t m_reverseAPIPort; uint16_t m_reverseAPIDeviceIndex; uint16_t m_reverseAPIChannelIndex; + int m_workspaceIndex; + QByteArray m_geometryBytes; Serializable *m_channelMarker; Serializable *m_rollupState; diff --git a/plugins/channeltx/udpsource/udpsourcegui.cpp b/plugins/channeltx/udpsource/udpsourcegui.cpp index 905a8cd4c..500c51e50 100644 --- a/plugins/channeltx/udpsource/udpsourcegui.cpp +++ b/plugins/channeltx/udpsource/udpsourcegui.cpp @@ -107,9 +107,10 @@ UDPSourceGUI::UDPSourceGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Baseb m_rfBandwidthChanged(false), m_doApplySettings(true) { - ui->setupUi(this); + ui->setupUi(getRollupContents()); + getRollupContents()->arrangeRollups(); m_helpURL = "plugins/channeltx/udpsource/readme.md"; - connect(this, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + connect(getRollupContents(), SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &))); setAttribute(Qt::WA_DeleteOnClose, true); @@ -154,6 +155,7 @@ UDPSourceGUI::UDPSourceGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Baseb m_settings.setRollupState(&m_rollupState); displaySettings(); + makeUIConnections(); applySettings(true); } @@ -193,7 +195,8 @@ void UDPSourceGUI::displaySettings() m_channelMarker.setColor(m_settings.m_rgbColor); setTitleColor(m_settings.m_rgbColor); - this->setWindowTitle(m_channelMarker.getTitle()); + setWindowTitle(m_channelMarker.getTitle()); + setTitle(m_channelMarker.getTitle()); displayStreamIndex(); blockApplySettings(true); @@ -236,7 +239,7 @@ void UDPSourceGUI::displaySettings() ui->applyBtn->setEnabled(false); ui->applyBtn->setStyleSheet("QPushButton { background:rgb(79,79,79); }"); - restoreState(m_rollupState); + getRollupContents()->restoreState(m_rollupState); blockApplySettings(false); } @@ -472,7 +475,7 @@ void UDPSourceGUI::onWidgetRolled(QWidget* widget, bool rollDown) m_udpSource->setSpectrum(rollDown); } - saveState(m_rollupState); + getRollupContents()->saveState(m_rollupState); applySettings(); } @@ -499,6 +502,7 @@ void UDPSourceGUI::onMenuDialogCalled(const QPoint &p) m_settings.m_reverseAPIChannelIndex = dialog.getReverseAPIChannelIndex(); setWindowTitle(m_channelMarker.getTitle()); + setTitle(m_channelMarker.getTitle()); setTitleColor(m_settings.m_rgbColor); applySettings(); @@ -637,3 +641,25 @@ void UDPSourceGUI::setSampleFormat(int index) } } +void UDPSourceGUI::makeUIConnections() +{ + QObject::connect(ui->deltaFrequency, &ValueDialZ::changed, this, &UDPSourceGUI::on_deltaFrequency_changed); + QObject::connect(ui->sampleFormat, QOverload::of(&QComboBox::currentIndexChanged), this, &UDPSourceGUI::on_sampleFormat_currentIndexChanged); + QObject::connect(ui->localUDPAddress, &QLineEdit::editingFinished, this, &UDPSourceGUI::on_localUDPAddress_editingFinished); + QObject::connect(ui->localUDPPort, &QLineEdit::editingFinished, this, &UDPSourceGUI::on_localUDPPort_editingFinished); + QObject::connect(ui->multicastAddress, &QLineEdit::editingFinished, this, &UDPSourceGUI::on_multicastAddress_editingFinished); + QObject::connect(ui->multicastJoin, &ButtonSwitch::toggled, this, &UDPSourceGUI::on_multicastJoin_toggled); + QObject::connect(ui->sampleRate, &QLineEdit::textEdited, this, &UDPSourceGUI::on_sampleRate_textEdited); + QObject::connect(ui->rfBandwidth, &QLineEdit::textEdited, this, &UDPSourceGUI::on_rfBandwidth_textEdited); + QObject::connect(ui->fmDeviation, &QLineEdit::textEdited, this, &UDPSourceGUI::on_fmDeviation_textEdited); + QObject::connect(ui->amModPercent, &QLineEdit::textEdited, this, &UDPSourceGUI::on_amModPercent_textEdited); + QObject::connect(ui->applyBtn, &QPushButton::clicked, this, &UDPSourceGUI::on_applyBtn_clicked); + QObject::connect(ui->gainIn, &QDial::valueChanged, this, &UDPSourceGUI::on_gainIn_valueChanged); + QObject::connect(ui->gainOut, &QDial::valueChanged, this, &UDPSourceGUI::on_gainOut_valueChanged); + QObject::connect(ui->squelch, &QSlider::valueChanged, this, &UDPSourceGUI::on_squelch_valueChanged); + QObject::connect(ui->squelchGate, &QDial::valueChanged, this, &UDPSourceGUI::on_squelchGate_valueChanged); + QObject::connect(ui->channelMute, &QToolButton::toggled, this, &UDPSourceGUI::on_channelMute_toggled); + QObject::connect(ui->resetUDPReadIndex, &QPushButton::clicked, this, &UDPSourceGUI::on_resetUDPReadIndex_clicked); + QObject::connect(ui->autoRWBalance, &ButtonSwitch::toggled, this, &UDPSourceGUI::on_autoRWBalance_toggled); + QObject::connect(ui->stereoInput, &QToolButton::toggled, this, &UDPSourceGUI::on_stereoInput_toggled); +} diff --git a/plugins/channeltx/udpsource/udpsourcegui.h b/plugins/channeltx/udpsource/udpsourcegui.h index d20614aa5..372fd6b99 100644 --- a/plugins/channeltx/udpsource/udpsourcegui.h +++ b/plugins/channeltx/udpsource/udpsourcegui.h @@ -49,6 +49,12 @@ public: virtual QByteArray serialize() const; virtual bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } + virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; }; + virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; }; + virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; }; + virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; }; + virtual QString getTitle() const { return m_settings.m_title; }; + virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; public slots: void channelMarkerChangedByCursor(); @@ -81,6 +87,7 @@ private: void setSampleFormat(int index); void setSampleFormatIndex(const UDPSourceSettings::SampleFormat& sampleFormat); bool handleMessage(const Message& message); + void makeUIConnections(); void leaveEvent(QEvent*); void enterEvent(QEvent*); diff --git a/plugins/channeltx/udpsource/udpsourcegui.ui b/plugins/channeltx/udpsource/udpsourcegui.ui index 95764c265..ee3d3a970 100644 --- a/plugins/channeltx/udpsource/udpsourcegui.ui +++ b/plugins/channeltx/udpsource/udpsourcegui.ui @@ -1,7 +1,7 @@ UDPSourceGUI - + 0 @@ -1040,9 +1040,9 @@ - RollupWidget + RollupContents QWidget -
gui/rollupwidget.h
+
gui/rollupcontents.h
1
diff --git a/plugins/channeltx/udpsource/udpsourcesettings.cpp b/plugins/channeltx/udpsource/udpsourcesettings.cpp index 5254a3e0d..b270d7707 100644 --- a/plugins/channeltx/udpsource/udpsourcesettings.cpp +++ b/plugins/channeltx/udpsource/udpsourcesettings.cpp @@ -60,6 +60,7 @@ void UDPSourceSettings::resetToDefaults() m_reverseAPIPort = 8888; m_reverseAPIDeviceIndex = 0; m_reverseAPIChannelIndex = 0; + m_workspaceIndex = 0; } QByteArray UDPSourceSettings::serialize() const @@ -102,6 +103,9 @@ QByteArray UDPSourceSettings::serialize() const s.writeBlob(27, m_rollupState->serialize()); } + s.writeS32(28, m_workspaceIndex); + s.writeBlob(29, m_geometryBytes); + return s.final(); } @@ -195,6 +199,9 @@ bool UDPSourceSettings::deserialize(const QByteArray& data) m_rollupState->deserialize(bytetmp); } + d.readS32(28, &m_workspaceIndex, 0); + d.readBlob(29, &m_geometryBytes); + return true; } else diff --git a/plugins/channeltx/udpsource/udpsourcesettings.h b/plugins/channeltx/udpsource/udpsourcesettings.h index e971db7ad..1b02f8fcf 100644 --- a/plugins/channeltx/udpsource/udpsourcesettings.h +++ b/plugins/channeltx/udpsource/udpsourcesettings.h @@ -67,6 +67,8 @@ struct UDPSourceSettings uint16_t m_reverseAPIPort; uint16_t m_reverseAPIDeviceIndex; uint16_t m_reverseAPIChannelIndex; + int m_workspaceIndex; + QByteArray m_geometryBytes; Serializable *m_channelMarker; Serializable *m_spectrumGUI; diff --git a/plugins/feature/ais/aisgui.cpp b/plugins/feature/ais/aisgui.cpp index e480f9223..455d52f9b 100644 --- a/plugins/feature/ais/aisgui.cpp +++ b/plugins/feature/ais/aisgui.cpp @@ -166,7 +166,6 @@ void AISGUI::onWidgetRolled(QWidget* widget, bool rollDown) (void) widget; (void) rollDown; - shrinkWindow(); getRollupContents()->saveState(m_rollupState); applySettings(); } diff --git a/plugins/feature/ais/aissettings.cpp b/plugins/feature/ais/aissettings.cpp index bdc7145f9..552543bcb 100644 --- a/plugins/feature/ais/aissettings.cpp +++ b/plugins/feature/ais/aissettings.cpp @@ -72,8 +72,8 @@ QByteArray AISSettings::serialize() const s.writeBlob(27, m_rollupState->serialize()); } - s.writeS32(27, m_workspaceIndex); - s.writeBlob(28, m_geometryBytes); + s.writeS32(28, m_workspaceIndex); + s.writeBlob(29, m_geometryBytes); for (int i = 0; i < AIS_VESSEL_COLUMNS; i++) { s.writeS32(300 + i, m_vesselColumnIndexes[i]); @@ -126,8 +126,8 @@ bool AISSettings::deserialize(const QByteArray& data) m_rollupState->deserialize(bytetmp); } - d.readS32(27, &m_workspaceIndex, 0); - d.readBlob(28, &m_geometryBytes); + d.readS32(28, &m_workspaceIndex, 0); + d.readBlob(29, &m_geometryBytes); for (int i = 0; i < AIS_VESSEL_COLUMNS; i++) { d.readS32(300 + i, &m_vesselColumnIndexes[i], i); diff --git a/plugins/feature/demodanalyzer/demodanalyzergui.ui b/plugins/feature/demodanalyzer/demodanalyzergui.ui index 6b4316257..a4ca773de 100644 --- a/plugins/feature/demodanalyzer/demodanalyzergui.ui +++ b/plugins/feature/demodanalyzer/demodanalyzergui.ui @@ -36,10 +36,16 @@ 0 10 - 631 + 718 41
+ + + 718 + 0 + + Settings @@ -262,7 +268,7 @@
- 716 + 718 0 @@ -287,6 +293,12 @@
+ + + 0 + 0 + + 200 @@ -323,7 +335,7 @@ - 716 + 718 0 @@ -348,6 +360,12 @@
+ + + 0 + 0 + + 200 diff --git a/plugins/feature/map/map.cpp b/plugins/feature/map/map.cpp index 259ca4dfd..b7f2a66c1 100644 --- a/plugins/feature/map/map.cpp +++ b/plugins/feature/map/map.cpp @@ -110,7 +110,7 @@ bool Map::handleMessage(const Message& cmd) } else if (MainCore::MsgMapItem::match(cmd)) { - qDebug() << "Map::handleMessage: MsgMapItem"; + // qDebug() << "Map::handleMessage: MsgMapItem"; MainCore::MsgMapItem& msgMapItem = (MainCore::MsgMapItem&) cmd; MainCore::MsgMapItem *copy = new MainCore::MsgMapItem(msgMapItem); getMessageQueueToGUI()->push(copy); diff --git a/plugins/feature/pertester/pertestergui.ui b/plugins/feature/pertester/pertestergui.ui index 648ee13db..adc0d8da9 100644 --- a/plugins/feature/pertester/pertestergui.ui +++ b/plugins/feature/pertester/pertestergui.ui @@ -35,7 +35,7 @@
- GS-232 Rotator Controller + Packet Error Rate Tester diff --git a/plugins/samplemimo/testmi/testmigui.cpp b/plugins/samplemimo/testmi/testmigui.cpp index 41fc673c1..173e71eb5 100644 --- a/plugins/samplemimo/testmi/testmigui.cpp +++ b/plugins/samplemimo/testmi/testmigui.cpp @@ -45,6 +45,8 @@ TestMIGui::TestMIGui(DeviceUISet *deviceUISet, QWidget* parent) : ui(new Ui::TestMIGui), m_deviceUISet(deviceUISet), m_settings(), + m_streamIndex(0), + m_spectrumStreamIndex(0), m_doApplySettings(true), m_forceSettings(true), m_sampleMIMO(nullptr), diff --git a/sdrbase/device/deviceapi.cpp b/sdrbase/device/deviceapi.cpp index c8a1448f0..813d243d3 100644 --- a/sdrbase/device/deviceapi.cpp +++ b/sdrbase/device/deviceapi.cpp @@ -819,3 +819,9 @@ void DeviceAPI::renumerateChannels() } } } + +void DeviceAPI::setDeviceSetIndex(int deviceSetIndex) +{ + m_deviceTabIndex = deviceSetIndex; + renumerateChannels(); +} diff --git a/sdrbase/device/deviceapi.h b/sdrbase/device/deviceapi.h index b11766a0d..630ee0ee0 100644 --- a/sdrbase/device/deviceapi.h +++ b/sdrbase/device/deviceapi.h @@ -123,6 +123,7 @@ public: uint32_t getDeviceItemIndex() const { return m_deviceItemIndex; } int getDeviceSetIndex() const { return m_deviceTabIndex; } + void setDeviceSetIndex(int deviceSetIndex); PluginInterface *getPluginInterface() { return m_pluginInterface; } void getDeviceEngineStateStr(QString& state, int subsystemIndex = 0); diff --git a/sdrbase/device/deviceset.h b/sdrbase/device/deviceset.h index c4c981d03..d697f1b85 100644 --- a/sdrbase/device/deviceset.h +++ b/sdrbase/device/deviceset.h @@ -51,6 +51,7 @@ public: int getNumberOfChannels() const { return m_channelInstanceRegistrations.size(); } int getIndex() const { return m_deviceTabIndex; } + void setIndex(int index) { m_deviceTabIndex = index; } void freeChannels(); const ChannelAPI *getChannelAt(int channelIndex) const; ChannelAPI *getChannelAt(int channelIndex); diff --git a/sdrbase/dsp/dspengine.cpp b/sdrbase/dsp/dspengine.cpp index 4685a4707..8db297d79 100644 --- a/sdrbase/dsp/dspengine.cpp +++ b/sdrbase/dsp/dspengine.cpp @@ -40,7 +40,7 @@ DSPEngine::DSPEngine() : DSPEngine::~DSPEngine() { - std::vector::iterator it = m_deviceSourceEngines.begin(); + QList::iterator it = m_deviceSourceEngines.begin(); while (it != m_deviceSourceEngines.end()) { @@ -63,6 +63,7 @@ DSPDeviceSourceEngine *DSPEngine::addDeviceSourceEngine() { m_deviceSourceEngines.push_back(new DSPDeviceSourceEngine(m_deviceSourceEnginesUIDSequence)); m_deviceSourceEnginesUIDSequence++; + m_deviceEngineReferences.push_back(DeviceEngineReference{0, m_deviceSourceEngines.size() - 1}); return m_deviceSourceEngines.back(); } @@ -71,9 +72,18 @@ void DSPEngine::removeLastDeviceSourceEngine() if (m_deviceSourceEngines.size() > 0) { DSPDeviceSourceEngine *lastDeviceEngine = m_deviceSourceEngines.back(); + int lastSourceDeviceEngineIndex = m_deviceSourceEngines.size() - 1; delete lastDeviceEngine; m_deviceSourceEngines.pop_back(); - m_deviceSourceEnginesUIDSequence--; + + for (int i = 0; i < m_deviceEngineReferences.size(); i++) + { + if (m_deviceEngineReferences[i].deviceEngineIndex == lastSourceDeviceEngineIndex) + { + m_deviceEngineReferences.removeAt(i); + break; + } + } } } @@ -81,6 +91,7 @@ DSPDeviceSinkEngine *DSPEngine::addDeviceSinkEngine() { m_deviceSinkEngines.push_back(new DSPDeviceSinkEngine(m_deviceSinkEnginesUIDSequence)); m_deviceSinkEnginesUIDSequence++; + m_deviceEngineReferences.push_back(DeviceEngineReference{1, m_deviceSinkEngines.size() - 1}); return m_deviceSinkEngines.back(); } @@ -89,9 +100,18 @@ void DSPEngine::removeLastDeviceSinkEngine() if (m_deviceSinkEngines.size() > 0) { DSPDeviceSinkEngine *lastDeviceEngine = m_deviceSinkEngines.back(); + int lastSinkDeviceEngineIndex = m_deviceSinkEngines.size() - 1; delete lastDeviceEngine; m_deviceSinkEngines.pop_back(); - m_deviceSinkEnginesUIDSequence--; + + for (int i = 0; i < m_deviceEngineReferences.size(); i++) + { + if (m_deviceEngineReferences[i].deviceEngineIndex == lastSinkDeviceEngineIndex) + { + m_deviceEngineReferences.removeAt(i); + break; + } + } } } @@ -99,6 +119,7 @@ DSPDeviceMIMOEngine *DSPEngine::addDeviceMIMOEngine() { m_deviceMIMOEngines.push_back(new DSPDeviceMIMOEngine(m_deviceMIMOEnginesUIDSequence)); m_deviceMIMOEnginesUIDSequence++; + m_deviceEngineReferences.push_back(DeviceEngineReference{2, m_deviceMIMOEngines.size() - 1}); return m_deviceMIMOEngines.back(); } @@ -107,58 +128,47 @@ void DSPEngine::removeLastDeviceMIMOEngine() if (m_deviceMIMOEngines.size() > 0) { DSPDeviceMIMOEngine *lastDeviceEngine = m_deviceMIMOEngines.back(); + int lastMIMODeviceEngineIndex = m_deviceMIMOEngines.size() - 1; delete lastDeviceEngine; m_deviceMIMOEngines.pop_back(); - m_deviceMIMOEnginesUIDSequence--; + + for (int i = 0; i < m_deviceEngineReferences.size(); i++) + { + if (m_deviceEngineReferences[i].deviceEngineIndex == lastMIMODeviceEngineIndex) + { + m_deviceEngineReferences.removeAt(i); + break; + } + } } } -DSPDeviceSourceEngine *DSPEngine::getDeviceSourceEngineByUID(uint uid) +void DSPEngine::removeDeviceEngineAt(int deviceIndex) { - std::vector::iterator it = m_deviceSourceEngines.begin(); - - while (it != m_deviceSourceEngines.end()) - { - if ((*it)->getUID() == uid) { - return *it; - } - - ++it; + if (deviceIndex >= m_deviceEngineReferences.size()) { + return; } - return nullptr; -} - -DSPDeviceSinkEngine *DSPEngine::getDeviceSinkEngineByUID(uint uid) -{ - std::vector::iterator it = m_deviceSinkEngines.begin(); - - while (it != m_deviceSinkEngines.end()) + if (m_deviceEngineReferences[deviceIndex].deviceEngineTYpe == 0) // source { - if ((*it)->getUID() == uid) { - return *it; - } - - ++it; + DSPDeviceSourceEngine *deviceEngine = m_deviceSourceEngines[m_deviceEngineReferences[deviceIndex].deviceEngineIndex]; + delete deviceEngine; + m_deviceSourceEngines.removeAt(m_deviceEngineReferences[deviceIndex].deviceEngineIndex); + } + else if (m_deviceEngineReferences[deviceIndex].deviceEngineTYpe == 1) // sink + { + DSPDeviceSinkEngine *deviceEngine = m_deviceSinkEngines[m_deviceEngineReferences[deviceIndex].deviceEngineIndex]; + delete deviceEngine; + m_deviceSinkEngines.removeAt(m_deviceEngineReferences[deviceIndex].deviceEngineIndex); + } + else if (m_deviceEngineReferences[deviceIndex].deviceEngineTYpe == 2) // MIMO + { + DSPDeviceMIMOEngine *deviceEngine = m_deviceMIMOEngines[m_deviceEngineReferences[deviceIndex].deviceEngineIndex]; + delete deviceEngine; + m_deviceMIMOEngines.removeAt(m_deviceEngineReferences[deviceIndex].deviceEngineIndex); } - return nullptr; -} - -DSPDeviceMIMOEngine *DSPEngine::getDeviceMIMOEngineByUID(uint uid) -{ - std::vector::iterator it = m_deviceMIMOEngines.begin(); - - while (it != m_deviceMIMOEngines.end()) - { - if ((*it)->getUID() == uid) { - return *it; - } - - ++it; - } - - return nullptr; + m_deviceEngineReferences.removeAt(deviceIndex); } bool DSPEngine::hasDVSerialSupport() @@ -200,4 +210,4 @@ void DSPEngine::createFFTFactory(const QString& fftWisdomFileName) void DSPEngine::preAllocateFFTs() { m_fftFactory->preallocate(7, 10, 1, 0); // pre-acllocate forward FFT only 1 per size from 128 to 1024 -} \ No newline at end of file +} diff --git a/sdrbase/dsp/dspengine.h b/sdrbase/dsp/dspengine.h index 832b68da2..ab8fd1fe7 100644 --- a/sdrbase/dsp/dspengine.h +++ b/sdrbase/dsp/dspengine.h @@ -52,20 +52,19 @@ public: DSPDeviceMIMOEngine *addDeviceMIMOEngine(); void removeLastDeviceMIMOEngine(); + void removeDeviceEngineAt(int deviceIndex); + AudioDeviceManager *getAudioDeviceManager() { return &m_audioDeviceManager; } AMBEEngine *getAMBEEngine() { return &m_ambeEngine; } uint32_t getDeviceSourceEnginesNumber() const { return m_deviceSourceEngines.size(); } - DSPDeviceSourceEngine *getDeviceSourceEngineByIndex(uint deviceIndex) { return m_deviceSourceEngines[deviceIndex]; } - DSPDeviceSourceEngine *getDeviceSourceEngineByUID(uint uid); + DSPDeviceSourceEngine *getDeviceSourceEngineByIndex(unsigned int deviceIndex) { return m_deviceSourceEngines[deviceIndex]; } uint32_t getDeviceSinkEnginesNumber() const { return m_deviceSinkEngines.size(); } - DSPDeviceSinkEngine *getDeviceSinkEngineByIndex(uint deviceIndex) { return m_deviceSinkEngines[deviceIndex]; } - DSPDeviceSinkEngine *getDeviceSinkEngineByUID(uint uid); + DSPDeviceSinkEngine *getDeviceSinkEngineByIndex(unsigned int deviceIndex) { return m_deviceSinkEngines[deviceIndex]; } uint32_t getDeviceMIMOEnginesNumber() const { return m_deviceMIMOEngines.size(); } - DSPDeviceMIMOEngine *getDeviceMIMOEngineByIndex(uint deviceIndex) { return m_deviceMIMOEngines[deviceIndex]; } - DSPDeviceMIMOEngine *getDeviceMIMOEngineByUID(uint uid); + DSPDeviceMIMOEngine *getDeviceMIMOEngineByIndex(unsigned int deviceIndex) { return m_deviceMIMOEngines[deviceIndex]; } // Serial DV methods: @@ -89,12 +88,19 @@ public: FFTFactory *getFFTFactory() { return m_fftFactory; } private: - std::vector m_deviceSourceEngines; - uint m_deviceSourceEnginesUIDSequence; - std::vector m_deviceSinkEngines; - uint m_deviceSinkEnginesUIDSequence; - std::vector m_deviceMIMOEngines; - uint m_deviceMIMOEnginesUIDSequence; + struct DeviceEngineReference + { + int deviceEngineTYpe; //!< 0: Rx, 1: Tx, 2: MIMO + int deviceEngineIndex; + }; + + QList m_deviceSourceEngines; + unsigned int m_deviceSourceEnginesUIDSequence; + QList m_deviceSinkEngines; + unsigned int m_deviceSinkEnginesUIDSequence; + QList m_deviceMIMOEngines; + unsigned int m_deviceMIMOEnginesUIDSequence; + QList m_deviceEngineReferences; AudioDeviceManager m_audioDeviceManager; int m_audioInputDeviceIndex; int m_audioOutputDeviceIndex; diff --git a/sdrbase/maincore.cpp b/sdrbase/maincore.cpp index f1a7f99a5..f504fdbba 100644 --- a/sdrbase/maincore.cpp +++ b/sdrbase/maincore.cpp @@ -24,6 +24,7 @@ #include "feature/featureset.h" #include "feature/feature.h" #include "device/deviceset.h" +#include "device/deviceapi.h" #include "channel/channelapi.h" #include "maincore.h" @@ -197,6 +198,24 @@ void MainCore::removeLastDeviceSet() } } +void MainCore::removeDeviceSet(int deviceSetIndex) +{ + if (deviceSetIndex < (int) m_deviceSets.size()) + { + DeviceSet *deviceSet = m_deviceSets[deviceSetIndex]; + m_deviceSetsMap.remove(deviceSet); + m_deviceSets.erase(m_deviceSets.begin() + deviceSetIndex); + delete deviceSet; + } + + // Renumerate + for (int i = 0; i < (int) m_deviceSets.size(); i++) + { + m_deviceSets[i]->m_deviceAPI->setDeviceSetIndex(i); + m_deviceSets[i]->setIndex(i); + } +} + void MainCore::addChannelInstance(DeviceSet *deviceSet, ChannelAPI *channelAPI) { m_channelsMap.insert(channelAPI, deviceSet); diff --git a/sdrbase/maincore.h b/sdrbase/maincore.h index 1ed670e70..0c1222dd0 100644 --- a/sdrbase/maincore.h +++ b/sdrbase/maincore.h @@ -725,6 +725,7 @@ public: void removeLastFeatureSet(); void appendDeviceSet(int deviceType); void removeLastDeviceSet(); + void removeDeviceSet(int deviceSetIndex); // slave mode - channels void addChannelInstance(DeviceSet *deviceSet, ChannelAPI *channelAPI); void removeChannelInstanceAt(DeviceSet *deviceSet, int channelIndex); diff --git a/sdrbase/settings/configuration.h b/sdrbase/settings/configuration.h index d08d51371..f99e6ed44 100644 --- a/sdrbase/settings/configuration.h +++ b/sdrbase/settings/configuration.h @@ -54,6 +54,7 @@ public: const FeatureSetPreset& getFeatureSetPreset() const { return m_featureSetPreset; } QList& getDeviceSetPresets() { return m_deviceSetPresets; } const QList& getDeviceSetPresets() const { return m_deviceSetPresets; } + int getNumberOfDeviceSetPresets() const { return m_deviceSetPresets.size(); } void clearData(); static bool configCompare(const Configuration *p1, Configuration *p2) diff --git a/sdrgui/channel/channelgui.cpp b/sdrgui/channel/channelgui.cpp index 96088e361..78673e2b1 100644 --- a/sdrgui/channel/channelgui.cpp +++ b/sdrgui/channel/channelgui.cpp @@ -16,12 +16,314 @@ /////////////////////////////////////////////////////////////////////////////////// #include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mainwindow.h" +#include "gui/workspaceselectiondialog.h" #include "channelgui.h" +ChannelGUI::ChannelGUI(QWidget *parent) : + QMdiSubWindow(parent), + m_deviceType(DeviceRx), + m_deviceSetIndex(0), + m_channelIndex(0), + m_contextMenuType(ContextMenuNone), + m_drag(false) +{ + qDebug("ChannelGUI::ChannelGUI"); + setWindowFlags(windowFlags() | Qt::FramelessWindowHint); + + m_indexLabel = new QLabel(); + m_indexLabel->setFixedSize(50, 16); + m_indexLabel->setStyleSheet("QLabel { background-color: rgb(128, 128, 128); qproperty-alignment: AlignCenter; }"); + m_indexLabel->setText(tr("X%1:%2").arg(m_deviceSetIndex).arg(m_channelIndex)); + m_indexLabel->setToolTip("Channel index"); + + m_settingsButton = new QPushButton(); + QIcon settingsIcon(":/gear.png"); + m_settingsButton->setIcon(settingsIcon); + m_settingsButton->setToolTip("Common settings"); + + m_titleLabel = new QLabel(); + m_titleLabel->setText("Channel"); + m_titleLabel->setToolTip("Channel name"); + m_titleLabel->setFixedHeight(20); + m_titleLabel->setMinimumWidth(20); + m_titleLabel->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Fixed); + + m_helpButton = new QPushButton(); + m_helpButton->setFixedSize(20, 20); + QIcon helpIcon(":/help.png"); + m_helpButton->setIcon(helpIcon); + m_helpButton->setToolTip("Show channel documentation in browser"); + + m_moveButton = new QPushButton(); + m_moveButton->setFixedSize(20, 20); + QIcon moveIcon(":/exit.png"); + m_moveButton->setIcon(moveIcon); + m_moveButton->setToolTip("Move to workspace"); + + m_shrinkButton = new QPushButton(); + m_shrinkButton->setFixedSize(20, 20); + QIcon shrinkIcon(":/shrink.png"); + m_shrinkButton->setIcon(shrinkIcon); + m_shrinkButton->setToolTip("Adjust window to minimum size"); + + m_hideButton = new QPushButton(); + m_hideButton->setFixedSize(20, 20); + QIcon hideIcon(":/hide.png"); + m_hideButton->setIcon(hideIcon); + m_hideButton->setToolTip("Hide channel"); + + m_closeButton = new QPushButton(); + m_closeButton->setFixedSize(20, 20); + QIcon closeIcon(":/cross.png"); + m_closeButton->setIcon(closeIcon); + m_closeButton->setToolTip("Close channel"); + + m_statusLabel = new QLabel(); + // m_statusLabel->setText("OK"); // for future use + m_statusLabel->setFixedHeight(20); + m_statusLabel->setMinimumWidth(20); + m_statusLabel->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Fixed); + m_statusLabel->setToolTip("Channel status"); + + m_layouts = new QVBoxLayout(); + m_layouts->setContentsMargins(0, 4, 0, 4); + m_layouts->setSpacing(2); + + m_topLayout = new QHBoxLayout(); + m_topLayout->setContentsMargins(0, 0, 0, 0); + m_topLayout->addWidget(m_indexLabel); + m_topLayout->addWidget(m_settingsButton); + m_topLayout->addWidget(m_titleLabel); + // m_topLayout->addStretch(1); + m_topLayout->addWidget(m_helpButton); + m_topLayout->addWidget(m_moveButton); + m_topLayout->addWidget(m_shrinkButton); + m_topLayout->addWidget(m_hideButton); + m_topLayout->addWidget(m_closeButton); + m_sizeGripTopRight = new QSizeGrip(this); + m_sizeGripTopRight->setStyleSheet("QSizeGrip { background-color: rgb(128, 128, 128); width: 10px; height: 10px; }"); + m_sizeGripTopRight->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + m_topLayout->addWidget(m_sizeGripTopRight, 0, Qt::AlignTop | Qt::AlignRight); + + m_centerLayout = new QHBoxLayout(); + m_centerLayout->addWidget(&m_rollupContents); + + m_bottomLayout = new QHBoxLayout(); + m_bottomLayout->setContentsMargins(0, 0, 0, 0); + m_bottomLayout->addWidget(m_statusLabel); + m_sizeGripBottomRight = new QSizeGrip(this); + m_sizeGripBottomRight->setStyleSheet("QSizeGrip { background-color: rgb(128, 128, 128); width: 10px; height: 10px; }"); + m_sizeGripBottomRight->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + // m_bottomLayout->addStretch(1); + m_bottomLayout->addWidget(m_sizeGripBottomRight, 0, Qt::AlignBottom | Qt::AlignRight); + + m_layouts->addLayout(m_topLayout); + m_layouts->addLayout(m_centerLayout); + m_layouts->addLayout(m_bottomLayout); + + QObjectCleanupHandler().add(layout()); + setLayout(m_layouts); + + connect(m_settingsButton, SIGNAL(clicked()), this, SLOT(activateSettingsDialog())); + connect(m_helpButton, SIGNAL(clicked()), this, SLOT(showHelp())); + connect(m_moveButton, SIGNAL(clicked()), this, SLOT(openMoveToWorkspaceDialog())); + connect(m_shrinkButton, SIGNAL(clicked()), this, SLOT(shrinkWindow())); + connect(this, SIGNAL(forceShrink()), this, SLOT(shrinkWindow())); + connect(m_hideButton, SIGNAL(clicked()), this, SLOT(hide())); + connect(m_closeButton, SIGNAL(clicked()), this, SLOT(close())); + + connect( + &m_rollupContents, + &RollupContents::widgetRolled, + this, + &ChannelGUI::onWidgetRolled + ); +} + +ChannelGUI::~ChannelGUI() +{ + qDebug("ChannelGUI::~ChannelGUI"); + delete m_sizeGripBottomRight; + delete m_bottomLayout; + delete m_centerLayout; + delete m_sizeGripTopRight; + delete m_topLayout; + delete m_layouts; + delete m_statusLabel; + delete m_closeButton; + delete m_hideButton; + delete m_shrinkButton; + delete m_moveButton; + delete m_helpButton; + delete m_titleLabel; + delete m_settingsButton; + delete m_indexLabel; + qDebug("ChannelGUI::~ChannelGUI: end"); +} + void ChannelGUI::closeEvent(QCloseEvent *event) { qDebug("ChannelGUI::closeEvent"); emit closing(); event->accept(); } + +void ChannelGUI::mousePressEvent(QMouseEvent* event) +{ + if ((event->button() == Qt::LeftButton) && isOnMovingPad()) + { + m_drag = true; + m_DragPosition = event->globalPos() - pos(); + event->accept(); + } +} + +void ChannelGUI::mouseMoveEvent(QMouseEvent* event) +{ + if ((event->buttons() & Qt::LeftButton) && isOnMovingPad()) + { + move(event->globalPos() - m_DragPosition); + event->accept(); + } +} + +void ChannelGUI::setStreamIndicator(const QString& indicator) +{ + (void) indicator; // TODO +} + +void ChannelGUI::activateSettingsDialog() +{ + QPoint p = mapFromGlobal(QCursor::pos()); + m_contextMenuType = ContextMenuChannelSettings; + emit customContextMenuRequested(p); +} + +void ChannelGUI::showHelp() +{ + if (m_helpURL.isEmpty()) { + return; + } + + QString url; + + if (m_helpURL.startsWith("http")) { + url = m_helpURL; + } else { + url = QString("https://github.com/f4exb/sdrangel/blob/master/%1").arg(m_helpURL); // Something like "plugins/channelrx/chanalyzer/readme.md" + } + + QDesktopServices::openUrl(QUrl(url)); +} + +void ChannelGUI::openMoveToWorkspaceDialog() +{ + int numberOfWorkspaces = MainWindow::getInstance()->getNumberOfWorkspaces(); + WorkspaceSelectionDialog dialog(numberOfWorkspaces, this); + dialog.exec(); + + if (dialog.hasChanged()) { + emit moveToWorkspace(dialog.getSelectedIndex()); + } +} + +void ChannelGUI::onWidgetRolled(QWidget *widget, bool show) +{ + if (show) + { + // qDebug("ChannelGUI::onWidgetRolled: show: %d %d", m_rollupContents.height(), widget->height()); + int dh = m_heightsMap.contains(widget) ? m_heightsMap[widget] - widget->height() : widget->minimumHeight(); + resize(width(), 52 + m_rollupContents.height() + dh); + } + else + { + // qDebug("ChannelGUI::onWidgetRolled: hide: %d %d", m_rollupContents.height(), widget->height()); + m_heightsMap[widget] = widget->height(); + resize(width(), 52 + m_rollupContents.height()); + } +} + +void ChannelGUI::shrinkWindow() +{ + qDebug("ChannelGUI::shrinkWindow"); + adjustSize(); +} + +void ChannelGUI::setTitle(const QString& title) +{ + m_titleLabel->setText(title); +} + +void ChannelGUI::setTitleColor(const QColor& c) +{ + m_indexLabel->setStyleSheet(tr("QLabel { background-color: %1; color: %2; }") + .arg(c.name()) + .arg(getTitleColor(c).name()) + ); +} + +void ChannelGUI::setDeviceType(DeviceType type) +{ + m_deviceType = type; + updateIndexLabel(); +} + +void ChannelGUI::setToolTip(const QString& tooltip) +{ + m_indexLabel->setToolTip(tooltip); +} + +void ChannelGUI::setIndex(int index) +{ + m_channelIndex = index; + updateIndexLabel(); +} + +void ChannelGUI::setDeviceSetIndex(int index) +{ + m_deviceSetIndex = index; + updateIndexLabel(); +} + +void ChannelGUI::updateIndexLabel() +{ + m_indexLabel->setText(tr("%1%2:%3").arg(getDeviceTypeTag()).arg(m_deviceSetIndex).arg(m_channelIndex)); +} + +bool ChannelGUI::isOnMovingPad() +{ + return m_indexLabel->underMouse() || m_titleLabel->underMouse() || m_statusLabel->underMouse(); +} + +QString ChannelGUI::getDeviceTypeTag() +{ + switch (m_deviceType) + { + case DeviceRx: + return "R"; + case DeviceTx: + return "T"; + case DeviceMIMO: + return "M"; + default: + return "X"; + } +} + +QColor ChannelGUI::getTitleColor(const QColor& backgroundColor) +{ + float l = 0.2126*backgroundColor.redF() + 0.7152*backgroundColor.greenF() + 0.0722*backgroundColor.blueF(); + return l < 0.5f ? Qt::white : Qt::black; +} diff --git a/sdrgui/channel/channelgui.h b/sdrgui/channel/channelgui.h index 4bb420002..4b8424314 100644 --- a/sdrgui/channel/channelgui.h +++ b/sdrgui/channel/channelgui.h @@ -1,5 +1,5 @@ /////////////////////////////////////////////////////////////////////////////////// -// Copyright (C) 2020 Edouard Griffiths, F4EXB // +// Copyright (C) 2020-2022 Edouard Griffiths, F4EXB // // // // This program is free software; you can redistribute it and/or modify // // it under the terms of the GNU General Public License as published by // @@ -18,33 +18,118 @@ #ifndef SDRGUI_CHANNEL_CHANNELGUI_H_ #define SDRGUI_CHANNEL_CHANNELGUI_H_ -#include "gui/rollupwidget.h" +#include +#include + +#include "gui/rollupcontents.h" #include "export.h" class QCloseEvent; class MessageQueue; +class QLabel; +class QPushButton; +class QVBoxLayout; +class QHBoxLayout; +class QSizeGrip; -class SDRGUI_API ChannelGUI : public RollupWidget +class SDRGUI_API ChannelGUI : public QMdiSubWindow { Q_OBJECT public: - ChannelGUI(QWidget *parent = nullptr) : - RollupWidget(parent) - { } - virtual ~ChannelGUI() { } + enum DeviceType + { + DeviceRx, + DeviceTx, + DeviceMIMO + }; + + enum ContextMenuType + { + ContextMenuNone, + ContextMenuChannelSettings, + ContextMenuStreamSettings + }; + + ChannelGUI(QWidget *parent = nullptr); + virtual ~ChannelGUI(); virtual void destroy() = 0; virtual void resetToDefaults() = 0; virtual QByteArray serialize() const = 0; virtual bool deserialize(const QByteArray& data) = 0; + // Data saved in the derived settings + virtual void setWorkspaceIndex(int index)= 0; + virtual int getWorkspaceIndex() const = 0; + virtual void setGeometryBytes(const QByteArray& blob) = 0; + virtual QByteArray getGeometryBytes() const = 0; + virtual QString getTitle() const = 0; + virtual QColor getTitleColor() const = 0; virtual MessageQueue* getInputMessageQueue() = 0; + RollupContents *getRollupContents() { return &m_rollupContents; } + void setTitle(const QString& title); + void setTitleColor(const QColor& c); + void setDeviceType(DeviceType type); + DeviceType getDeviceType() const { return m_deviceType; } + void setToolTip(const QString& tooltip); + void setIndex(int index); + int getIndex() const { return m_channelIndex; } + void setDeviceSetIndex(int index); + int getDeviceSetIndex() const { return m_channelIndex; } + protected: void closeEvent(QCloseEvent *event); + void mousePressEvent(QMouseEvent* event); + void mouseMoveEvent(QMouseEvent* event); + void resetContextMenuType() { m_contextMenuType = ContextMenuNone; } + void setStreamIndicator(const QString& indicator); // TODO + + DeviceType m_deviceType; + int m_deviceSetIndex; + int m_channelIndex; + QString m_helpURL; + RollupContents m_rollupContents; + ContextMenuType m_contextMenuType; + +protected slots: + void shrinkWindow(); + +private: + void updateIndexLabel(); + bool isOnMovingPad(); + QString getDeviceTypeTag(); + static QColor getTitleColor(const QColor& backgroundColor); + + QLabel *m_indexLabel; + QPushButton *m_settingsButton; + QLabel *m_titleLabel; + QPushButton *m_helpButton; + QPushButton *m_moveButton; + QPushButton *m_shrinkButton; + QPushButton *m_hideButton; + QPushButton *m_closeButton; + QLabel *m_statusLabel; + QVBoxLayout *m_layouts; + QHBoxLayout *m_topLayout; + QHBoxLayout *m_centerLayout; + QHBoxLayout *m_bottomLayout; + QSizeGrip *m_sizeGripTopRight; + QSizeGrip *m_sizeGripBottomRight; + bool m_drag; + QPoint m_DragPosition; + QMap m_heightsMap; + +private slots: + void activateSettingsDialog(); + void showHelp(); + void openMoveToWorkspaceDialog(); + void onWidgetRolled(QWidget *widget, bool show); signals: void closing(); + void moveToWorkspace(int workspaceIndex); + void forceShrink(); }; #endif // SDRGUI_CHANNEL_CHANNELGUI_H_ diff --git a/sdrgui/device/devicegui.cpp b/sdrgui/device/devicegui.cpp index bd76017cf..6778c168c 100644 --- a/sdrgui/device/devicegui.cpp +++ b/sdrgui/device/devicegui.cpp @@ -168,6 +168,7 @@ DeviceGUI::DeviceGUI(QWidget *parent) : connect(m_changeDeviceButton, SIGNAL(clicked()), this, SLOT(openChangeDeviceDialog())); connect(m_reloadDeviceButton, SIGNAL(clicked()), this, SLOT(deviceReload())); + connect(m_addChannelsButton, SIGNAL(clicked()), this, SLOT(openAddChannelsDialog())); connect(m_helpButton, SIGNAL(clicked()), this, SLOT(showHelp())); connect(m_moveButton, SIGNAL(clicked()), this, SLOT(openMoveToWorkspaceDialog())); connect(m_shrinkButton, SIGNAL(clicked()), this, SLOT(shrinkWindow())); @@ -175,6 +176,14 @@ DeviceGUI::DeviceGUI(QWidget *parent) : connect(m_closeButton, SIGNAL(clicked()), this, SLOT(close())); connect(this, SIGNAL(forceClose()), this, SLOT(close())); connect(m_showSpectrumButton, SIGNAL(clicked()), this, SLOT(showSpectrumHandler())); + connect(m_showAllChannelsButton, SIGNAL(clicked()), this, SLOT(showAllChannelsHandler())); + + QObject::connect( + &m_channelAddDialog, + &ChannelAddDialog::addChannel, + this, + &DeviceGUI::addChannelEmitted + ); } DeviceGUI::~DeviceGUI() @@ -275,11 +284,21 @@ void DeviceGUI::openMoveToWorkspaceDialog() } } +void DeviceGUI::openAddChannelsDialog() +{ + m_channelAddDialog.exec(); +} + void DeviceGUI::showSpectrumHandler() { emit showSpectrum(m_deviceSetIndex); } +void DeviceGUI::showAllChannelsHandler() +{ + emit showAllChannels(m_deviceSetIndex); +} + void DeviceGUI::shrinkWindow() { qDebug("DeviceGUI::shrinkWindow"); diff --git a/sdrgui/device/devicegui.h b/sdrgui/device/devicegui.h index 5ba14ccad..61c36f8d9 100644 --- a/sdrgui/device/devicegui.h +++ b/sdrgui/device/devicegui.h @@ -25,6 +25,7 @@ #include #include +#include "gui/channeladddialog.h" #include "export.h" class QCloseEvent; @@ -76,6 +77,7 @@ public: void setIndex(int index); int getIndex() const { return m_deviceSetIndex; } void setCurrentDeviceIndex(int index) { m_currentDeviceIndex = index; } //!< index in plugins list + void setChannelNames(const QStringList& channelNames) { m_channelAddDialog.addChannelNames(channelNames); } protected: void closeEvent(QCloseEvent *event); @@ -119,13 +121,16 @@ private: bool m_drag; QPoint m_DragPosition; int m_currentDeviceIndex; //!< Index in device plugins registrations + ChannelAddDialog m_channelAddDialog; private slots: void openChangeDeviceDialog(); + void openAddChannelsDialog(); void deviceReload(); void showHelp(); void openMoveToWorkspaceDialog(); void showSpectrumHandler(); + void showAllChannelsHandler(); signals: void forceClose(); @@ -135,6 +140,8 @@ signals: void deviceAdd(int deviceType, int deviceIndex); void deviceChange(int newDeviceIndex); void showSpectrum(int deviceSetIndex); + void showAllChannels(int deviceSetIndex); + void addChannelEmitted(int channelIndex); }; #endif // INCLUDE_DEVICEGUI_H diff --git a/sdrgui/device/deviceuiset.cpp b/sdrgui/device/deviceuiset.cpp index 2a8bff4f9..74f813e4a 100644 --- a/sdrgui/device/deviceuiset.cpp +++ b/sdrgui/device/deviceuiset.cpp @@ -36,6 +36,7 @@ #include "channel/channelgui.h" #include "mainspectrum/mainspectrumgui.h" #include "settings/preset.h" +#include "mainwindow.h" #include "deviceuiset.h" @@ -74,6 +75,18 @@ DeviceUISet::~DeviceUISet() // delete m_spectrum; } +void DeviceUISet::setIndex(int deviceSetIndex) +{ + m_deviceGUI->setIndex(deviceSetIndex); + m_mainSpectrumGUI->setIndex(deviceSetIndex); + + for (auto& channelRegistation : m_channelInstanceRegistrations) { + channelRegistation.m_gui->setDeviceSetIndex(deviceSetIndex); + } + + m_deviceSetIndex = deviceSetIndex; +} + void DeviceUISet::setSpectrumScalingFactor(float scalef) { m_spectrumVis->setScalef(scalef); @@ -161,6 +174,11 @@ ChannelAPI *DeviceUISet::getChannelAt(int channelIndex) return m_deviceSet->getChannelAt(channelIndex); } +ChannelGUI *DeviceUISet::getChannelGUIAt(int channelIndex) +{ + return m_channelInstanceRegistrations[channelIndex].m_gui; +} + void DeviceUISet::loadDeviceSetSettings( const Preset* preset, PluginAPI *pluginAPI, @@ -168,9 +186,6 @@ void DeviceUISet::loadDeviceSetSettings( Workspace *currentWorkspace ) { - (void) workspaces; // TODO: use for channels - (void) currentWorkspace; // TODO: use for channels - m_spectrumGUI->deserialize(preset->getSpectrumConfig()); m_deviceAPI->loadSamplingDeviceSettings(preset); @@ -179,11 +194,11 @@ void DeviceUISet::loadDeviceSetSettings( } if (m_deviceSourceEngine) { // source device - loadRxChannelSettings(preset, pluginAPI); + loadRxChannelSettings(preset, pluginAPI, workspaces, currentWorkspace); } else if (m_deviceSinkEngine) { // sink device - loadTxChannelSettings(preset, pluginAPI); + loadTxChannelSettings(preset, pluginAPI, workspaces, currentWorkspace); } else if (m_deviceMIMOEngine) { // MIMO device - loadMIMOChannelSettings(preset, pluginAPI); + loadMIMOChannelSettings(preset, pluginAPI, workspaces, currentWorkspace); } } @@ -220,7 +235,7 @@ void DeviceUISet::saveDeviceSetSettings(Preset* preset) const m_deviceAPI->saveSamplingDeviceSettings(preset); } -void DeviceUISet::loadRxChannelSettings(const Preset *preset, PluginAPI *pluginAPI) +void DeviceUISet::loadRxChannelSettings(const Preset *preset, PluginAPI *pluginAPI, QList *workspaces, Workspace *currentWorkspace) { if (preset->isSourcePreset()) { @@ -248,6 +263,7 @@ void DeviceUISet::loadRxChannelSettings(const Preset *preset, PluginAPI *pluginA { const Preset::ChannelConfig& channelConfig = preset->getChannelConfig(i); ChannelGUI *rxChannelGUI = nullptr; + ChannelAPI *channelAPI = nullptr; // create channel instance @@ -259,7 +275,6 @@ void DeviceUISet::loadRxChannelSettings(const Preset *preset, PluginAPI *pluginA qDebug("DeviceUISet::loadRxChannelSettings: creating new channel [%s] from config [%s]", qPrintable((*channelRegistrations)[i].m_channelIdURI), qPrintable(channelConfig.m_channelIdURI)); - ChannelAPI *channelAPI; BasebandSampleSink *rxChannel; (*channelRegistrations)[i].m_plugin->createRxChannel(m_deviceAPI, &rxChannel, &channelAPI); rxChannelGUI = (*channelRegistrations)[i].m_plugin->createRxChannelGUI(this, rxChannel); @@ -275,10 +290,34 @@ void DeviceUISet::loadRxChannelSettings(const Preset *preset, PluginAPI *pluginA } } - if (rxChannelGUI) + if (rxChannelGUI && channelAPI) { qDebug("DeviceUISet::loadRxChannelSettings: deserializing channel [%s]", qPrintable(channelConfig.m_channelIdURI)); rxChannelGUI->deserialize(channelConfig.m_config); + int originalWorkspaceIndex = rxChannelGUI->getWorkspaceIndex(); + + if (workspaces && (workspaces->size() > 0) && (originalWorkspaceIndex < workspaces->size())) // restore in original workspace + { + (*workspaces)[originalWorkspaceIndex]->addToMdiArea((QMdiSubWindow*) rxChannelGUI); + } + else if (currentWorkspace) // restore in current workspace + { + rxChannelGUI->setWorkspaceIndex(currentWorkspace->getIndex()); + currentWorkspace->addToMdiArea((QMdiSubWindow*) rxChannelGUI); + } + + rxChannelGUI->restoreGeometry(rxChannelGUI->getGeometryBytes()); + rxChannelGUI->setDeviceType(ChannelGUI::DeviceRx); + rxChannelGUI->setDeviceSetIndex(m_deviceSetIndex); + rxChannelGUI->setIndex(channelAPI->getIndexInDeviceSet()); + rxChannelGUI->setToolTip(m_deviceAPI->getSamplingDeviceDisplayName()); + + QObject::connect( + rxChannelGUI, + &ChannelGUI::moveToWorkspace, + this, + [=](int wsIndexDest){ MainWindow::getInstance()->channelMove(rxChannelGUI, wsIndexDest); } + ); } } } @@ -292,10 +331,12 @@ void DeviceUISet::saveRxChannelSettings(Preset *preset) const { if (preset->isSourcePreset()) { - for(int i = 0; i < m_channelInstanceRegistrations.count(); i++) + for (int i = 0; i < m_channelInstanceRegistrations.count(); i++) { + ChannelGUI *channelGUI = m_channelInstanceRegistrations[i].m_gui; qDebug("DeviceUISet::saveRxChannelSettings: saving channel [%s]", qPrintable(m_channelInstanceRegistrations[i].m_channelAPI->getURI())); - preset->addChannel(m_channelInstanceRegistrations[i].m_channelAPI->getURI(), m_channelInstanceRegistrations[i].m_gui->serialize()); + channelGUI->setGeometryBytes(channelGUI->saveGeometry()); + preset->addChannel(m_channelInstanceRegistrations[i].m_channelAPI->getURI(), channelGUI->serialize()); } } else @@ -304,7 +345,7 @@ void DeviceUISet::saveRxChannelSettings(Preset *preset) const } } -void DeviceUISet::loadTxChannelSettings(const Preset *preset, PluginAPI *pluginAPI) +void DeviceUISet::loadTxChannelSettings(const Preset *preset, PluginAPI *pluginAPI, QList *workspaces, Workspace *currentWorkspace) { if (preset->isSinkPreset()) { @@ -332,6 +373,7 @@ void DeviceUISet::loadTxChannelSettings(const Preset *preset, PluginAPI *pluginA { const Preset::ChannelConfig& channelConfig = preset->getChannelConfig(i); ChannelGUI *txChannelGUI = nullptr; + ChannelAPI *channelAPI = nullptr; // create channel instance @@ -342,7 +384,6 @@ void DeviceUISet::loadTxChannelSettings(const Preset *preset, PluginAPI *pluginA qDebug("DeviceUISet::loadTxChannelSettings: creating new channel [%s] from config [%s]", qPrintable((*channelRegistrations)[i].m_channelIdURI), qPrintable(channelConfig.m_channelIdURI)); - ChannelAPI *channelAPI; BasebandSampleSource *txChannel; (*channelRegistrations)[i].m_plugin->createTxChannel(m_deviceAPI, &txChannel, &channelAPI); txChannelGUI = (*channelRegistrations)[i].m_plugin->createTxChannelGUI(this, txChannel); @@ -358,10 +399,34 @@ void DeviceUISet::loadTxChannelSettings(const Preset *preset, PluginAPI *pluginA } } - if(txChannelGUI) + if (txChannelGUI && channelAPI) { qDebug("DeviceUISet::loadTxChannelSettings: deserializing channel [%s]", qPrintable(channelConfig.m_channelIdURI)); txChannelGUI->deserialize(channelConfig.m_config); + int originalWorkspaceIndex = txChannelGUI->getWorkspaceIndex(); + + if (workspaces && (workspaces->size() > 0) && (originalWorkspaceIndex < workspaces->size())) // restore in original workspace + { + (*workspaces)[originalWorkspaceIndex]->addToMdiArea((QMdiSubWindow*) txChannelGUI); + } + else if (currentWorkspace) // restore in current workspace + { + txChannelGUI->setWorkspaceIndex(currentWorkspace->getIndex()); + currentWorkspace->addToMdiArea((QMdiSubWindow*) txChannelGUI); + } + + txChannelGUI->restoreGeometry(txChannelGUI->getGeometryBytes()); + txChannelGUI->setDeviceType(ChannelGUI::DeviceRx); + txChannelGUI->setDeviceSetIndex(m_deviceSetIndex); + txChannelGUI->setIndex(channelAPI->getIndexInDeviceSet()); + txChannelGUI->setToolTip(m_deviceAPI->getSamplingDeviceDisplayName()); + + QObject::connect( + txChannelGUI, + &ChannelGUI::moveToWorkspace, + this, + [=](int wsIndexDest){ MainWindow::getInstance()->channelMove(txChannelGUI, wsIndexDest); } + ); } } } @@ -376,10 +441,12 @@ void DeviceUISet::saveTxChannelSettings(Preset *preset) const { if (preset->isSinkPreset()) { - for(int i = 0; i < m_channelInstanceRegistrations.count(); i++) + for (int i = 0; i < m_channelInstanceRegistrations.count(); i++) { + ChannelGUI *channelGUI = m_channelInstanceRegistrations[i].m_gui; qDebug("DeviceUISet::saveTxChannelSettings: saving channel [%s]", qPrintable(m_channelInstanceRegistrations[i].m_channelAPI->getURI())); - preset->addChannel(m_channelInstanceRegistrations[i].m_channelAPI->getURI(), m_channelInstanceRegistrations[i].m_gui->serialize()); + channelGUI->setGeometryBytes(channelGUI->saveGeometry()); + preset->addChannel(m_channelInstanceRegistrations[i].m_channelAPI->getURI(), channelGUI->serialize()); } } else @@ -388,7 +455,7 @@ void DeviceUISet::saveTxChannelSettings(Preset *preset) const } } -void DeviceUISet::loadMIMOChannelSettings(const Preset *preset, PluginAPI *pluginAPI) +void DeviceUISet::loadMIMOChannelSettings(const Preset *preset, PluginAPI *pluginAPI, QList *workspaces, Workspace *currentWorkspace) { if (preset->isMIMOPreset()) { @@ -415,6 +482,7 @@ void DeviceUISet::loadMIMOChannelSettings(const Preset *preset, PluginAPI *plugi { const Preset::ChannelConfig& channelConfig = preset->getChannelConfig(i); ChannelGUI *mimoChannelGUI = nullptr; + ChannelAPI *channelAPI = nullptr; // create channel instance @@ -426,7 +494,6 @@ void DeviceUISet::loadMIMOChannelSettings(const Preset *preset, PluginAPI *plugi qDebug("DeviceUISet::loadMIMOChannelSettings: creating new channel [%s] from config [%s]", qPrintable((*channelRegistrations)[i].m_channelIdURI), qPrintable(channelConfig.m_channelIdURI)); - ChannelAPI *channelAPI; MIMOChannel *mimoChannel; (*channelRegistrations)[i].m_plugin->createMIMOChannel(m_deviceAPI, &mimoChannel, &channelAPI); mimoChannelGUI = (*channelRegistrations)[i].m_plugin->createMIMOChannelGUI(this, mimoChannel); @@ -443,10 +510,34 @@ void DeviceUISet::loadMIMOChannelSettings(const Preset *preset, PluginAPI *plugi } } - if (mimoChannelGUI) + if (mimoChannelGUI && channelAPI) { qDebug("DeviceUISet::loadMIMOChannelSettings: deserializing channel [%s]", qPrintable(channelConfig.m_channelIdURI)); mimoChannelGUI->deserialize(channelConfig.m_config); + int originalWorkspaceIndex = mimoChannelGUI->getWorkspaceIndex(); + + if (workspaces && (workspaces->size() > 0) && (originalWorkspaceIndex < workspaces->size())) // restore in original workspace + { + (*workspaces)[originalWorkspaceIndex]->addToMdiArea((QMdiSubWindow*) mimoChannelGUI); + } + else if (currentWorkspace) // restore in current workspace + { + mimoChannelGUI->setWorkspaceIndex(currentWorkspace->getIndex()); + currentWorkspace->addToMdiArea((QMdiSubWindow*) mimoChannelGUI); + } + + mimoChannelGUI->restoreGeometry(mimoChannelGUI->getGeometryBytes()); + mimoChannelGUI->setDeviceType(ChannelGUI::DeviceRx); + mimoChannelGUI->setDeviceSetIndex(m_deviceSetIndex); + mimoChannelGUI->setIndex(channelAPI->getIndexInDeviceSet()); + mimoChannelGUI->setToolTip(m_deviceAPI->getSamplingDeviceDisplayName()); + + QObject::connect( + mimoChannelGUI, + &ChannelGUI::moveToWorkspace, + this, + [=](int wsIndexDest){ MainWindow::getInstance()->channelMove(mimoChannelGUI, wsIndexDest); } + ); } } } @@ -460,10 +551,12 @@ void DeviceUISet::saveMIMOChannelSettings(Preset *preset) const { if (preset->isMIMOPreset()) { - for(int i = 0; i < m_channelInstanceRegistrations.count(); i++) + for (int i = 0; i < m_channelInstanceRegistrations.count(); i++) { + ChannelGUI *channelGUI = m_channelInstanceRegistrations[i].m_gui; qDebug("DeviceUISet::saveMIMOChannelSettings: saving channel [%s]", qPrintable(m_channelInstanceRegistrations[i].m_channelAPI->getURI())); - preset->addChannel(m_channelInstanceRegistrations[i].m_channelAPI->getURI(), m_channelInstanceRegistrations[i].m_gui->serialize()); + channelGUI->setGeometryBytes(channelGUI->saveGeometry()); + preset->addChannel(m_channelInstanceRegistrations[i].m_channelAPI->getURI(), channelGUI->serialize()); } } else @@ -494,6 +587,8 @@ bool DeviceUISet::ChannelInstanceRegistration::operator<(const ChannelInstanceRe void DeviceUISet::handleChannelGUIClosing(ChannelGUI* channelGUI) { + qDebug("DeviceUISet::handleChannelGUIClosing: %s: %d", qPrintable(channelGUI->getTitle()), channelGUI->getIndex()); + for (ChannelInstanceRegistrations::iterator it = m_channelInstanceRegistrations.begin(); it != m_channelInstanceRegistrations.end(); ++it) { if (it->m_gui == channelGUI) @@ -504,6 +599,11 @@ void DeviceUISet::handleChannelGUIClosing(ChannelGUI* channelGUI) break; } } + + // Renumerate + for (int i = 0; i < m_channelInstanceRegistrations.count(); i++) { + m_channelInstanceRegistrations.at(i).m_gui->setIndex(i); + } } int DeviceUISet::webapiSpectrumSettingsGet(SWGSDRangel::SWGGLSpectrum& response, QString& errorMessage) const diff --git a/sdrgui/device/deviceuiset.h b/sdrgui/device/deviceuiset.h index 5d133d4b7..3daf59524 100644 --- a/sdrgui/device/deviceuiset.h +++ b/sdrgui/device/deviceuiset.h @@ -71,6 +71,7 @@ public: DeviceUISet(int deviceSetIndex, DeviceSet *deviceSet); ~DeviceUISet(); + void setIndex(int deviceSetIndex); GLSpectrum *getSpectrum() { return m_spectrum; } //!< Direct spectrum getter void setSpectrumScalingFactor(float scalef); void addChannelMarker(ChannelMarker* channelMarker); //!< Add channel marker to spectrum @@ -80,6 +81,7 @@ public: void freeChannels(); void deleteChannel(int channelIndex); ChannelAPI *getChannelAt(int channelIndex); + ChannelGUI *getChannelGUIAt(int channelIndex); void loadDeviceSetSettings( const Preset* preset, @@ -145,9 +147,9 @@ private: int m_nbAvailableTxChannels; //!< Number of Tx channels available for selection int m_nbAvailableMIMOChannels; //!< Number of MIMO channels available for selection - void loadRxChannelSettings(const Preset* preset, PluginAPI *pluginAPI); - void loadTxChannelSettings(const Preset* preset, PluginAPI *pluginAPI); - void loadMIMOChannelSettings(const Preset* preset, PluginAPI *pluginAPI); + void loadRxChannelSettings(const Preset* preset, PluginAPI *pluginAPI, QList *workspaces, Workspace *currentWorkspace); + void loadTxChannelSettings(const Preset* preset, PluginAPI *pluginAPI, QList *workspaces, Workspace *currentWorkspace); + void loadMIMOChannelSettings(const Preset* preset, PluginAPI *pluginAPI, QList *workspaces, Workspace *currentWorkspace); void saveRxChannelSettings(Preset* preset) const; void saveTxChannelSettings(Preset* preset) const; void saveMIMOChannelSettings(Preset* preset) const; diff --git a/sdrgui/feature/featuregui.cpp b/sdrgui/feature/featuregui.cpp index 248be215b..b9ff810e7 100644 --- a/sdrgui/feature/featuregui.cpp +++ b/sdrgui/feature/featuregui.cpp @@ -132,6 +132,13 @@ FeatureGUI::FeatureGUI(QWidget *parent) : connect(m_shrinkButton, SIGNAL(clicked()), this, SLOT(shrinkWindow())); connect(this, SIGNAL(forceShrink()), this, SLOT(shrinkWindow())); connect(m_closeButton, SIGNAL(clicked()), this, SLOT(close())); + + connect( + &m_rollupContents, + &RollupContents::widgetRolled, + this, + &FeatureGUI::onWidgetRolled + ); } FeatureGUI::~FeatureGUI() @@ -215,6 +222,22 @@ void FeatureGUI::openMoveToWorkspaceDialog() } } +void FeatureGUI::onWidgetRolled(QWidget *widget, bool show) +{ + if (show) + { + // qDebug("FeatureGUI::onWidgetRolled: show: %d %d", m_rollupContents.height(), widget->height()); + int dh = m_heightsMap.contains(widget) ? m_heightsMap[widget] - widget->height() : widget->minimumHeight(); + resize(width(), 52 + m_rollupContents.height() + dh); + } + else + { + // qDebug("FeatureGUI::onWidgetRolled: hide: %d %d", m_rollupContents.height(), widget->height()); + m_heightsMap[widget] = widget->height(); + resize(width(), 52 + m_rollupContents.height()); + } +} + void FeatureGUI::shrinkWindow() { qDebug("FeatureGUI::shrinkWindow"); diff --git a/sdrgui/feature/featuregui.h b/sdrgui/feature/featuregui.h index ce185df40..ca92b4209 100644 --- a/sdrgui/feature/featuregui.h +++ b/sdrgui/feature/featuregui.h @@ -19,6 +19,7 @@ #define SDRGUI_FEATURE_FEATUREGUI_H_ #include +#include #include "gui/rollupcontents.h" #include "export.h" @@ -96,11 +97,13 @@ private: QSizeGrip *m_sizeGripBottomRight; bool m_drag; QPoint m_DragPosition; + QMap m_heightsMap; private slots: void activateSettingsDialog(); void showHelp(); void openMoveToWorkspaceDialog(); + void onWidgetRolled(QWidget *widget, bool show); signals: void closing(); diff --git a/sdrgui/feature/featureuiset.cpp b/sdrgui/feature/featureuiset.cpp index 3bf8c7453..37ab5279c 100644 --- a/sdrgui/feature/featureuiset.cpp +++ b/sdrgui/feature/featureuiset.cpp @@ -92,6 +92,11 @@ void FeatureUISet::deleteFeature(int featureIndex) m_featureInstanceRegistrations.removeAt(featureIndex); m_featureSet->removeFeatureInstanceAt(featureIndex); } + + // Renumerate + for (int i = 0; i < m_featureInstanceRegistrations.count(); i++) { + m_featureInstanceRegistrations.at(i).m_gui->setIndex(i); + } } const Feature *FeatureUISet::getFeatureAt(int featureIndex) const @@ -237,4 +242,9 @@ void FeatureUISet::handleClosingFeatureGUI(FeatureGUI *featureGUI) break; } } + + // Renumerate + for (int i = 0; i < m_featureInstanceRegistrations.count(); i++) { + m_featureInstanceRegistrations.at(i).m_gui->setIndex(i); + } } diff --git a/sdrgui/mainwindow.cpp b/sdrgui/mainwindow.cpp index 7c9a4b462..5570df766 100644 --- a/sdrgui/mainwindow.cpp +++ b/sdrgui/mainwindow.cpp @@ -96,6 +96,7 @@ #include #include #include +#include #if defined(HAS_LIMERFEUSB) #include "limerfegui/limerfeusbdialog.h" @@ -401,6 +402,13 @@ void MainWindow::sampleSourceAdd(Workspace *workspace, int deviceIndex) [=](int wsIndexDest){ this->mainSpectrumMove(m_deviceUIs.back()->m_mainSpectrumGUI, wsIndexDest); } ); + QObject::connect( + m_deviceUIs.back()->m_deviceGUI, + &DeviceGUI::addChannelEmitted, + this, + [=](int channelIndex){ this->channelAddClicked(workspace, deviceSetIndex, channelIndex); } + ); + workspace->addToMdiArea(m_deviceUIs.back()->m_deviceGUI); workspace->addToMdiArea(m_deviceUIs.back()->m_mainSpectrumGUI); emit m_mainCore->deviceSetAdded(deviceSetIndex, deviceAPI); @@ -522,6 +530,19 @@ void MainWindow::sampleSourceCreate( this, [=](int deviceSetIndex){ this->mainSpectrumShow(this->m_deviceUIs[deviceSetIndex]->m_mainSpectrumGUI); } ); + QObject::connect( + deviceGUI, + &DeviceGUI::showAllChannels, + this, + &MainWindow::showAllChannels + ); + QObject::connect( + deviceGUI, + &DeviceGUI::closing, + this, + [=](){ this->removeDeviceSet(deviceGUI->getIndex()); } + ); + deviceAPI->getSampleSource()->setMessageQueueToGUI(deviceGUI->getInputMessageQueue()); deviceUISet->m_deviceGUI = deviceGUI; const PluginInterface::SamplingDevice *selectedDevice = DeviceEnumerator::instance()->getRxSamplingDevice(selectedDeviceIndex); @@ -536,6 +557,9 @@ void MainWindow::sampleSourceCreate( deviceGUI->setToolTip(samplingDevice->displayedName); deviceGUI->setTitle(samplingDevice->displayedName.split(" ")[0]); deviceGUI->setCurrentDeviceIndex(selectedDeviceIndex); + QStringList channelNames; + m_pluginManager->listRxChannels(channelNames); + deviceGUI->setChannelNames(channelNames); MainSpectrumGUI *mainSpectrumGUI = deviceUISet->m_mainSpectrumGUI; mainSpectrumGUI->setDeviceType(MainSpectrumGUI::DeviceRx); mainSpectrumGUI->setIndex(deviceSetIndex); @@ -589,6 +613,13 @@ void MainWindow::sampleSinkAdd(Workspace *workspace, int deviceIndex) [=](int wsIndexDest){ this->mainSpectrumMove(m_deviceUIs.back()->m_mainSpectrumGUI, wsIndexDest); } ); + QObject::connect( + m_deviceUIs.back()->m_deviceGUI, + &DeviceGUI::addChannelEmitted, + this, + [=](int channelIndex){ this->channelAddClicked(workspace, deviceSetIndex, channelIndex); } + ); + workspace->addToMdiArea(m_deviceUIs.back()->m_deviceGUI); workspace->addToMdiArea(m_deviceUIs.back()->m_mainSpectrumGUI); emit m_mainCore->deviceSetAdded(deviceSetIndex, deviceAPI); @@ -704,6 +735,24 @@ void MainWindow::sampleSinkCreate( this, [=](int newDeviceIndex){ this->sampleDeviceChangeHandler(deviceGUI, newDeviceIndex); } ); + QObject::connect( + deviceGUI, + &DeviceGUI::showSpectrum, + this, + [=](int deviceSetIndex){ this->mainSpectrumShow(this->m_deviceUIs[deviceSetIndex]->m_mainSpectrumGUI); } + ); + QObject::connect( + deviceGUI, + &DeviceGUI::showAllChannels, + this, + &MainWindow::showAllChannels + ); + QObject::connect( + deviceGUI, + &DeviceGUI::closing, + this, + [=](){ this->removeDeviceSet(deviceGUI->getIndex()); } + ); deviceAPI->getSampleSink()->setMessageQueueToGUI(deviceGUI->getInputMessageQueue()); deviceUISet->m_deviceGUI = deviceGUI; const PluginInterface::SamplingDevice *selectedDevice = DeviceEnumerator::instance()->getRxSamplingDevice(selectedDeviceIndex); @@ -718,6 +767,9 @@ void MainWindow::sampleSinkCreate( deviceGUI->setToolTip(samplingDevice->displayedName); deviceGUI->setTitle(samplingDevice->displayedName.split(" ")[0]); deviceGUI->setCurrentDeviceIndex(selectedDeviceIndex); + QStringList channelNames; + m_pluginManager->listTxChannels(channelNames); + deviceGUI->setChannelNames(channelNames); MainSpectrumGUI *spectrumGUI = deviceUISet->m_mainSpectrumGUI; spectrumGUI->setDeviceType(MainSpectrumGUI::DeviceTx); spectrumGUI->setIndex(deviceSetIndex); @@ -779,6 +831,13 @@ void MainWindow::sampleMIMOAdd(Workspace *workspace, int deviceIndex) [=](int wsIndexDest){ this->mainSpectrumMove(m_deviceUIs.back()->m_mainSpectrumGUI, wsIndexDest); } ); + QObject::connect( + m_deviceUIs.back()->m_deviceGUI, + &DeviceGUI::addChannelEmitted, + this, + [=](int channelIndex){ this->channelAddClicked(workspace, deviceSetIndex, channelIndex); } + ); + workspace->addToMdiArea(m_deviceUIs.back()->m_deviceGUI); workspace->addToMdiArea(m_deviceUIs.back()->m_mainSpectrumGUI); emit m_mainCore->deviceSetAdded(deviceSetIndex, deviceAPI); @@ -860,6 +919,24 @@ void MainWindow::sampleMIMOCreate( this, [=](int newDeviceIndex){ this->sampleDeviceChangeHandler(deviceGUI, newDeviceIndex); } ); + QObject::connect( + deviceGUI, + &DeviceGUI::showSpectrum, + this, + [=](int deviceSetIndex){ this->mainSpectrumShow(this->m_deviceUIs[deviceSetIndex]->m_mainSpectrumGUI); } + ); + QObject::connect( + deviceGUI, + &DeviceGUI::showAllChannels, + this, + &MainWindow::showAllChannels + ); + QObject::connect( + deviceGUI, + &DeviceGUI::closing, + this, + [=](){ this->removeDeviceSet(deviceGUI->getIndex()); } + ); deviceAPI->getSampleMIMO()->setMessageQueueToGUI(deviceGUI->getInputMessageQueue()); deviceUISet->m_deviceGUI = deviceGUI; const PluginInterface::SamplingDevice *selectedDevice = DeviceEnumerator::instance()->getRxSamplingDevice(selectedDeviceIndex); @@ -874,6 +951,9 @@ void MainWindow::sampleMIMOCreate( deviceGUI->setToolTip(samplingDevice->displayedName); deviceGUI->setTitle(samplingDevice->displayedName.split(" ")[0]); deviceGUI->setCurrentDeviceIndex(selectedDeviceIndex); + QStringList channelNames; + m_pluginManager->listMIMOChannels(channelNames); + deviceGUI->setChannelNames(channelNames); MainSpectrumGUI *spectrumGUI = deviceUISet->m_mainSpectrumGUI; spectrumGUI->setDeviceType(MainSpectrumGUI::DeviceMIMO); spectrumGUI->setIndex(deviceSetIndex); @@ -881,6 +961,97 @@ void MainWindow::sampleMIMOCreate( spectrumGUI->setTitle(samplingDevice->displayedName.split(" ")[0]); } +void MainWindow::removeDeviceSet(int deviceSetIndex) +{ + if (deviceSetIndex >= (int) m_deviceUIs.size()) { + return; + } + + DeviceUISet *deviceUISet = m_deviceUIs[deviceSetIndex]; + + if (deviceUISet->m_deviceSourceEngine) // source device + { + DSPDeviceSourceEngine *deviceEngine = deviceUISet->m_deviceSourceEngine; + deviceEngine->stopAcquistion(); + deviceEngine->removeSink(deviceUISet->m_spectrumVis); + + // deletes old UI and core object + deviceUISet->freeChannels(); // destroys the channel instances + deviceUISet->m_deviceAPI->getSampleSource()->setMessageQueueToGUI(nullptr); // have source stop sending messages to the GUI + deviceUISet->m_deviceGUI->destroy(); + deviceUISet->m_deviceAPI->resetSamplingDeviceId(); + deviceUISet->m_deviceAPI->getPluginInterface()->deleteSampleSourcePluginInstanceInput( + deviceUISet->m_deviceAPI->getSampleSource()); + deviceUISet->m_deviceAPI->clearBuddiesLists(); // clear old API buddies lists + + DeviceAPI *sourceAPI = deviceUISet->m_deviceAPI; + delete deviceUISet; + + deviceEngine->stop(); + m_dspEngine->removeDeviceEngineAt(deviceSetIndex); + + delete sourceAPI; + } + else if (deviceUISet->m_deviceSinkEngine) // sink device + { + DSPDeviceSinkEngine *deviceEngine = deviceUISet->m_deviceSinkEngine; + deviceEngine->stopGeneration(); + deviceEngine->removeSpectrumSink(deviceUISet->m_spectrumVis); + + // deletes old UI and output object + deviceUISet->freeChannels(); + deviceUISet->m_deviceAPI->getSampleSink()->setMessageQueueToGUI(nullptr); // have sink stop sending messages to the GUI + deviceUISet->m_deviceGUI->destroy(); + deviceUISet->m_deviceAPI->resetSamplingDeviceId(); + deviceUISet->m_deviceAPI->getPluginInterface()->deleteSampleSinkPluginInstanceOutput( + deviceUISet->m_deviceAPI->getSampleSink()); + deviceUISet->m_deviceAPI->clearBuddiesLists(); // clear old API buddies lists + + DeviceAPI *sinkAPI = deviceUISet->m_deviceAPI; + delete deviceUISet; + + deviceEngine->stop(); + m_dspEngine->removeDeviceEngineAt(deviceSetIndex); + + delete sinkAPI; + } + else if (deviceUISet->m_deviceMIMOEngine) // MIMO device + { + DSPDeviceMIMOEngine *deviceEngine = deviceUISet->m_deviceMIMOEngine; + deviceEngine->stopProcess(1); // Tx side + deviceEngine->stopProcess(0); // Rx side + deviceEngine->removeSpectrumSink(deviceUISet->m_spectrumVis); + + // deletes old UI and output object + deviceUISet->freeChannels(); + deviceUISet->m_deviceAPI->getSampleMIMO()->setMessageQueueToGUI(nullptr); // have sink stop sending messages to the GUI + deviceUISet->m_deviceGUI->destroy(); + deviceUISet->m_deviceAPI->resetSamplingDeviceId(); + deviceUISet->m_deviceAPI->getPluginInterface()->deleteSampleMIMOPluginInstanceMIMO( + deviceUISet->m_deviceAPI->getSampleMIMO()); + + DeviceAPI *mimoAPI = deviceUISet->m_deviceAPI; + delete deviceUISet; + + deviceEngine->stop(); + m_dspEngine->removeDeviceEngineAt(deviceSetIndex); + + delete mimoAPI; + } + + m_deviceUIs.erase(m_deviceUIs.begin() + deviceSetIndex); + m_mainCore->removeDeviceSet(deviceSetIndex); + + // Renumerate + for (int i = 0; i < (int) m_deviceUIs.size(); i++) + { + DeviceUISet *deviceUISet = m_deviceUIs[i]; + deviceUISet->setIndex(i); + } + + emit m_mainCore->deviceSetRemoved(deviceSetIndex); +} + void MainWindow::removeLastDevice() { int removedDeviceSetIndex = m_deviceUIs.size() - 1; @@ -891,10 +1062,6 @@ void MainWindow::removeLastDevice() lastDeviceEngine->stopAcquistion(); lastDeviceEngine->removeSink(m_deviceUIs.back()->m_spectrumVis); - // ui->tabSpectraGUI->removeTab(ui->tabSpectraGUI->count() - 1); - // ui->tabSpectra->removeTab(ui->tabSpectra->count() - 1); - // ui->inputViewDock->removeLastDevice(); - // deletes old UI and input object m_deviceUIs.back()->freeChannels(); // destroys the channel instances m_deviceUIs.back()->m_deviceAPI->getSampleSource()->setMessageQueueToGUI(nullptr); // have source stop sending messages to the GUI @@ -904,11 +1071,6 @@ void MainWindow::removeLastDevice() m_deviceUIs.back()->m_deviceAPI->getSampleSource()); m_deviceUIs.back()->m_deviceAPI->clearBuddiesLists(); // clear old API buddies lists - // ui->tabChannels->removeTab(ui->tabChannels->count() - 1); - - // m_deviceWidgetTabs.removeLast(); - // restoreDeviceTabs(); - DeviceAPI *sourceAPI = m_deviceUIs.back()->m_deviceAPI; delete m_deviceUIs.back(); @@ -923,10 +1085,6 @@ void MainWindow::removeLastDevice() lastDeviceEngine->stopGeneration(); lastDeviceEngine->removeSpectrumSink(m_deviceUIs.back()->m_spectrumVis); - // ui->tabSpectraGUI->removeTab(ui->tabSpectraGUI->count() - 1); - // ui->tabSpectra->removeTab(ui->tabSpectra->count() - 1); - // ui->inputViewDock->removeLastDevice(); - // deletes old UI and output object m_deviceUIs.back()->freeChannels(); m_deviceUIs.back()->m_deviceAPI->getSampleSink()->setMessageQueueToGUI(nullptr); // have sink stop sending messages to the GUI @@ -936,11 +1094,6 @@ void MainWindow::removeLastDevice() m_deviceUIs.back()->m_deviceAPI->getSampleSink()); m_deviceUIs.back()->m_deviceAPI->clearBuddiesLists(); // clear old API buddies lists - // ui->tabChannels->removeTab(ui->tabChannels->count() - 1); - - // m_deviceWidgetTabs.removeLast(); - // restoreDeviceTabs(); - DeviceAPI *sinkAPI = m_deviceUIs.back()->m_deviceAPI; delete m_deviceUIs.back(); @@ -956,10 +1109,6 @@ void MainWindow::removeLastDevice() lastDeviceEngine->stopProcess(0); // Rx side lastDeviceEngine->removeSpectrumSink(m_deviceUIs.back()->m_spectrumVis); - // ui->tabSpectraGUI->removeTab(ui->tabSpectraGUI->count() - 1); - // ui->tabSpectra->removeTab(ui->tabSpectra->count() - 1); - // ui->inputViewDock->removeLastDevice(); - // deletes old UI and output object m_deviceUIs.back()->freeChannels(); m_deviceUIs.back()->m_deviceAPI->getSampleMIMO()->setMessageQueueToGUI(nullptr); // have sink stop sending messages to the GUI @@ -968,11 +1117,6 @@ void MainWindow::removeLastDevice() m_deviceUIs.back()->m_deviceAPI->getPluginInterface()->deleteSampleMIMOPluginInstanceMIMO( m_deviceUIs.back()->m_deviceAPI->getSampleMIMO()); - // ui->tabChannels->removeTab(ui->tabChannels->count() - 1); - - // m_deviceWidgetTabs.removeLast(); - // restoreDeviceTabs(); - DeviceAPI *mimoAPI = m_deviceUIs.back()->m_deviceAPI; delete m_deviceUIs.back(); @@ -1119,7 +1263,7 @@ void MainWindow::saveFeatureSetPresetSettings(FeatureSetPreset* preset, int feat featureUI->saveFeatureSetSettings(preset); } -void MainWindow::loadConfiguration(const Configuration *configuration) +void MainWindow::loadConfiguration(const Configuration *configuration, bool fromDialog) { qDebug("MainWindow::loadConfiguration: configuration [%s | %s] %d workspace(s) - %d device set(s) - %d feature(s)", qPrintable(configuration->getGroup()), @@ -1129,6 +1273,21 @@ void MainWindow::loadConfiguration(const Configuration *configuration) configuration->getFeatureSetPreset().getFeatureCount() ); + QMessageBox *waitBox = nullptr; + + if (fromDialog) + { + waitBox = new QMessageBox(this); + waitBox->setStandardButtons(QMessageBox::NoButton); + waitBox->setWindowModality(Qt::NonModal); + waitBox->setIcon(QMessageBox::Information); + waitBox->setText("Loading configuration "); + waitBox->setInformativeText("Deleting existing..."); + waitBox->setWindowTitle("Please wait"); + waitBox->show(); + waitBox->raise(); + } + // Wipe out everything first // Device sets @@ -1155,6 +1314,10 @@ void MainWindow::loadConfiguration(const Configuration *configuration) } // Device sets + if (waitBox) { + waitBox->setInformativeText("Loading device sets..."); + } + const QList& deviceSetPresets = configuration->getDeviceSetPresets(); for (const auto& deviceSetPreset : deviceSetPresets) @@ -1201,7 +1364,12 @@ void MainWindow::loadConfiguration(const Configuration *configuration) m_deviceUIs.back()->m_mainSpectrumGUI->restoreGeometry(deviceSetPreset.getSpectrumGeometry()); m_deviceUIs.back()->loadDeviceSetSettings(&deviceSetPreset, m_pluginManager->getPluginAPI(), &m_workspaces, nullptr); } + // Features + if (waitBox) { + waitBox->setInformativeText("Loading device sets..."); + } + m_featureUIs[0]->loadFeatureSetSettings( &configuration->getFeatureSetPreset(), m_pluginManager->getPluginAPI(), @@ -1222,9 +1390,19 @@ void MainWindow::loadConfiguration(const Configuration *configuration) } // Lastly restore workspaces geometry + if (waitBox) { + waitBox->setInformativeText("Finalizing..."); + } + for (int i = 0; i < configuration->getNumberOfWorkspaces(); i++) { m_workspaces[i]->restoreGeometry(configuration->getWorkspaceGeometries()[i]); } + + if (waitBox) + { + waitBox->close(); + delete waitBox; + } } void MainWindow::saveConfiguration(Configuration *configuration) @@ -2166,7 +2344,7 @@ void MainWindow::on_action_Configurations_triggered() &dialog, &ConfigurationsDialog::loadConfiguration, this, - &MainWindow::loadConfiguration + [=](const Configuration* configuration) { this->loadConfiguration(configuration, true); } ); dialog.exec(); } @@ -2323,6 +2501,13 @@ void MainWindow::sampleSourceChange(int deviceSetIndex, int newDeviceIndex, Work sampleSourceCreate(deviceSetIndex, newDeviceIndex, deviceUISet->m_deviceAPI, deviceUISet); deviceUISet->m_deviceGUI->setWorkspaceIndex(workspace->getIndex()); workspace->addToMdiArea(deviceUISet->m_deviceGUI); + + QObject::connect( + deviceUISet->m_deviceGUI, + &DeviceGUI::addChannelEmitted, + this, + [=](int channelIndex){ this->channelAddClicked(workspace, deviceSetIndex, channelIndex); } + ); } } @@ -2345,6 +2530,13 @@ void MainWindow::sampleSinkChange(int deviceSetIndex, int newDeviceIndex, Worksp sampleSourceCreate(deviceSetIndex, newDeviceIndex, deviceUISet->m_deviceAPI, deviceUISet); deviceUISet->m_deviceGUI->setWorkspaceIndex(workspace->getIndex()); workspace->addToMdiArea(deviceUISet->m_deviceGUI); + + QObject::connect( + deviceUISet->m_deviceGUI, + &DeviceGUI::addChannelEmitted, + this, + [=](int channelIndex){ this->channelAddClicked(workspace, deviceSetIndex, channelIndex); } + ); } } @@ -2366,78 +2558,111 @@ void MainWindow::sampleMIMOChange(int deviceSetIndex, int newDeviceIndex, Worksp sampleSourceCreate(deviceSetIndex, newDeviceIndex, deviceUISet->m_deviceAPI, deviceUISet); deviceUISet->m_deviceGUI->setWorkspaceIndex(workspace->getIndex()); workspace->addToMdiArea(deviceUISet->m_deviceGUI); + + QObject::connect( + deviceUISet->m_deviceGUI, + &DeviceGUI::addChannelEmitted, + this, + [=](int channelIndex){ this->channelAddClicked(workspace, deviceSetIndex, channelIndex); } + ); } } -void MainWindow::channelAddClicked(int channelIndex) +void MainWindow::channelAddClicked(Workspace *workspace, int deviceSetIndex, int channelIndex) { - // Do it in the currently selected source tab - // int currentChannelTabIndex = ui->tabChannels->currentIndex(); + if (deviceSetIndex >= 0) + { + DeviceUISet *deviceUI = m_deviceUIs[deviceSetIndex]; + ChannelGUI *gui = nullptr; + DeviceAPI *deviceAPI = deviceUI->m_deviceAPI; - // if (currentChannelTabIndex >= 0) - // { - // DeviceUISet *deviceUI = m_deviceUIs[currentChannelTabIndex]; + if (deviceUI->m_deviceSourceEngine) // source device => Rx channels + { + PluginAPI::ChannelRegistrations *channelRegistrations = m_pluginManager->getRxChannelRegistrations(); // Available channel plugins + PluginInterface *pluginInterface = (*channelRegistrations)[channelIndex].m_plugin; + ChannelAPI *channelAPI; + BasebandSampleSink *rxChannel; + pluginInterface->createRxChannel(deviceUI->m_deviceAPI, &rxChannel, &channelAPI); + gui = pluginInterface->createRxChannelGUI(deviceUI, rxChannel); + deviceUI->registerRxChannelInstance(channelAPI, gui); + gui->setDeviceType(ChannelGUI::DeviceRx); + gui->setIndex(channelAPI->getIndexInDeviceSet()); + } + else if (deviceUI->m_deviceSinkEngine) // sink device => Tx channels + { + PluginAPI::ChannelRegistrations *channelRegistrations = m_pluginManager->getTxChannelRegistrations(); // Available channel plugins + PluginInterface *pluginInterface = (*channelRegistrations)[channelIndex].m_plugin; + ChannelAPI *channelAPI; + BasebandSampleSource *txChannel; + pluginInterface->createTxChannel(deviceUI->m_deviceAPI, &txChannel, &channelAPI); + gui = pluginInterface->createTxChannelGUI(deviceUI, txChannel); + deviceUI->registerTxChannelInstance(channelAPI, gui); + gui->setDeviceType(ChannelGUI::DeviceTx); + gui->setIndex(channelAPI->getIndexInDeviceSet()); + } + else if (deviceUI->m_deviceMIMOEngine) // MIMO device => all possible channels. Depends on index range + { + int nbMIMOChannels = deviceUI->getNumberOfAvailableMIMOChannels(); + int nbRxChannels = deviceUI->getNumberOfAvailableRxChannels(); + int nbTxChannels = deviceUI->getNumberOfAvailableTxChannels(); + qDebug("MainWindow::channelAddClicked: MIMO: dev %d : nbMIMO: %d nbRx: %d nbTx: %d selected: %d", + deviceSetIndex, nbMIMOChannels, nbRxChannels, nbTxChannels, channelIndex); - // if (deviceUI->m_deviceSourceEngine) // source device => Rx channels - // { - // PluginAPI::ChannelRegistrations *channelRegistrations = m_pluginManager->getRxChannelRegistrations(); // Available channel plugins - // PluginInterface *pluginInterface = (*channelRegistrations)[channelIndex].m_plugin; - // ChannelAPI *channelAPI; - // BasebandSampleSink *rxChannel; - // pluginInterface->createRxChannel(deviceUI->m_deviceAPI, &rxChannel, &channelAPI); - // ChannelGUI *gui = pluginInterface->createRxChannelGUI(deviceUI, rxChannel); - // deviceUI->registerRxChannelInstance(channelAPI, gui); - // } - // else if (deviceUI->m_deviceSinkEngine) // sink device => Tx channels - // { - // PluginAPI::ChannelRegistrations *channelRegistrations = m_pluginManager->getTxChannelRegistrations(); // Available channel plugins - // PluginInterface *pluginInterface = (*channelRegistrations)[channelIndex].m_plugin; - // ChannelAPI *channelAPI; - // BasebandSampleSource *txChannel; - // pluginInterface->createTxChannel(deviceUI->m_deviceAPI, &txChannel, &channelAPI); - // ChannelGUI *gui = pluginInterface->createTxChannelGUI(deviceUI, txChannel); - // deviceUI->registerTxChannelInstance(channelAPI, gui); - // } - // else if (deviceUI->m_deviceMIMOEngine) // MIMO device => all possible channels. Depends on index range - // { - // int nbMIMOChannels = deviceUI->getNumberOfAvailableMIMOChannels(); - // int nbRxChannels = deviceUI->getNumberOfAvailableRxChannels(); - // int nbTxChannels = deviceUI->getNumberOfAvailableTxChannels(); - // qDebug("MainWindow::channelAddClicked: MIMO: tab %d : nbMIMO: %d nbRx: %d nbTx: %d selected: %d", - // currentChannelTabIndex, nbMIMOChannels, nbRxChannels, nbTxChannels, channelIndex); + if (channelIndex < nbMIMOChannels) + { + PluginAPI::ChannelRegistrations *channelRegistrations = m_pluginManager->getMIMOChannelRegistrations(); // Available channel plugins + PluginInterface *pluginInterface = (*channelRegistrations)[channelIndex].m_plugin; + ChannelAPI *channelAPI; + MIMOChannel *mimoChannel; + pluginInterface->createMIMOChannel(deviceUI->m_deviceAPI, &mimoChannel, &channelAPI); + gui = pluginInterface->createMIMOChannelGUI(deviceUI, mimoChannel); + deviceUI->registerChannelInstance(channelAPI, gui); + gui->setIndex(channelAPI->getIndexInDeviceSet()); + } + else if (channelIndex < nbMIMOChannels + nbRxChannels) // Rx + { + PluginAPI::ChannelRegistrations *channelRegistrations = m_pluginManager->getRxChannelRegistrations(); // Available channel plugins + PluginInterface *pluginInterface = (*channelRegistrations)[channelIndex - nbMIMOChannels].m_plugin; + ChannelAPI *channelAPI; + BasebandSampleSink *rxChannel; + pluginInterface->createRxChannel(deviceUI->m_deviceAPI, &rxChannel, &channelAPI); + gui = pluginInterface->createRxChannelGUI(deviceUI, rxChannel); + deviceUI->registerRxChannelInstance(channelAPI, gui); + gui->setIndex(channelAPI->getIndexInDeviceSet()); + } + else if (channelIndex < nbMIMOChannels + nbRxChannels + nbTxChannels) + { + PluginAPI::ChannelRegistrations *channelRegistrations = m_pluginManager->getTxChannelRegistrations(); // Available channel plugins + PluginInterface *pluginInterface = (*channelRegistrations)[channelIndex - nbMIMOChannels - nbRxChannels].m_plugin; + ChannelAPI *channelAPI; + BasebandSampleSource *txChannel; + pluginInterface->createTxChannel(deviceUI->m_deviceAPI, &txChannel, &channelAPI); + gui = pluginInterface->createTxChannelGUI(deviceUI, txChannel); + deviceUI->registerTxChannelInstance(channelAPI, gui); + gui->setIndex(channelAPI->getIndexInDeviceSet()); + } - // if (channelIndex < nbMIMOChannels) - // { - // PluginAPI::ChannelRegistrations *channelRegistrations = m_pluginManager->getMIMOChannelRegistrations(); // Available channel plugins - // PluginInterface *pluginInterface = (*channelRegistrations)[channelIndex].m_plugin; - // ChannelAPI *channelAPI; - // MIMOChannel *mimoChannel; - // pluginInterface->createMIMOChannel(deviceUI->m_deviceAPI, &mimoChannel, &channelAPI); - // ChannelGUI *gui = pluginInterface->createMIMOChannelGUI(deviceUI, mimoChannel); - // deviceUI->registerChannelInstance(channelAPI, gui); - // } - // else if (channelIndex < nbMIMOChannels + nbRxChannels) // Rx - // { - // PluginAPI::ChannelRegistrations *channelRegistrations = m_pluginManager->getRxChannelRegistrations(); // Available channel plugins - // PluginInterface *pluginInterface = (*channelRegistrations)[channelIndex - nbMIMOChannels].m_plugin; - // ChannelAPI *channelAPI; - // BasebandSampleSink *rxChannel; - // pluginInterface->createRxChannel(deviceUI->m_deviceAPI, &rxChannel, &channelAPI); - // ChannelGUI *gui = pluginInterface->createRxChannelGUI(deviceUI, rxChannel); - // deviceUI->registerRxChannelInstance(channelAPI, gui); - // } - // else if (channelIndex < nbMIMOChannels + nbRxChannels + nbTxChannels) - // { - // PluginAPI::ChannelRegistrations *channelRegistrations = m_pluginManager->getTxChannelRegistrations(); // Available channel plugins - // PluginInterface *pluginInterface = (*channelRegistrations)[channelIndex - nbMIMOChannels - nbRxChannels].m_plugin; - // ChannelAPI *channelAPI; - // BasebandSampleSource *txChannel; - // pluginInterface->createTxChannel(deviceUI->m_deviceAPI, &txChannel, &channelAPI); - // ChannelGUI *gui = pluginInterface->createTxChannelGUI(deviceUI, txChannel); - // deviceUI->registerTxChannelInstance(channelAPI, gui); - // } - // } - // } + gui->setDeviceType(ChannelGUI::DeviceMIMO); + } + + if (gui) + { + QObject::connect( + gui, + &ChannelGUI::moveToWorkspace, + this, + [=](int wsIndexDest){ this->channelMove(gui, wsIndexDest); } + ); + + gui->setDeviceSetIndex(deviceSetIndex); + gui->setToolTip(deviceAPI->getSamplingDeviceDisplayName()); + gui->setWorkspaceIndex(workspace->getIndex()); + qDebug("MainWindow::channelAddClicked: adding %s to workspace #%d", + qPrintable(gui->getTitle()), workspace->getIndex()); + workspace->addToMdiArea((QMdiSubWindow*) gui); + //gui->restoreGeometry(gui->getGeometryBytes()); + } + } } void MainWindow::featureAddClicked(Workspace *workspace, int featureIndex) @@ -2494,6 +2719,19 @@ void MainWindow::deviceMove(DeviceGUI *gui, int wsIndexDestnation) m_workspaces[wsIndexDestnation]->addToMdiArea(gui); } +void MainWindow::channelMove(ChannelGUI *gui, int wsIndexDestnation) +{ + int wsIndexOrigin = gui->getWorkspaceIndex(); + + if (wsIndexOrigin == wsIndexDestnation) { + return; + } + + m_workspaces[wsIndexOrigin]->removeFromMdiArea(gui); + gui->setWorkspaceIndex(wsIndexDestnation); + m_workspaces[wsIndexDestnation]->addToMdiArea(gui); +} + void MainWindow::mainSpectrumMove(MainSpectrumGUI *gui, int wsIndexDestnation) { int wsIndexOrigin = gui->getWorkspaceIndex(); @@ -2512,6 +2750,15 @@ void MainWindow::mainSpectrumShow(MainSpectrumGUI *gui) gui->show(); } +void MainWindow::showAllChannels(int deviceSetIndex) +{ + DeviceUISet *deviceUISet = m_deviceUIs[deviceSetIndex]; + + for (int i = 0; i < deviceUISet->getNumberOfChannels(); i++) { + deviceUISet->getChannelGUIAt(i)->show(); + } +} + void MainWindow::openFeaturePresetsDialog(QPoint p, Workspace *workspace) { FeaturePresetsDialog dialog; diff --git a/sdrgui/mainwindow.h b/sdrgui/mainwindow.h index 2b799cf3d..174d5f366 100644 --- a/sdrgui/mainwindow.h +++ b/sdrgui/mainwindow.h @@ -57,6 +57,7 @@ class Preset; class Command; class FeatureSetPreset; class CommandKeyReceiver; +class ConfigurationsDialog; class QMenuBar; class Workspace; @@ -79,6 +80,9 @@ public: void commandKeysDisconnect(QObject *object, const char *slot); int getNumberOfWorkspaces() const { return m_workspaces.size(); } +public slots: + void channelMove(ChannelGUI *gui, int wsIndexDestnation); + private: enum { PGroup, @@ -137,9 +141,10 @@ private: QTreeWidgetItem* addPresetToTree(const Preset* preset); void applySettings(); + void removeDeviceSet(int deviceSetIndex); void removeLastDevice(); void addFeatureSet(); - void removeFeatureSet(unsigned int tabIndex); + void removeFeatureSet(unsigned int featureSetIndex); void removeAllFeatureSets(); void deleteChannel(int deviceSetIndex, int channelIndex); void sampleDeviceChange(int deviceType, int deviceSetIndex, int newDeviceIndex, Workspace *workspace); @@ -175,7 +180,7 @@ private slots: void addWorkspace(); void viewAllWorkspaces(); void removeEmptyWorkspaces(); - void loadConfiguration(const Configuration *configuration); + void loadConfiguration(const Configuration *configuration, bool fromDialog = false); void saveConfiguration(Configuration *configuration); void sampleSourceAdd(Workspace *workspace, int deviceIndex); void sampleSinkAdd(Workspace *workspace, int deviceIndex); @@ -202,13 +207,14 @@ private slots: void on_action_My_Position_triggered(); void on_action_DeviceUserArguments_triggered(); void on_action_commands_triggered(); - void channelAddClicked(int channelIndex); + void channelAddClicked(Workspace *workspace, int deviceSetIndex, int channelIndex); void featureAddClicked(Workspace *workspace, int featureIndex); void featureMove(FeatureGUI *gui, int wsIndexDestnation); void openFeaturePresetsDialog(QPoint p, Workspace *workspace); void deviceMove(DeviceGUI *gui, int wsIndexDestnation); void mainSpectrumMove(MainSpectrumGUI *gui, int wsIndexDestnation); void mainSpectrumShow(MainSpectrumGUI *gui); + void showAllChannels(int deviceSetIndex); void on_action_Quick_Start_triggered(); void on_action_Main_Window_triggered(); void on_action_Loaded_Plugins_triggered(); From f1cf2f4f18e70540cedac294ac2ba82d832fafe9 Mon Sep 17 00:00:00 2001 From: f4exb Date: Tue, 12 Apr 2022 18:27:27 +0200 Subject: [PATCH 011/115] Massive UI revamping (v7): persistent channel show/hide --- .../beamsteeringcwmod/beamsteeringcwmodgui.h | 2 ++ .../beamsteeringcwmodsettings.cpp | 3 +++ .../beamsteeringcwmod/beamsteeringcwmodsettings.h | 1 + .../interferometer/interferometergui.h | 2 ++ .../interferometer/interferometersettings.cpp | 3 +++ .../interferometer/interferometersettings.h | 1 + plugins/channelrx/chanalyzer/chanalyzergui.h | 2 ++ .../channelrx/chanalyzer/chanalyzersettings.cpp | 5 ++++- plugins/channelrx/chanalyzer/chanalyzersettings.h | 1 + plugins/channelrx/demodadsb/adsbdemodgui.h | 2 ++ plugins/channelrx/demodadsb/adsbdemodsettings.cpp | 3 +++ plugins/channelrx/demodadsb/adsbdemodsettings.h | 2 ++ plugins/channelrx/demodais/aisdemodgui.h | 2 ++ plugins/channelrx/demodais/aisdemodsettings.cpp | 3 +++ plugins/channelrx/demodais/aisdemodsettings.h | 1 + plugins/channelrx/demodam/amdemodgui.h | 2 ++ plugins/channelrx/demodam/amdemodsettings.cpp | 3 +++ plugins/channelrx/demodam/amdemodsettings.h | 1 + plugins/channelrx/demodapt/aptdemodgui.h | 2 ++ plugins/channelrx/demodapt/aptdemodsettings.cpp | 3 +++ plugins/channelrx/demodapt/aptdemodsettings.h | 1 + plugins/channelrx/demodatv/atvdemodgui.h | 2 ++ plugins/channelrx/demodatv/atvdemodsettings.cpp | 3 +++ plugins/channelrx/demodatv/atvdemodsettings.h | 1 + plugins/channelrx/demodbfm/bfmdemodgui.h | 2 ++ plugins/channelrx/demodbfm/bfmdemodsettings.cpp | 3 +++ plugins/channelrx/demodbfm/bfmdemodsettings.h | 1 + .../channelrx/demodchirpchat/chirpchatdemodgui.h | 2 ++ .../demodchirpchat/chirpchatdemodsettings.cpp | 3 +++ .../demodchirpchat/chirpchatdemodsettings.h | 1 + plugins/channelrx/demoddab/dabdemodgui.h | 2 ++ plugins/channelrx/demoddab/dabdemodsettings.cpp | 3 +++ plugins/channelrx/demoddab/dabdemodsettings.h | 1 + plugins/channelrx/demoddatv/datvdemodgui.h | 2 ++ plugins/channelrx/demoddatv/datvdemodsettings.cpp | 3 +++ plugins/channelrx/demoddatv/datvdemodsettings.h | 1 + plugins/channelrx/demoddsd/dsddemodgui.h | 2 ++ plugins/channelrx/demoddsd/dsddemodsettings.cpp | 3 +++ plugins/channelrx/demoddsd/dsddemodsettings.h | 1 + plugins/channelrx/demodfreedv/freedvdemodgui.h | 2 ++ .../channelrx/demodfreedv/freedvdemodsettings.cpp | 3 +++ .../channelrx/demodfreedv/freedvdemodsettings.h | 1 + plugins/channelrx/demodnfm/nfmdemodgui.h | 2 ++ plugins/channelrx/demodnfm/nfmdemodsettings.cpp | 3 +++ plugins/channelrx/demodnfm/nfmdemodsettings.h | 1 + plugins/channelrx/demodpacket/packetdemodgui.h | 2 ++ .../channelrx/demodpacket/packetdemodsettings.cpp | 3 +++ .../channelrx/demodpacket/packetdemodsettings.h | 1 + plugins/channelrx/demodpager/pagerdemodgui.h | 2 ++ .../channelrx/demodpager/pagerdemodsettings.cpp | 3 +++ plugins/channelrx/demodpager/pagerdemodsettings.h | 1 + .../demodradiosonde/radiosondedemodgui.h | 2 ++ .../demodradiosonde/radiosondedemodsettings.cpp | 3 +++ .../demodradiosonde/radiosondedemodsettings.h | 1 + plugins/channelrx/demodssb/ssbdemodgui.h | 2 ++ plugins/channelrx/demodssb/ssbdemodsettings.cpp | 3 +++ plugins/channelrx/demodssb/ssbdemodsettings.h | 1 + plugins/channelrx/demodvor/vordemodgui.h | 2 ++ plugins/channelrx/demodvor/vordemodsettings.cpp | 3 +++ plugins/channelrx/demodvor/vordemodsettings.h | 1 + plugins/channelrx/demodvorsc/vordemodscgui.h | 2 ++ .../channelrx/demodvorsc/vordemodscsettings.cpp | 3 +++ plugins/channelrx/demodvorsc/vordemodscsettings.h | 1 + plugins/channelrx/demodwfm/wfmdemodgui.h | 2 ++ plugins/channelrx/demodwfm/wfmdemodsettings.cpp | 3 +++ plugins/channelrx/demodwfm/wfmdemodsettings.h | 1 + plugins/channelrx/filesink/filesinkgui.h | 2 ++ plugins/channelrx/filesink/filesinksettings.cpp | 3 +++ plugins/channelrx/filesink/filesinksettings.h | 1 + plugins/channelrx/freqtracker/freqtrackergui.h | 2 ++ .../channelrx/freqtracker/freqtrackersettings.cpp | 3 +++ .../channelrx/freqtracker/freqtrackersettings.h | 1 + plugins/channelrx/localsink/localsinkgui.h | 2 ++ plugins/channelrx/localsink/localsinksettings.cpp | 3 +++ plugins/channelrx/localsink/localsinksettings.h | 1 + plugins/channelrx/noisefigure/noisefiguregui.h | 2 ++ .../channelrx/noisefigure/noisefiguresettings.cpp | 3 +++ .../channelrx/noisefigure/noisefiguresettings.h | 1 + .../channelrx/radioastronomy/radioastronomygui.h | 2 ++ .../radioastronomy/radioastronomysettings.cpp | 3 +++ .../radioastronomy/radioastronomysettings.h | 1 + plugins/channelrx/radioclock/radioclockgui.h | 2 ++ .../channelrx/radioclock/radioclocksettings.cpp | 3 +++ plugins/channelrx/radioclock/radioclocksettings.h | 1 + plugins/channelrx/remotesink/remotesinkgui.h | 2 ++ .../channelrx/remotesink/remotesinksettings.cpp | 3 +++ plugins/channelrx/remotesink/remotesinksettings.h | 1 + .../channelrx/sigmffilesink/sigmffilesinkgui.h | 2 ++ .../sigmffilesink/sigmffilesinksettings.cpp | 3 +++ .../sigmffilesink/sigmffilesinksettings.h | 1 + plugins/channelrx/udpsink/udpsinkgui.h | 2 ++ plugins/channelrx/udpsink/udpsinksettings.cpp | 7 +++++-- plugins/channelrx/udpsink/udpsinksettings.h | 1 + plugins/channeltx/filesource/filesourcegui.h | 2 ++ .../channeltx/filesource/filesourcesettings.cpp | 3 +++ plugins/channeltx/filesource/filesourcesettings.h | 1 + plugins/channeltx/localsource/localsourcegui.h | 2 ++ .../channeltx/localsource/localsourcesettings.cpp | 7 +++++-- .../channeltx/localsource/localsourcesettings.h | 1 + .../channeltx/mod802.15.4/ieee_802_15_4_modgui.h | 3 ++- .../mod802.15.4/ieee_802_15_4_modsettings.cpp | 3 +++ .../mod802.15.4/ieee_802_15_4_modsettings.h | 1 + plugins/channeltx/modais/aismodgui.h | 2 ++ plugins/channeltx/modais/aismodsettings.cpp | 3 +++ plugins/channeltx/modais/aismodsettings.h | 1 + plugins/channeltx/modam/ammodgui.h | 2 ++ plugins/channeltx/modam/ammodsettings.cpp | 3 +++ plugins/channeltx/modam/ammodsettings.h | 1 + plugins/channeltx/modatv/atvmodgui.h | 2 ++ plugins/channeltx/modatv/atvmodsettings.cpp | 3 +++ plugins/channeltx/modatv/atvmodsettings.h | 1 + plugins/channeltx/modchirpchat/chirpchatmodgui.h | 2 ++ .../modchirpchat/chirpchatmodsettings.cpp | 4 ++++ .../channeltx/modchirpchat/chirpchatmodsettings.h | 1 + plugins/channeltx/moddatv/datvmodgui.h | 2 ++ plugins/channeltx/moddatv/datvmodsettings.cpp | 3 +++ plugins/channeltx/moddatv/datvmodsettings.h | 1 + plugins/channeltx/modfreedv/freedvmodgui.h | 2 ++ plugins/channeltx/modfreedv/freedvmodsettings.cpp | 3 +++ plugins/channeltx/modfreedv/freedvmodsettings.h | 1 + plugins/channeltx/modnfm/nfmmodgui.h | 2 ++ plugins/channeltx/modnfm/nfmmodsettings.cpp | 3 +++ plugins/channeltx/modnfm/nfmmodsettings.h | 1 + plugins/channeltx/modpacket/packetmodgui.h | 2 ++ plugins/channeltx/modpacket/packetmodsettings.cpp | 3 +++ plugins/channeltx/modpacket/packetmodsettings.h | 1 + plugins/channeltx/modssb/ssbmodgui.h | 2 ++ plugins/channeltx/modssb/ssbmodsettings.cpp | 3 +++ plugins/channeltx/modssb/ssbmodsettings.h | 1 + plugins/channeltx/modwfm/wfmmodgui.h | 2 ++ plugins/channeltx/modwfm/wfmmodsettings.cpp | 3 +++ plugins/channeltx/modwfm/wfmmodsettings.h | 1 + plugins/channeltx/remotesource/remotesourcegui.h | 2 ++ .../remotesource/remotesourcesettings.cpp | 3 +++ .../channeltx/remotesource/remotesourcesettings.h | 1 + plugins/channeltx/udpsource/udpsourcegui.h | 2 ++ plugins/channeltx/udpsource/udpsourcesettings.cpp | 3 +++ plugins/channeltx/udpsource/udpsourcesettings.h | 1 + sdrgui/channel/channelgui.h | 2 ++ sdrgui/device/deviceuiset.cpp | 15 +++++++++++++++ 140 files changed, 300 insertions(+), 6 deletions(-) diff --git a/plugins/channelmimo/beamsteeringcwmod/beamsteeringcwmodgui.h b/plugins/channelmimo/beamsteeringcwmod/beamsteeringcwmodgui.h index 5b55b94fc..4c88cdd67 100644 --- a/plugins/channelmimo/beamsteeringcwmod/beamsteeringcwmodgui.h +++ b/plugins/channelmimo/beamsteeringcwmod/beamsteeringcwmodgui.h @@ -54,6 +54,8 @@ public: virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; }; virtual QString getTitle() const { return m_settings.m_title; }; virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; + virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } + virtual bool getHidden() const { return m_settings.m_hidden; } private: Ui::BeamSteeringCWModGUI* ui; diff --git a/plugins/channelmimo/beamsteeringcwmod/beamsteeringcwmodsettings.cpp b/plugins/channelmimo/beamsteeringcwmod/beamsteeringcwmodsettings.cpp index 8c056d7e0..fccedcc43 100644 --- a/plugins/channelmimo/beamsteeringcwmod/beamsteeringcwmodsettings.cpp +++ b/plugins/channelmimo/beamsteeringcwmod/beamsteeringcwmodsettings.cpp @@ -44,6 +44,7 @@ void BeamSteeringCWModSettings::resetToDefaults() m_reverseAPIDeviceIndex = 0; m_reverseAPIChannelIndex = 0; m_workspaceIndex = 0; + m_hidden = false; } QByteArray BeamSteeringCWModSettings::serialize() const @@ -67,6 +68,7 @@ QByteArray BeamSteeringCWModSettings::serialize() const s.writeS32(16, m_workspaceIndex); s.writeBlob(17, m_geometryBytes); + s.writeBool(18, m_hidden); return s.final(); } @@ -120,6 +122,7 @@ bool BeamSteeringCWModSettings::deserialize(const QByteArray& data) d.readS32(16, &m_workspaceIndex); d.readBlob(17, &m_geometryBytes); + d.readBool(18, &m_hidden, false); return true; } diff --git a/plugins/channelmimo/beamsteeringcwmod/beamsteeringcwmodsettings.h b/plugins/channelmimo/beamsteeringcwmod/beamsteeringcwmodsettings.h index 1dfbba1cd..bb64cb1a2 100644 --- a/plugins/channelmimo/beamsteeringcwmod/beamsteeringcwmodsettings.h +++ b/plugins/channelmimo/beamsteeringcwmod/beamsteeringcwmodsettings.h @@ -38,6 +38,7 @@ struct BeamSteeringCWModSettings uint16_t m_reverseAPIChannelIndex; int m_workspaceIndex; QByteArray m_geometryBytes; + bool m_hidden; Serializable *m_channelMarker; Serializable *m_rollupState; diff --git a/plugins/channelmimo/interferometer/interferometergui.h b/plugins/channelmimo/interferometer/interferometergui.h index d3dd6a03d..2d8ac1670 100644 --- a/plugins/channelmimo/interferometer/interferometergui.h +++ b/plugins/channelmimo/interferometer/interferometergui.h @@ -53,6 +53,8 @@ public: virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; }; virtual QString getTitle() const { return m_settings.m_title; }; virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; + virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } + virtual bool getHidden() const { return m_settings.m_hidden; } private: Ui::InterferometerGUI* ui; diff --git a/plugins/channelmimo/interferometer/interferometersettings.cpp b/plugins/channelmimo/interferometer/interferometersettings.cpp index e0c5846cf..013dde031 100644 --- a/plugins/channelmimo/interferometer/interferometersettings.cpp +++ b/plugins/channelmimo/interferometer/interferometersettings.cpp @@ -45,6 +45,7 @@ void InterferometerSettings::resetToDefaults() m_reverseAPIDeviceIndex = 0; m_reverseAPIChannelIndex = 0; m_workspaceIndex = 0; + m_hidden = false; } QByteArray InterferometerSettings::serialize() const @@ -64,6 +65,7 @@ QByteArray InterferometerSettings::serialize() const s.writeS32(12, m_phase); s.writeS32(13,m_workspaceIndex); s.writeBlob(14, m_geometryBytes); + s.writeBool(15, m_hidden); if (m_spectrumGUI) { s.writeBlob(20, m_spectrumGUI->serialize()); @@ -122,6 +124,7 @@ bool InterferometerSettings::deserialize(const QByteArray& data) m_phase = tmp < -180 ? -180 : tmp > 180 ? 180 : tmp; d.readS32(13, &m_workspaceIndex); d.readBlob(14, &m_geometryBytes); + d.readBool(15, &m_hidden, false); if (m_spectrumGUI) { diff --git a/plugins/channelmimo/interferometer/interferometersettings.h b/plugins/channelmimo/interferometer/interferometersettings.h index ead9dc259..30f2bf64e 100644 --- a/plugins/channelmimo/interferometer/interferometersettings.h +++ b/plugins/channelmimo/interferometer/interferometersettings.h @@ -50,6 +50,7 @@ struct InterferometerSettings uint16_t m_reverseAPIChannelIndex; int m_workspaceIndex; QByteArray m_geometryBytes; + bool m_hidden; Serializable *m_channelMarker; Serializable *m_spectrumGUI; diff --git a/plugins/channelrx/chanalyzer/chanalyzergui.h b/plugins/channelrx/chanalyzer/chanalyzergui.h index 69bee761e..1126b5fc3 100644 --- a/plugins/channelrx/chanalyzer/chanalyzergui.h +++ b/plugins/channelrx/chanalyzer/chanalyzergui.h @@ -55,6 +55,8 @@ public: virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; }; virtual QString getTitle() const { return m_settings.m_title; }; virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; + virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } + virtual bool getHidden() const { return m_settings.m_hidden; } public slots: void channelMarkerChangedByCursor(); diff --git a/plugins/channelrx/chanalyzer/chanalyzersettings.cpp b/plugins/channelrx/chanalyzer/chanalyzersettings.cpp index 3f3a21774..7f3ee49a4 100644 --- a/plugins/channelrx/chanalyzer/chanalyzersettings.cpp +++ b/plugins/channelrx/chanalyzer/chanalyzersettings.cpp @@ -60,6 +60,7 @@ void ChannelAnalyzerSettings::resetToDefaults() m_reverseAPIDeviceIndex = 0; m_reverseAPIChannelIndex = 0; m_workspaceIndex = 0; + m_hidden = false; } QByteArray ChannelAnalyzerSettings::serialize() const @@ -107,7 +108,8 @@ QByteArray ChannelAnalyzerSettings::serialize() const s.writeU32(27, m_reverseAPIChannelIndex); s.writeS32(28, m_streamIndex); s.writeS32(29, m_workspaceIndex); - s.writeString(30, m_geometryBytes); + s.writeBlob(30, m_geometryBytes); + s.writeBool(31, m_hidden); return s.final(); } @@ -184,6 +186,7 @@ bool ChannelAnalyzerSettings::deserialize(const QByteArray& data) d.readS32(28, &m_streamIndex, 0); d.readS32(29, &m_workspaceIndex, 0); d.readBlob(30, &m_geometryBytes); + d.readBool(31, &m_hidden, false); return true; } diff --git a/plugins/channelrx/chanalyzer/chanalyzersettings.h b/plugins/channelrx/chanalyzer/chanalyzersettings.h index 2469007ca..23865a355 100644 --- a/plugins/channelrx/chanalyzer/chanalyzersettings.h +++ b/plugins/channelrx/chanalyzer/chanalyzersettings.h @@ -62,6 +62,7 @@ struct ChannelAnalyzerSettings uint16_t m_reverseAPIChannelIndex; int m_workspaceIndex; QByteArray m_geometryBytes; + bool m_hidden; ChannelAnalyzerSettings(); void resetToDefaults(); diff --git a/plugins/channelrx/demodadsb/adsbdemodgui.h b/plugins/channelrx/demodadsb/adsbdemodgui.h index 22157eecd..c37be1e15 100644 --- a/plugins/channelrx/demodadsb/adsbdemodgui.h +++ b/plugins/channelrx/demodadsb/adsbdemodgui.h @@ -758,6 +758,8 @@ public: virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; }; virtual QString getTitle() const { return m_settings.m_title; }; virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; + virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } + virtual bool getHidden() const { return m_settings.m_hidden; } void highlightAircraft(Aircraft *aircraft); void targetAircraft(Aircraft *aircraft); diff --git a/plugins/channelrx/demodadsb/adsbdemodsettings.cpp b/plugins/channelrx/demodadsb/adsbdemodsettings.cpp index dcb35d644..b23b0fb67 100644 --- a/plugins/channelrx/demodadsb/adsbdemodsettings.cpp +++ b/plugins/channelrx/demodadsb/adsbdemodsettings.cpp @@ -97,6 +97,7 @@ void ADSBDemodSettings::resetToDefaults() m_verboseModelMatching = false; m_airfieldElevation = 0; m_workspaceIndex = 0; + m_hidden = false; } QByteArray ADSBDemodSettings::serialize() const @@ -175,6 +176,7 @@ QByteArray ADSBDemodSettings::serialize() const s.writeString(58, m_importMaxLongitude); s.writeS32(59, m_workspaceIndex); s.writeBlob(60, m_geometryBytes); + s.writeBool(61, m_hidden); for (int i = 0; i < ADSBDEMOD_COLUMNS; i++) { s.writeS32(100 + i, m_columnIndexes[i]); @@ -303,6 +305,7 @@ bool ADSBDemodSettings::deserialize(const QByteArray& data) d.readString(58, &m_importMaxLongitude, ""); d.readS32(59, &m_workspaceIndex, 0); d.readBlob(60, &m_geometryBytes); + d.readBool(61, &m_hidden, false); for (int i = 0; i < ADSBDEMOD_COLUMNS; i++) { d.readS32(100 + i, &m_columnIndexes[i], i); diff --git a/plugins/channelrx/demodadsb/adsbdemodsettings.h b/plugins/channelrx/demodadsb/adsbdemodsettings.h index 5e14da134..8fd12a054 100644 --- a/plugins/channelrx/demodadsb/adsbdemodsettings.h +++ b/plugins/channelrx/demodadsb/adsbdemodsettings.h @@ -118,6 +118,8 @@ struct ADSBDemodSettings uint16_t m_reverseAPIChannelIndex; int m_workspaceIndex; QByteArray m_geometryBytes; + bool m_hidden; + int m_columnIndexes[ADSBDEMOD_COLUMNS];//!< How the columns are ordered in the table int m_columnSizes[ADSBDEMOD_COLUMNS]; //!< Size of the coumns in the table diff --git a/plugins/channelrx/demodais/aisdemodgui.h b/plugins/channelrx/demodais/aisdemodgui.h index 1cdf5d3ba..727ac72eb 100644 --- a/plugins/channelrx/demodais/aisdemodgui.h +++ b/plugins/channelrx/demodais/aisdemodgui.h @@ -64,6 +64,8 @@ public: virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; }; virtual QString getTitle() const { return m_settings.m_title; }; virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; + virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } + virtual bool getHidden() const { return m_settings.m_hidden; } public slots: void channelMarkerChangedByCursor(); diff --git a/plugins/channelrx/demodais/aisdemodsettings.cpp b/plugins/channelrx/demodais/aisdemodsettings.cpp index 5597c447f..b719dfb0e 100644 --- a/plugins/channelrx/demodais/aisdemodsettings.cpp +++ b/plugins/channelrx/demodais/aisdemodsettings.cpp @@ -56,6 +56,7 @@ void AISDemodSettings::resetToDefaults() m_reverseAPIDeviceIndex = 0; m_reverseAPIChannelIndex = 0; m_workspaceIndex = 0; + m_hidden = false; for (int i = 0; i < AISDEMOD_MESSAGE_COLUMNS; i++) { @@ -103,6 +104,7 @@ QByteArray AISDemodSettings::serialize() const s.writeS32(26, m_workspaceIndex); s.writeBlob(27, m_geometryBytes); + s.writeBool(28, m_hidden); for (int i = 0; i < AISDEMOD_MESSAGE_COLUMNS; i++) s.writeS32(100 + i, m_messageColumnIndexes[i]); @@ -189,6 +191,7 @@ bool AISDemodSettings::deserialize(const QByteArray& data) d.readS32(26, &m_workspaceIndex, 0); d.readBlob(27, &m_geometryBytes); + d.readBool(28, &m_hidden, false); for (int i = 0; i < AISDEMOD_MESSAGE_COLUMNS; i++) { d.readS32(100 + i, &m_messageColumnIndexes[i], i); diff --git a/plugins/channelrx/demodais/aisdemodsettings.h b/plugins/channelrx/demodais/aisdemodsettings.h index bd06a99e5..2d22273cc 100644 --- a/plugins/channelrx/demodais/aisdemodsettings.h +++ b/plugins/channelrx/demodais/aisdemodsettings.h @@ -63,6 +63,7 @@ struct AISDemodSettings Serializable *m_rollupState; int m_workspaceIndex; QByteArray m_geometryBytes; + bool m_hidden; int m_messageColumnIndexes[AISDEMOD_MESSAGE_COLUMNS];//!< How the columns are ordered in the table int m_messageColumnSizes[AISDEMOD_MESSAGE_COLUMNS]; //!< Size of the columns in the table diff --git a/plugins/channelrx/demodam/amdemodgui.h b/plugins/channelrx/demodam/amdemodgui.h index adfc37404..bfb834b78 100644 --- a/plugins/channelrx/demodam/amdemodgui.h +++ b/plugins/channelrx/demodam/amdemodgui.h @@ -37,6 +37,8 @@ public: virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; }; virtual QString getTitle() const { return m_settings.m_title; }; virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; + virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } + virtual bool getHidden() const { return m_settings.m_hidden; } public slots: void channelMarkerChangedByCursor(); diff --git a/plugins/channelrx/demodam/amdemodsettings.cpp b/plugins/channelrx/demodam/amdemodsettings.cpp index cf5fcd8f3..8c6de24c9 100644 --- a/plugins/channelrx/demodam/amdemodsettings.cpp +++ b/plugins/channelrx/demodam/amdemodsettings.cpp @@ -50,6 +50,7 @@ void AMDemodSettings::resetToDefaults() m_reverseAPIDeviceIndex = 0; m_reverseAPIChannelIndex = 0; m_workspaceIndex = 0; + m_hidden = false; } QByteArray AMDemodSettings::serialize() const @@ -83,6 +84,7 @@ QByteArray AMDemodSettings::serialize() const s.writeS32(20, m_workspaceIndex); s.writeBlob(21, m_geometryBytes); + s.writeBool(22, m_hidden); return s.final(); } @@ -149,6 +151,7 @@ bool AMDemodSettings::deserialize(const QByteArray& data) d.readS32(20, &m_workspaceIndex, 0); d.readBlob(21, &m_geometryBytes); + d.readBool(22, &m_hidden, false); return true; } diff --git a/plugins/channelrx/demodam/amdemodsettings.h b/plugins/channelrx/demodam/amdemodsettings.h index 810d03b30..b925ac596 100644 --- a/plugins/channelrx/demodam/amdemodsettings.h +++ b/plugins/channelrx/demodam/amdemodsettings.h @@ -52,6 +52,7 @@ struct AMDemodSettings Serializable *m_rollupState; int m_workspaceIndex; QByteArray m_geometryBytes; + bool m_hidden; AMDemodSettings(); void resetToDefaults(); diff --git a/plugins/channelrx/demodapt/aptdemodgui.h b/plugins/channelrx/demodapt/aptdemodgui.h index 9b28c1641..52981e464 100644 --- a/plugins/channelrx/demodapt/aptdemodgui.h +++ b/plugins/channelrx/demodapt/aptdemodgui.h @@ -81,6 +81,8 @@ public: virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; }; virtual QString getTitle() const { return m_settings.m_title; }; virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; + virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } + virtual bool getHidden() const { return m_settings.m_hidden; } public slots: void channelMarkerChangedByCursor(); diff --git a/plugins/channelrx/demodapt/aptdemodsettings.cpp b/plugins/channelrx/demodapt/aptdemodsettings.cpp index 115905f9b..5e4e9ed6a 100644 --- a/plugins/channelrx/demodapt/aptdemodsettings.cpp +++ b/plugins/channelrx/demodapt/aptdemodsettings.cpp @@ -70,6 +70,7 @@ void APTDemodSettings::resetToDefaults() m_reverseAPIDeviceIndex = 0; m_reverseAPIChannelIndex = 0; m_workspaceIndex = 0; + m_hidden = false; } QByteArray APTDemodSettings::serialize() const @@ -123,6 +124,7 @@ QByteArray APTDemodSettings::serialize() const s.writeFloat(38, m_satYaw); s.writeS32(39, m_workspaceIndex); s.writeBlob(40, m_geometryBytes); + s.writeBool(41, m_hidden); return s.final(); } @@ -206,6 +208,7 @@ bool APTDemodSettings::deserialize(const QByteArray& data) d.readFloat(38, &m_satYaw, 0.0f); d.readS32(39, &m_workspaceIndex, 0); d.readBlob(40, &m_geometryBytes); + d.readBool(41, &m_hidden, false); return true; } diff --git a/plugins/channelrx/demodapt/aptdemodsettings.h b/plugins/channelrx/demodapt/aptdemodsettings.h index 08a53c1af..ce42c5e6f 100644 --- a/plugins/channelrx/demodapt/aptdemodsettings.h +++ b/plugins/channelrx/demodapt/aptdemodsettings.h @@ -68,6 +68,7 @@ struct APTDemodSettings Serializable *m_rollupState; int m_workspaceIndex; QByteArray m_geometryBytes; + bool m_hidden; // The following are really working state, rather than settings QString m_tle; // Satelite two-line elements, from satellite tracker diff --git a/plugins/channelrx/demodatv/atvdemodgui.h b/plugins/channelrx/demodatv/atvdemodgui.h index c4ce6a57c..793015d60 100644 --- a/plugins/channelrx/demodatv/atvdemodgui.h +++ b/plugins/channelrx/demodatv/atvdemodgui.h @@ -56,6 +56,8 @@ public: virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; }; virtual QString getTitle() const { return m_settings.m_title; }; virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; + virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } + virtual bool getHidden() const { return m_settings.m_hidden; } public slots: void channelMarkerChangedByCursor(); diff --git a/plugins/channelrx/demodatv/atvdemodsettings.cpp b/plugins/channelrx/demodatv/atvdemodsettings.cpp index be423fb78..2d914e7ca 100644 --- a/plugins/channelrx/demodatv/atvdemodsettings.cpp +++ b/plugins/channelrx/demodatv/atvdemodsettings.cpp @@ -60,6 +60,7 @@ void ATVDemodSettings::resetToDefaults() m_reverseAPIDeviceIndex = 0; m_reverseAPIChannelIndex = 0; m_workspaceIndex = 0; + m_hidden = false; } QByteArray ATVDemodSettings::serialize() const @@ -104,6 +105,7 @@ QByteArray ATVDemodSettings::serialize() const s.writeU32(30, m_reverseAPIChannelIndex); s.writeS32(31, m_workspaceIndex); s.writeBlob(32, m_geometryBytes); + s.writeBool(33, m_hidden); return s.final(); } @@ -183,6 +185,7 @@ bool ATVDemodSettings::deserialize(const QByteArray& arrData) m_reverseAPIChannelIndex = utmp > 99 ? 99 : utmp; d.readS32(31, &m_workspaceIndex, 0); d.readBlob(32, &m_geometryBytes); + d.readBool(33, &m_hidden, false); return true; } diff --git a/plugins/channelrx/demodatv/atvdemodsettings.h b/plugins/channelrx/demodatv/atvdemodsettings.h index 5a8eba3f3..c20afe176 100644 --- a/plugins/channelrx/demodatv/atvdemodsettings.h +++ b/plugins/channelrx/demodatv/atvdemodsettings.h @@ -82,6 +82,7 @@ struct ATVDemodSettings Serializable *m_rollupState; int m_workspaceIndex; QByteArray m_geometryBytes; + bool m_hidden; ATVDemodSettings(); void resetToDefaults(); diff --git a/plugins/channelrx/demodbfm/bfmdemodgui.h b/plugins/channelrx/demodbfm/bfmdemodgui.h index b3d8f77c5..c6d69ae75 100644 --- a/plugins/channelrx/demodbfm/bfmdemodgui.h +++ b/plugins/channelrx/demodbfm/bfmdemodgui.h @@ -54,6 +54,8 @@ public: virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; }; virtual QString getTitle() const { return m_settings.m_title; }; virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; + virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } + virtual bool getHidden() const { return m_settings.m_hidden; } public slots: void channelMarkerChangedByCursor(); diff --git a/plugins/channelrx/demodbfm/bfmdemodsettings.cpp b/plugins/channelrx/demodbfm/bfmdemodsettings.cpp index c09fcabfa..93dd244ca 100644 --- a/plugins/channelrx/demodbfm/bfmdemodsettings.cpp +++ b/plugins/channelrx/demodbfm/bfmdemodsettings.cpp @@ -57,6 +57,7 @@ void BFMDemodSettings::resetToDefaults() m_reverseAPIDeviceIndex = 0; m_reverseAPIChannelIndex = 0; m_workspaceIndex = 0; + m_hidden = false; } QByteArray BFMDemodSettings::serialize() const @@ -96,6 +97,7 @@ QByteArray BFMDemodSettings::serialize() const s.writeBool(21, m_rdsActive); s.writeS32(22, m_workspaceIndex); s.writeBlob(23, m_geometryBytes); + s.writeBool(24, m_hidden); return s.final(); } @@ -172,6 +174,7 @@ bool BFMDemodSettings::deserialize(const QByteArray& data) d.readBool(21, &m_rdsActive, false); d.readS32(22, &m_workspaceIndex, 0); d.readBlob(23, &m_geometryBytes); + d.readBool(24, &m_hidden, false); return true; } diff --git a/plugins/channelrx/demodbfm/bfmdemodsettings.h b/plugins/channelrx/demodbfm/bfmdemodsettings.h index 866f1589e..89237d325 100644 --- a/plugins/channelrx/demodbfm/bfmdemodsettings.h +++ b/plugins/channelrx/demodbfm/bfmdemodsettings.h @@ -44,6 +44,7 @@ struct BFMDemodSettings uint16_t m_reverseAPIChannelIndex; int m_workspaceIndex; QByteArray m_geometryBytes; + bool m_hidden; Serializable *m_channelMarker; Serializable *m_spectrumGUI; diff --git a/plugins/channelrx/demodchirpchat/chirpchatdemodgui.h b/plugins/channelrx/demodchirpchat/chirpchatdemodgui.h index fd1ebf4b6..3d62b5bff 100644 --- a/plugins/channelrx/demodchirpchat/chirpchatdemodgui.h +++ b/plugins/channelrx/demodchirpchat/chirpchatdemodgui.h @@ -52,6 +52,8 @@ public: virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; }; virtual QString getTitle() const { return m_settings.m_title; }; virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; + virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } + virtual bool getHidden() const { return m_settings.m_hidden; } private slots: void channelMarkerChangedByCursor(); diff --git a/plugins/channelrx/demodchirpchat/chirpchatdemodsettings.cpp b/plugins/channelrx/demodchirpchat/chirpchatdemodsettings.cpp index 4ae9d9411..b9b5a9388 100644 --- a/plugins/channelrx/demodchirpchat/chirpchatdemodsettings.cpp +++ b/plugins/channelrx/demodchirpchat/chirpchatdemodsettings.cpp @@ -90,6 +90,7 @@ void ChirpChatDemodSettings::resetToDefaults() m_reverseAPIDeviceIndex = 0; m_reverseAPIChannelIndex = 0; m_workspaceIndex = 0; + m_hidden = false; } QByteArray ChirpChatDemodSettings::serialize() const @@ -135,6 +136,7 @@ QByteArray ChirpChatDemodSettings::serialize() const s.writeS32(30, m_workspaceIndex); s.writeBlob(31, m_geometryBytes); + s.writeBool(32, m_hidden); return s.final(); } @@ -218,6 +220,7 @@ bool ChirpChatDemodSettings::deserialize(const QByteArray& data) d.readS32(30, &m_workspaceIndex, 0); d.readBlob(31, &m_geometryBytes); + d.readBool(32, &m_hidden, false); return true; } diff --git a/plugins/channelrx/demodchirpchat/chirpchatdemodsettings.h b/plugins/channelrx/demodchirpchat/chirpchatdemodsettings.h index f2bb10613..71c5c1384 100644 --- a/plugins/channelrx/demodchirpchat/chirpchatdemodsettings.h +++ b/plugins/channelrx/demodchirpchat/chirpchatdemodsettings.h @@ -65,6 +65,7 @@ struct ChirpChatDemodSettings uint16_t m_reverseAPIChannelIndex; int m_workspaceIndex; QByteArray m_geometryBytes; + bool m_hidden; Serializable *m_channelMarker; Serializable *m_spectrumGUI; diff --git a/plugins/channelrx/demoddab/dabdemodgui.h b/plugins/channelrx/demoddab/dabdemodgui.h index f8b59f534..9b080e1ba 100644 --- a/plugins/channelrx/demoddab/dabdemodgui.h +++ b/plugins/channelrx/demoddab/dabdemodgui.h @@ -59,6 +59,8 @@ public: virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; }; virtual QString getTitle() const { return m_settings.m_title; }; virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; + virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } + virtual bool getHidden() const { return m_settings.m_hidden; } public slots: void channelMarkerChangedByCursor(); diff --git a/plugins/channelrx/demoddab/dabdemodsettings.cpp b/plugins/channelrx/demoddab/dabdemodsettings.cpp index db575b573..14d16ab31 100644 --- a/plugins/channelrx/demoddab/dabdemodsettings.cpp +++ b/plugins/channelrx/demoddab/dabdemodsettings.cpp @@ -48,6 +48,7 @@ void DABDemodSettings::resetToDefaults() m_reverseAPIDeviceIndex = 0; m_reverseAPIChannelIndex = 0; m_workspaceIndex = 0; + m_hidden = false; for (int i = 0; i < DABDEMOD_COLUMNS; i++) { @@ -85,6 +86,7 @@ QByteArray DABDemodSettings::serialize() const s.writeS32(17, m_workspaceIndex); s.writeBlob(18, m_geometryBytes); + s.writeBool(19, m_hidden); for (int i = 0; i < DABDEMOD_COLUMNS; i++) { s.writeS32(100 + i, m_columnIndexes[i]); @@ -152,6 +154,7 @@ bool DABDemodSettings::deserialize(const QByteArray& data) d.readS32(17, &m_workspaceIndex, 0); d.readBlob(18, &m_geometryBytes); + d.readBool(19, &m_hidden, false); for (int i = 0; i < DABDEMOD_COLUMNS; i++) { d.readS32(100 + i, &m_columnIndexes[i], i); diff --git a/plugins/channelrx/demoddab/dabdemodsettings.h b/plugins/channelrx/demoddab/dabdemodsettings.h index 596ba2eaf..c2fa8c979 100644 --- a/plugins/channelrx/demoddab/dabdemodsettings.h +++ b/plugins/channelrx/demoddab/dabdemodsettings.h @@ -48,6 +48,7 @@ struct DABDemodSettings Serializable *m_rollupState; int m_workspaceIndex; QByteArray m_geometryBytes; + bool m_hidden; int m_columnIndexes[DABDEMOD_COLUMNS];//!< How the columns are ordered in the table int m_columnSizes[DABDEMOD_COLUMNS]; //!< Size of the columns in the table diff --git a/plugins/channelrx/demoddatv/datvdemodgui.h b/plugins/channelrx/demoddatv/datvdemodgui.h index 3812290a7..5ef25996c 100644 --- a/plugins/channelrx/demoddatv/datvdemodgui.h +++ b/plugins/channelrx/demoddatv/datvdemodgui.h @@ -56,6 +56,8 @@ public: virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; }; virtual QString getTitle() const { return m_settings.m_title; }; virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; + virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } + virtual bool getHidden() const { return m_settings.m_hidden; } virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } diff --git a/plugins/channelrx/demoddatv/datvdemodsettings.cpp b/plugins/channelrx/demoddatv/datvdemodsettings.cpp index eba558f95..da1d9773b 100644 --- a/plugins/channelrx/demoddatv/datvdemodsettings.cpp +++ b/plugins/channelrx/demoddatv/datvdemodsettings.cpp @@ -71,6 +71,7 @@ void DATVDemodSettings::resetToDefaults() m_reverseAPIDeviceIndex = 0; m_reverseAPIChannelIndex = 0; m_workspaceIndex = 0; + m_hidden = false; } QByteArray DATVDemodSettings::serialize() const @@ -122,6 +123,7 @@ QByteArray DATVDemodSettings::serialize() const s.writeS32(38, m_workspaceIndex); s.writeBlob(39, m_geometryBytes); + s.writeBool(40, m_hidden); return s.final(); } @@ -219,6 +221,7 @@ bool DATVDemodSettings::deserialize(const QByteArray& data) d.readS32(38, &m_workspaceIndex, 0); d.readBlob(39, &m_geometryBytes); + d.readBool(40, &m_hidden, false); validateSystemConfiguration(); diff --git a/plugins/channelrx/demoddatv/datvdemodsettings.h b/plugins/channelrx/demoddatv/datvdemodsettings.h index 8da3bbc9a..9c72c0a6a 100644 --- a/plugins/channelrx/demoddatv/datvdemodsettings.h +++ b/plugins/channelrx/demoddatv/datvdemodsettings.h @@ -110,6 +110,7 @@ struct DATVDemodSettings Serializable *m_rollupState; int m_workspaceIndex; QByteArray m_geometryBytes; + bool m_hidden; static const int m_softLDPCMaxMaxTrials = 50; diff --git a/plugins/channelrx/demoddsd/dsddemodgui.h b/plugins/channelrx/demoddsd/dsddemodgui.h index 1d4e4aacd..57f072a73 100644 --- a/plugins/channelrx/demoddsd/dsddemodgui.h +++ b/plugins/channelrx/demoddsd/dsddemodgui.h @@ -58,6 +58,8 @@ public: virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; }; virtual QString getTitle() const { return m_settings.m_title; }; virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; + virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } + virtual bool getHidden() const { return m_settings.m_hidden; } public slots: void channelMarkerChangedByCursor(); diff --git a/plugins/channelrx/demoddsd/dsddemodsettings.cpp b/plugins/channelrx/demoddsd/dsddemodsettings.cpp index 3c025b48e..ed7a6394f 100644 --- a/plugins/channelrx/demoddsd/dsddemodsettings.cpp +++ b/plugins/channelrx/demoddsd/dsddemodsettings.cpp @@ -60,6 +60,7 @@ void DSDDemodSettings::resetToDefaults() m_reverseAPIDeviceIndex = 0; m_reverseAPIChannelIndex = 0; m_workspaceIndex = 0; + m_hidden = false; } QByteArray DSDDemodSettings::serialize() const @@ -105,6 +106,7 @@ QByteArray DSDDemodSettings::serialize() const s.writeS32(32, m_workspaceIndex); s.writeBlob(33, m_geometryBytes); + s.writeBool(34, m_hidden); return s.final(); } @@ -187,6 +189,7 @@ bool DSDDemodSettings::deserialize(const QByteArray& data) d.readS32(32, &m_workspaceIndex, 0); d.readBlob(33, &m_geometryBytes); + d.readBool(34, &m_hidden, false); return true; } diff --git a/plugins/channelrx/demoddsd/dsddemodsettings.h b/plugins/channelrx/demoddsd/dsddemodsettings.h index 3c875edea..3339be650 100644 --- a/plugins/channelrx/demoddsd/dsddemodsettings.h +++ b/plugins/channelrx/demoddsd/dsddemodsettings.h @@ -54,6 +54,7 @@ struct DSDDemodSettings uint16_t m_reverseAPIChannelIndex; int m_workspaceIndex; QByteArray m_geometryBytes; + bool m_hidden; Serializable *m_channelMarker; Serializable *m_rollupState; diff --git a/plugins/channelrx/demodfreedv/freedvdemodgui.h b/plugins/channelrx/demodfreedv/freedvdemodgui.h index 655e2115f..f50c72fca 100644 --- a/plugins/channelrx/demodfreedv/freedvdemodgui.h +++ b/plugins/channelrx/demodfreedv/freedvdemodgui.h @@ -56,6 +56,8 @@ public: virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; }; virtual QString getTitle() const { return m_settings.m_title; }; virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; + virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } + virtual bool getHidden() const { return m_settings.m_hidden; } public slots: void channelMarkerChangedByCursor(); diff --git a/plugins/channelrx/demodfreedv/freedvdemodsettings.cpp b/plugins/channelrx/demodfreedv/freedvdemodsettings.cpp index ff0b51613..26a2de4b1 100644 --- a/plugins/channelrx/demodfreedv/freedvdemodsettings.cpp +++ b/plugins/channelrx/demodfreedv/freedvdemodsettings.cpp @@ -57,6 +57,7 @@ void FreeDVDemodSettings::resetToDefaults() m_reverseAPIDeviceIndex = 0; m_reverseAPIChannelIndex = 0; m_workspaceIndex = 0; + m_hidden = false; } QByteArray FreeDVDemodSettings::serialize() const @@ -89,6 +90,7 @@ QByteArray FreeDVDemodSettings::serialize() const s.writeS32(26, m_workspaceIndex); s.writeBlob(27, m_geometryBytes); + s.writeBool(28, m_hidden); return s.final(); } @@ -160,6 +162,7 @@ bool FreeDVDemodSettings::deserialize(const QByteArray& data) d.readS32(26, &m_workspaceIndex, 0); d.readBlob(27, &m_geometryBytes); + d.readBool(28, &m_hidden, false); return true; } diff --git a/plugins/channelrx/demodfreedv/freedvdemodsettings.h b/plugins/channelrx/demodfreedv/freedvdemodsettings.h index 84b4fca3a..293d94747 100644 --- a/plugins/channelrx/demodfreedv/freedvdemodsettings.h +++ b/plugins/channelrx/demodfreedv/freedvdemodsettings.h @@ -53,6 +53,7 @@ struct FreeDVDemodSettings uint16_t m_reverseAPIChannelIndex; int m_workspaceIndex; QByteArray m_geometryBytes; + bool m_hidden; Serializable *m_channelMarker; Serializable *m_spectrumGUI; diff --git a/plugins/channelrx/demodnfm/nfmdemodgui.h b/plugins/channelrx/demodnfm/nfmdemodgui.h index 1e82aed46..0514d59aa 100644 --- a/plugins/channelrx/demodnfm/nfmdemodgui.h +++ b/plugins/channelrx/demodnfm/nfmdemodgui.h @@ -36,6 +36,8 @@ public: virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; }; virtual QString getTitle() const { return m_settings.m_title; }; virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; + virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } + virtual bool getHidden() const { return m_settings.m_hidden; } public slots: void channelMarkerChangedByCursor(); diff --git a/plugins/channelrx/demodnfm/nfmdemodsettings.cpp b/plugins/channelrx/demodnfm/nfmdemodsettings.cpp index 07160b45c..a73462ae2 100644 --- a/plugins/channelrx/demodnfm/nfmdemodsettings.cpp +++ b/plugins/channelrx/demodnfm/nfmdemodsettings.cpp @@ -75,6 +75,7 @@ void NFMDemodSettings::resetToDefaults() m_reverseAPIDeviceIndex = 0; m_reverseAPIChannelIndex = 0; m_workspaceIndex = 0; + m_hidden = false; } QByteArray NFMDemodSettings::serialize() const @@ -116,6 +117,7 @@ QByteArray NFMDemodSettings::serialize() const s.writeS32(27, m_workspaceIndex); s.writeBlob(28, m_geometryBytes); + s.writeBool(29, m_hidden); return s.final(); } @@ -188,6 +190,7 @@ bool NFMDemodSettings::deserialize(const QByteArray& data) d.readS32(27, &m_workspaceIndex, 0); d.readBlob(28, &m_geometryBytes); + d.readBool(29, &m_hidden, false); return true; } diff --git a/plugins/channelrx/demodnfm/nfmdemodsettings.h b/plugins/channelrx/demodnfm/nfmdemodsettings.h index fd7da3321..b20fa3a2b 100644 --- a/plugins/channelrx/demodnfm/nfmdemodsettings.h +++ b/plugins/channelrx/demodnfm/nfmdemodsettings.h @@ -56,6 +56,7 @@ struct NFMDemodSettings uint16_t m_reverseAPIChannelIndex; int m_workspaceIndex; QByteArray m_geometryBytes; + bool m_hidden; Serializable *m_channelMarker; Serializable *m_rollupState; diff --git a/plugins/channelrx/demodpacket/packetdemodgui.h b/plugins/channelrx/demodpacket/packetdemodgui.h index 6004a4cad..56fe1e3d7 100644 --- a/plugins/channelrx/demodpacket/packetdemodgui.h +++ b/plugins/channelrx/demodpacket/packetdemodgui.h @@ -64,6 +64,8 @@ public: virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; }; virtual QString getTitle() const { return m_settings.m_title; }; virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; + virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } + virtual bool getHidden() const { return m_settings.m_hidden; } public slots: void channelMarkerChangedByCursor(); diff --git a/plugins/channelrx/demodpacket/packetdemodsettings.cpp b/plugins/channelrx/demodpacket/packetdemodsettings.cpp index 0aa16157b..93abb748e 100644 --- a/plugins/channelrx/demodpacket/packetdemodsettings.cpp +++ b/plugins/channelrx/demodpacket/packetdemodsettings.cpp @@ -54,6 +54,7 @@ void PacketDemodSettings::resetToDefaults() m_reverseAPIDeviceIndex = 0; m_reverseAPIChannelIndex = 0; m_workspaceIndex = 0; + m_hidden = false; for (int i = 0; i < PACKETDEMOD_COLUMNS; i++) { @@ -98,6 +99,7 @@ QByteArray PacketDemodSettings::serialize() const s.writeS32(28, m_workspaceIndex); s.writeBlob(29, m_geometryBytes); + s.writeBool(30, m_hidden); for (int i = 0; i < PACKETDEMOD_COLUMNS; i++) { s.writeS32(100 + i, m_columnIndexes[i]); @@ -179,6 +181,7 @@ bool PacketDemodSettings::deserialize(const QByteArray& data) d.readS32(28, &m_workspaceIndex, 0); d.readBlob(29, &m_geometryBytes); + d.readBool(30, &m_hidden, false); for (int i = 0; i < PACKETDEMOD_COLUMNS; i++) { d.readS32(100 + i, &m_columnIndexes[i], i); diff --git a/plugins/channelrx/demodpacket/packetdemodsettings.h b/plugins/channelrx/demodpacket/packetdemodsettings.h index 77be10352..07bd5f2ca 100644 --- a/plugins/channelrx/demodpacket/packetdemodsettings.h +++ b/plugins/channelrx/demodpacket/packetdemodsettings.h @@ -59,6 +59,7 @@ struct PacketDemodSettings Serializable *m_rollupState; int m_workspaceIndex; QByteArray m_geometryBytes; + bool m_hidden; int m_columnIndexes[PACKETDEMOD_COLUMNS];//!< How the columns are ordered in the table int m_columnSizes[PACKETDEMOD_COLUMNS]; //!< Size of the columns in the table diff --git a/plugins/channelrx/demodpager/pagerdemodgui.h b/plugins/channelrx/demodpager/pagerdemodgui.h index d17093c8a..4549f8078 100644 --- a/plugins/channelrx/demodpager/pagerdemodgui.h +++ b/plugins/channelrx/demodpager/pagerdemodgui.h @@ -58,6 +58,8 @@ public: virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; }; virtual QString getTitle() const { return m_settings.m_title; }; virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; + virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } + virtual bool getHidden() const { return m_settings.m_hidden; } public slots: void channelMarkerChangedByCursor(); diff --git a/plugins/channelrx/demodpager/pagerdemodsettings.cpp b/plugins/channelrx/demodpager/pagerdemodsettings.cpp index c2fdbb889..76e6a5743 100644 --- a/plugins/channelrx/demodpager/pagerdemodsettings.cpp +++ b/plugins/channelrx/demodpager/pagerdemodsettings.cpp @@ -57,6 +57,7 @@ void PagerDemodSettings::resetToDefaults() m_reverseAPIChannelIndex = 0; m_reverse = false; m_workspaceIndex = 0; + m_hidden = false; for (int i = 0; i < PAGERDEMOD_MESSAGE_COLUMNS; i++) { @@ -106,6 +107,7 @@ QByteArray PagerDemodSettings::serialize() const s.writeS32(28, m_workspaceIndex); s.writeBlob(29, m_geometryBytes); + s.writeBool(30, m_hidden); for (int i = 0; i < PAGERDEMOD_MESSAGE_COLUMNS; i++) { s.writeS32(100 + i, m_messageColumnIndexes[i]); @@ -200,6 +202,7 @@ bool PagerDemodSettings::deserialize(const QByteArray& data) d.readS32(28, &m_workspaceIndex, 0); d.readBlob(29, &m_geometryBytes); + d.readBool(30, &m_hidden, false); for (int i = 0; i < PAGERDEMOD_MESSAGE_COLUMNS; i++) { d.readS32(100 + i, &m_messageColumnIndexes[i], i); diff --git a/plugins/channelrx/demodpager/pagerdemodsettings.h b/plugins/channelrx/demodpager/pagerdemodsettings.h index a56dcd916..430162214 100644 --- a/plugins/channelrx/demodpager/pagerdemodsettings.h +++ b/plugins/channelrx/demodpager/pagerdemodsettings.h @@ -69,6 +69,7 @@ struct PagerDemodSettings Serializable *m_rollupState; int m_workspaceIndex; QByteArray m_geometryBytes; + bool m_hidden; int m_messageColumnIndexes[PAGERDEMOD_MESSAGE_COLUMNS];//!< How the columns are ordered in the table int m_messageColumnSizes[PAGERDEMOD_MESSAGE_COLUMNS]; //!< Size of the columns in the table diff --git a/plugins/channelrx/demodradiosonde/radiosondedemodgui.h b/plugins/channelrx/demodradiosonde/radiosondedemodgui.h index 4db42e013..173a7a74b 100644 --- a/plugins/channelrx/demodradiosonde/radiosondedemodgui.h +++ b/plugins/channelrx/demodradiosonde/radiosondedemodgui.h @@ -60,6 +60,8 @@ public: virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; }; virtual QString getTitle() const { return m_settings.m_title; }; virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; + virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } + virtual bool getHidden() const { return m_settings.m_hidden; } public slots: void channelMarkerChangedByCursor(); diff --git a/plugins/channelrx/demodradiosonde/radiosondedemodsettings.cpp b/plugins/channelrx/demodradiosonde/radiosondedemodsettings.cpp index 64f9d163a..0f97da8dd 100644 --- a/plugins/channelrx/demodradiosonde/radiosondedemodsettings.cpp +++ b/plugins/channelrx/demodradiosonde/radiosondedemodsettings.cpp @@ -55,6 +55,7 @@ void RadiosondeDemodSettings::resetToDefaults() m_reverseAPIDeviceIndex = 0; m_reverseAPIChannelIndex = 0; m_workspaceIndex = 0; + m_hidden = false; for (int i = 0; i < RADIOSONDEDEMOD_FRAME_COLUMNS; i++) { @@ -101,6 +102,7 @@ QByteArray RadiosondeDemodSettings::serialize() const s.writeS32(26, m_workspaceIndex); s.writeBlob(27, m_geometryBytes); + s.writeBool(28, m_hidden); for (int i = 0; i < RADIOSONDEDEMOD_FRAME_COLUMNS; i++) s.writeS32(100 + i, m_frameColumnIndexes[i]); @@ -186,6 +188,7 @@ bool RadiosondeDemodSettings::deserialize(const QByteArray& data) d.readS32(26, &m_workspaceIndex, 0); d.readBlob(27, &m_geometryBytes); + d.readBool(28, &m_hidden, false); for (int i = 0; i < RADIOSONDEDEMOD_FRAME_COLUMNS; i++) { d.readS32(100 + i, &m_frameColumnIndexes[i], i); diff --git a/plugins/channelrx/demodradiosonde/radiosondedemodsettings.h b/plugins/channelrx/demodradiosonde/radiosondedemodsettings.h index 416dbf7f7..46b5f2142 100644 --- a/plugins/channelrx/demodradiosonde/radiosondedemodsettings.h +++ b/plugins/channelrx/demodradiosonde/radiosondedemodsettings.h @@ -59,6 +59,7 @@ struct RadiosondeDemodSettings Serializable *m_rollupState; int m_workspaceIndex; QByteArray m_geometryBytes; + bool m_hidden; int m_frameColumnIndexes[RADIOSONDEDEMOD_FRAME_COLUMNS];//!< How the columns are ordered in the table int m_frameColumnSizes[RADIOSONDEDEMOD_FRAME_COLUMNS]; //!< Size of the columns in the table diff --git a/plugins/channelrx/demodssb/ssbdemodgui.h b/plugins/channelrx/demodssb/ssbdemodgui.h index b7fca1d20..c520777fc 100644 --- a/plugins/channelrx/demodssb/ssbdemodgui.h +++ b/plugins/channelrx/demodssb/ssbdemodgui.h @@ -39,6 +39,8 @@ public: virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; }; virtual QString getTitle() const { return m_settings.m_title; }; virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; + virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } + virtual bool getHidden() const { return m_settings.m_hidden; } public slots: void channelMarkerChangedByCursor(); diff --git a/plugins/channelrx/demodssb/ssbdemodsettings.cpp b/plugins/channelrx/demodssb/ssbdemodsettings.cpp index 476f88930..3eb35e735 100644 --- a/plugins/channelrx/demodssb/ssbdemodsettings.cpp +++ b/plugins/channelrx/demodssb/ssbdemodsettings.cpp @@ -64,6 +64,7 @@ void SSBDemodSettings::resetToDefaults() m_reverseAPIDeviceIndex = 0; m_reverseAPIChannelIndex = 0; m_workspaceIndex = 0; + m_hidden = false; } QByteArray SSBDemodSettings::serialize() const @@ -103,6 +104,7 @@ QByteArray SSBDemodSettings::serialize() const s.writeS32(25, m_workspaceIndex); s.writeBlob(26, m_geometryBytes); + s.writeBool(27, m_hidden); return s.final(); } @@ -174,6 +176,7 @@ bool SSBDemodSettings::deserialize(const QByteArray& data) d.readS32(25, &m_workspaceIndex, 0); d.readBlob(26, &m_geometryBytes); + d.readBool(27, &m_hidden, false); return true; } diff --git a/plugins/channelrx/demodssb/ssbdemodsettings.h b/plugins/channelrx/demodssb/ssbdemodsettings.h index 9ecd1a637..d1c51695b 100644 --- a/plugins/channelrx/demodssb/ssbdemodsettings.h +++ b/plugins/channelrx/demodssb/ssbdemodsettings.h @@ -49,6 +49,7 @@ struct SSBDemodSettings uint16_t m_reverseAPIChannelIndex; int m_workspaceIndex; QByteArray m_geometryBytes; + bool m_hidden; Serializable *m_channelMarker; Serializable *m_spectrumGUI; diff --git a/plugins/channelrx/demodvor/vordemodgui.h b/plugins/channelrx/demodvor/vordemodgui.h index 950d040d6..a1909572a 100644 --- a/plugins/channelrx/demodvor/vordemodgui.h +++ b/plugins/channelrx/demodvor/vordemodgui.h @@ -217,6 +217,8 @@ public: virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; }; virtual QString getTitle() const { return m_settings.m_title; }; virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; + virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } + virtual bool getHidden() const { return m_settings.m_hidden; } void selectVOR(VORGUI *vorGUI, bool selected); diff --git a/plugins/channelrx/demodvor/vordemodsettings.cpp b/plugins/channelrx/demodvor/vordemodsettings.cpp index 68d7c8c8d..97f28663e 100644 --- a/plugins/channelrx/demodvor/vordemodsettings.cpp +++ b/plugins/channelrx/demodvor/vordemodsettings.cpp @@ -45,6 +45,7 @@ void VORDemodSettings::resetToDefaults() m_reverseAPIDeviceIndex = 0; m_reverseAPIChannelIndex = 0; m_workspaceIndex = 0; + m_hidden = false; m_identThreshold = 2.0; m_refThresholdDB = -45.0; @@ -88,6 +89,7 @@ QByteArray VORDemodSettings::serialize() const s.writeS32(25, m_workspaceIndex); s.writeBlob(26, m_geometryBytes); + s.writeBool(27, m_hidden); for (int i = 0; i < VORDEMOD_COLUMNS; i++) { s.writeS32(100 + i, m_columnIndexes[i]); @@ -159,6 +161,7 @@ bool VORDemodSettings::deserialize(const QByteArray& data) d.readS32(25, &m_workspaceIndex, 0); d.readBlob(26, &m_geometryBytes); + d.readBool(27, &m_hidden, false); for (int i = 0; i < VORDEMOD_COLUMNS; i++) { d.readS32(100 + i, &m_columnIndexes[i], i); diff --git a/plugins/channelrx/demodvor/vordemodsettings.h b/plugins/channelrx/demodvor/vordemodsettings.h index 4f121a2ec..af755e360 100644 --- a/plugins/channelrx/demodvor/vordemodsettings.h +++ b/plugins/channelrx/demodvor/vordemodsettings.h @@ -56,6 +56,7 @@ struct VORDemodSettings Serializable *m_rollupState; int m_workspaceIndex; QByteArray m_geometryBytes; + bool m_hidden; int m_columnIndexes[VORDEMOD_COLUMNS];//!< How the columns are ordered in the table int m_columnSizes[VORDEMOD_COLUMNS]; //!< Size of the coumns in the table diff --git a/plugins/channelrx/demodvorsc/vordemodscgui.h b/plugins/channelrx/demodvorsc/vordemodscgui.h index 460cddd2d..c8fc4b8aa 100644 --- a/plugins/channelrx/demodvorsc/vordemodscgui.h +++ b/plugins/channelrx/demodvorsc/vordemodscgui.h @@ -54,6 +54,8 @@ public: virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; }; virtual QString getTitle() const { return m_settings.m_title; }; virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; + virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } + virtual bool getHidden() const { return m_settings.m_hidden; } public slots: void channelMarkerChangedByCursor(); diff --git a/plugins/channelrx/demodvorsc/vordemodscsettings.cpp b/plugins/channelrx/demodvorsc/vordemodscsettings.cpp index 5f1cd2eb5..e4380ad4a 100644 --- a/plugins/channelrx/demodvorsc/vordemodscsettings.cpp +++ b/plugins/channelrx/demodvorsc/vordemodscsettings.cpp @@ -47,6 +47,7 @@ void VORDemodSCSettings::resetToDefaults() m_reverseAPIDeviceIndex = 0; m_reverseAPIChannelIndex = 0; m_workspaceIndex = 0; + m_hidden = false; m_identThreshold = 2.0; m_refThresholdDB = -45.0; @@ -83,6 +84,7 @@ QByteArray VORDemodSCSettings::serialize() const s.writeS32(24, m_workspaceIndex); s.writeBlob(25, m_geometryBytes); + s.writeBool(26, m_hidden); return s.final(); } @@ -146,6 +148,7 @@ bool VORDemodSCSettings::deserialize(const QByteArray& data) d.readS32(24, &m_workspaceIndex, 0); d.readBlob(25, &m_geometryBytes); + d.readBool(26, &m_hidden, false); return true; } diff --git a/plugins/channelrx/demodvorsc/vordemodscsettings.h b/plugins/channelrx/demodvorsc/vordemodscsettings.h index a5e1ac962..8e19f5483 100644 --- a/plugins/channelrx/demodvorsc/vordemodscsettings.h +++ b/plugins/channelrx/demodvorsc/vordemodscsettings.h @@ -48,6 +48,7 @@ struct VORDemodSCSettings Serializable *m_rollupState; int m_workspaceIndex; QByteArray m_geometryBytes; + bool m_hidden; // Highest frequency is the FM subcarrier at up to ~11kHz // However, old VORs can have 0.005% frequency offset, which is 6kHz diff --git a/plugins/channelrx/demodwfm/wfmdemodgui.h b/plugins/channelrx/demodwfm/wfmdemodgui.h index dfd2c3502..5894b8253 100644 --- a/plugins/channelrx/demodwfm/wfmdemodgui.h +++ b/plugins/channelrx/demodwfm/wfmdemodgui.h @@ -34,6 +34,8 @@ public: virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; }; virtual QString getTitle() const { return m_settings.m_title; }; virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; + virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } + virtual bool getHidden() const { return m_settings.m_hidden; } public slots: void channelMarkerChangedByCursor(); diff --git a/plugins/channelrx/demodwfm/wfmdemodsettings.cpp b/plugins/channelrx/demodwfm/wfmdemodsettings.cpp index 779096e82..f0b778616 100644 --- a/plugins/channelrx/demodwfm/wfmdemodsettings.cpp +++ b/plugins/channelrx/demodwfm/wfmdemodsettings.cpp @@ -53,6 +53,7 @@ void WFMDemodSettings::resetToDefaults() m_reverseAPIDeviceIndex = 0; m_reverseAPIChannelIndex = 0; m_workspaceIndex = 0; + m_hidden = false; } QByteArray WFMDemodSettings::serialize() const @@ -84,6 +85,7 @@ QByteArray WFMDemodSettings::serialize() const s.writeS32(19, m_workspaceIndex); s.writeBlob(20, m_geometryBytes); + s.writeBool(21, m_hidden); return s.final(); } @@ -149,6 +151,7 @@ bool WFMDemodSettings::deserialize(const QByteArray& data) d.readS32(19, &m_workspaceIndex, 0); d.readBlob(20, &m_geometryBytes); + d.readBool(21, &m_hidden, false); return true; } diff --git a/plugins/channelrx/demodwfm/wfmdemodsettings.h b/plugins/channelrx/demodwfm/wfmdemodsettings.h index 4c98cbb4e..e839fc124 100644 --- a/plugins/channelrx/demodwfm/wfmdemodsettings.h +++ b/plugins/channelrx/demodwfm/wfmdemodsettings.h @@ -42,6 +42,7 @@ struct WFMDemodSettings uint16_t m_reverseAPIChannelIndex; int m_workspaceIndex; QByteArray m_geometryBytes; + bool m_hidden; Serializable *m_channelMarker; Serializable *m_rollupState; diff --git a/plugins/channelrx/filesink/filesinkgui.h b/plugins/channelrx/filesink/filesinkgui.h index bddedd7bb..daec1f789 100644 --- a/plugins/channelrx/filesink/filesinkgui.h +++ b/plugins/channelrx/filesink/filesinkgui.h @@ -55,6 +55,8 @@ public: virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; }; virtual QString getTitle() const { return m_settings.m_title; }; virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; + virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } + virtual bool getHidden() const { return m_settings.m_hidden; } public slots: void channelMarkerChangedByCursor(); diff --git a/plugins/channelrx/filesink/filesinksettings.cpp b/plugins/channelrx/filesink/filesinksettings.cpp index 4730701b4..8726054a7 100644 --- a/plugins/channelrx/filesink/filesinksettings.cpp +++ b/plugins/channelrx/filesink/filesinksettings.cpp @@ -50,6 +50,7 @@ void FileSinkSettings::resetToDefaults() m_reverseAPIDeviceIndex = 0; m_reverseAPIChannelIndex = 0; m_workspaceIndex = 0; + m_hidden = false; } QByteArray FileSinkSettings::serialize() const @@ -88,6 +89,7 @@ QByteArray FileSinkSettings::serialize() const s.writeS32(20, m_workspaceIndex); s.writeBlob(21, m_geometryBytes); + s.writeBool(22, m_hidden); return s.final(); } @@ -159,6 +161,7 @@ bool FileSinkSettings::deserialize(const QByteArray& data) d.readS32(20, &m_workspaceIndex, 0); d.readBlob(21, &m_geometryBytes); + d.readBool(22, &m_hidden, false); return true; } diff --git a/plugins/channelrx/filesink/filesinksettings.h b/plugins/channelrx/filesink/filesinksettings.h index 08bc92062..4c43d43fb 100644 --- a/plugins/channelrx/filesink/filesinksettings.h +++ b/plugins/channelrx/filesink/filesinksettings.h @@ -43,6 +43,7 @@ struct FileSinkSettings uint16_t m_reverseAPIChannelIndex; int m_workspaceIndex; QByteArray m_geometryBytes; + bool m_hidden; Serializable *m_spectrumGUI; Serializable *m_channelMarker; diff --git a/plugins/channelrx/freqtracker/freqtrackergui.h b/plugins/channelrx/freqtracker/freqtrackergui.h index 310ddb19f..81990dd69 100644 --- a/plugins/channelrx/freqtracker/freqtrackergui.h +++ b/plugins/channelrx/freqtracker/freqtrackergui.h @@ -55,6 +55,8 @@ public: virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; }; virtual QString getTitle() const { return m_settings.m_title; }; virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; + virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } + virtual bool getHidden() const { return m_settings.m_hidden; } public slots: void channelMarkerChangedByCursor(); diff --git a/plugins/channelrx/freqtracker/freqtrackersettings.cpp b/plugins/channelrx/freqtracker/freqtrackersettings.cpp index 9ac8e1e05..300e1282e 100644 --- a/plugins/channelrx/freqtracker/freqtrackersettings.cpp +++ b/plugins/channelrx/freqtracker/freqtrackersettings.cpp @@ -53,6 +53,7 @@ void FreqTrackerSettings::resetToDefaults() m_reverseAPIDeviceIndex = 0; m_reverseAPIChannelIndex = 0; m_workspaceIndex = 0; + m_hidden = false; } QByteArray FreqTrackerSettings::serialize() const @@ -94,6 +95,7 @@ QByteArray FreqTrackerSettings::serialize() const s.writeS32(24, m_workspaceIndex); s.writeBlob(25, m_geometryBytes); + s.writeBool(26, m_hidden); return s.final(); } @@ -176,6 +178,7 @@ bool FreqTrackerSettings::deserialize(const QByteArray& data) d.readS32(24, &m_workspaceIndex, 0); d.readBlob(25, &m_geometryBytes); + d.readBool(26, &m_hidden, false); return true; } diff --git a/plugins/channelrx/freqtracker/freqtrackersettings.h b/plugins/channelrx/freqtracker/freqtrackersettings.h index 0e5734f66..1129aa3ea 100644 --- a/plugins/channelrx/freqtracker/freqtrackersettings.h +++ b/plugins/channelrx/freqtracker/freqtrackersettings.h @@ -59,6 +59,7 @@ struct FreqTrackerSettings Serializable *m_rollupState; int m_workspaceIndex; QByteArray m_geometryBytes; + bool m_hidden; FreqTrackerSettings(); void resetToDefaults(); diff --git a/plugins/channelrx/localsink/localsinkgui.h b/plugins/channelrx/localsink/localsinkgui.h index 285894aca..614f64dfd 100644 --- a/plugins/channelrx/localsink/localsinkgui.h +++ b/plugins/channelrx/localsink/localsinkgui.h @@ -54,6 +54,8 @@ public: virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; }; virtual QString getTitle() const { return m_settings.m_title; }; virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; + virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } + virtual bool getHidden() const { return m_settings.m_hidden; } private: Ui::LocalSinkGUI* ui; diff --git a/plugins/channelrx/localsink/localsinksettings.cpp b/plugins/channelrx/localsink/localsinksettings.cpp index d04577cfb..ecf2f9a48 100644 --- a/plugins/channelrx/localsink/localsinksettings.cpp +++ b/plugins/channelrx/localsink/localsinksettings.cpp @@ -45,6 +45,7 @@ void LocalSinkSettings::resetToDefaults() m_reverseAPIDeviceIndex = 0; m_reverseAPIChannelIndex = 0; m_workspaceIndex = 0; + m_hidden = false; } QByteArray LocalSinkSettings::serialize() const @@ -73,6 +74,7 @@ QByteArray LocalSinkSettings::serialize() const s.writeS32(16, m_workspaceIndex); s.writeBlob(17, m_geometryBytes); + s.writeBool(18, m_hidden); return s.final(); } @@ -130,6 +132,7 @@ bool LocalSinkSettings::deserialize(const QByteArray& data) d.readS32(16, &m_workspaceIndex, 0); d.readBlob(17, &m_geometryBytes); + d.readBool(18, &m_hidden, false); return true; } diff --git a/plugins/channelrx/localsink/localsinksettings.h b/plugins/channelrx/localsink/localsinksettings.h index 0dee85451..48a52beaa 100644 --- a/plugins/channelrx/localsink/localsinksettings.h +++ b/plugins/channelrx/localsink/localsinksettings.h @@ -39,6 +39,7 @@ struct LocalSinkSettings uint16_t m_reverseAPIChannelIndex; int m_workspaceIndex; QByteArray m_geometryBytes; + bool m_hidden; Serializable *m_channelMarker; Serializable *m_rollupState; diff --git a/plugins/channelrx/noisefigure/noisefiguregui.h b/plugins/channelrx/noisefigure/noisefiguregui.h index 29b7ba556..95981be46 100644 --- a/plugins/channelrx/noisefigure/noisefiguregui.h +++ b/plugins/channelrx/noisefigure/noisefiguregui.h @@ -61,6 +61,8 @@ public: virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; }; virtual QString getTitle() const { return m_settings.m_title; }; virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; + virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } + virtual bool getHidden() const { return m_settings.m_hidden; } public slots: void channelMarkerChangedByCursor(); diff --git a/plugins/channelrx/noisefigure/noisefiguresettings.cpp b/plugins/channelrx/noisefigure/noisefiguresettings.cpp index fe12e037b..27dccc40f 100644 --- a/plugins/channelrx/noisefigure/noisefiguresettings.cpp +++ b/plugins/channelrx/noisefigure/noisefiguresettings.cpp @@ -66,6 +66,7 @@ void NoiseFigureSettings::resetToDefaults() m_reverseAPIDeviceIndex = 0; m_reverseAPIChannelIndex = 0; m_workspaceIndex = 0; + m_hidden = false; for (int i = 0; i < NOISEFIGURE_COLUMNS; i++) { @@ -120,6 +121,7 @@ QByteArray NoiseFigureSettings::serialize() const s.writeS32(29, m_workspaceIndex); s.writeBlob(30, m_geometryBytes); + s.writeBool(31, m_hidden); for (int i = 0; i < NOISEFIGURE_COLUMNS; i++) { s.writeS32(100 + i, m_resultsColumnIndexes[i]); @@ -203,6 +205,7 @@ bool NoiseFigureSettings::deserialize(const QByteArray& data) d.readS32(29, &m_workspaceIndex, 0); d.readBlob(30, &m_geometryBytes); + d.readBool(31, &m_hidden, false); for (int i = 0; i < NOISEFIGURE_COLUMNS; i++) { d.readS32(100 + i, &m_resultsColumnIndexes[i], i); diff --git a/plugins/channelrx/noisefigure/noisefiguresettings.h b/plugins/channelrx/noisefigure/noisefiguresettings.h index f5f121227..a57b444f7 100644 --- a/plugins/channelrx/noisefigure/noisefiguresettings.h +++ b/plugins/channelrx/noisefigure/noisefiguresettings.h @@ -88,6 +88,7 @@ struct NoiseFigureSettings Serializable *m_rollupState; int m_workspaceIndex; QByteArray m_geometryBytes; + bool m_hidden; int m_resultsColumnIndexes[NOISEFIGURE_COLUMNS];//!< How the columns are ordered in the table int m_resultsColumnSizes[NOISEFIGURE_COLUMNS]; //!< Size of the columns in the table diff --git a/plugins/channelrx/radioastronomy/radioastronomygui.h b/plugins/channelrx/radioastronomy/radioastronomygui.h index 0c7e664d3..e40761d1c 100644 --- a/plugins/channelrx/radioastronomy/radioastronomygui.h +++ b/plugins/channelrx/radioastronomy/radioastronomygui.h @@ -204,6 +204,8 @@ public: virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; }; virtual QString getTitle() const { return m_settings.m_title; }; virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; + virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } + virtual bool getHidden() const { return m_settings.m_hidden; } public slots: void channelMarkerChangedByCursor(); diff --git a/plugins/channelrx/radioastronomy/radioastronomysettings.cpp b/plugins/channelrx/radioastronomy/radioastronomysettings.cpp index db2dfd132..66473becd 100644 --- a/plugins/channelrx/radioastronomy/radioastronomysettings.cpp +++ b/plugins/channelrx/radioastronomy/radioastronomysettings.cpp @@ -164,6 +164,7 @@ void RadioAstronomySettings::resetToDefaults() m_reverseAPIDeviceIndex = 0; m_reverseAPIChannelIndex = 0; m_workspaceIndex = 0; + m_hidden = false; for (int i = 0; i < RADIOASTRONOMY_POWERTABLE_COLUMNS; i++) { @@ -308,6 +309,7 @@ QByteArray RadioAstronomySettings::serialize() const s.writeU32(188, m_reverseAPIChannelIndex); s.writeS32(189, m_workspaceIndex); s.writeBlob(190, m_geometryBytes); + s.writeBool(191, m_hidden); for (int i = 0; i < RADIOASTRONOMY_POWERTABLE_COLUMNS; i++) { s.writeS32(400 + i, m_powerTableColumnIndexes[i]); @@ -477,6 +479,7 @@ bool RadioAstronomySettings::deserialize(const QByteArray& data) m_reverseAPIChannelIndex = utmp > 99 ? 99 : utmp; d.readS32(189, &m_workspaceIndex, 0); d.readBlob(190, &m_geometryBytes); + d.readBool(191, &m_hidden, false); for (int i = 0; i < RADIOASTRONOMY_POWERTABLE_COLUMNS; i++) { d.readS32(400 + i, &m_powerTableColumnIndexes[i], i); diff --git a/plugins/channelrx/radioastronomy/radioastronomysettings.h b/plugins/channelrx/radioastronomy/radioastronomysettings.h index cf222b5eb..f10ab2bd6 100644 --- a/plugins/channelrx/radioastronomy/radioastronomysettings.h +++ b/plugins/channelrx/radioastronomy/radioastronomysettings.h @@ -225,6 +225,7 @@ struct RadioAstronomySettings uint16_t m_reverseAPIChannelIndex; int m_workspaceIndex; QByteArray m_geometryBytes; + bool m_hidden; int m_powerTableColumnIndexes[RADIOASTRONOMY_POWERTABLE_COLUMNS];//!< How the columns are ordered in the table int m_powerTableColumnSizes[RADIOASTRONOMY_POWERTABLE_COLUMNS]; //!< Size of the columns in the table diff --git a/plugins/channelrx/radioclock/radioclockgui.h b/plugins/channelrx/radioclock/radioclockgui.h index 8d1956559..fe39104f9 100644 --- a/plugins/channelrx/radioclock/radioclockgui.h +++ b/plugins/channelrx/radioclock/radioclockgui.h @@ -57,6 +57,8 @@ public: virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; }; virtual QString getTitle() const { return m_settings.m_title; }; virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; + virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } + virtual bool getHidden() const { return m_settings.m_hidden; } public slots: void channelMarkerChangedByCursor(); diff --git a/plugins/channelrx/radioclock/radioclocksettings.cpp b/plugins/channelrx/radioclock/radioclocksettings.cpp index 5af35acf0..6d39d78be 100644 --- a/plugins/channelrx/radioclock/radioclocksettings.cpp +++ b/plugins/channelrx/radioclock/radioclocksettings.cpp @@ -47,6 +47,7 @@ void RadioClockSettings::resetToDefaults() m_reverseAPIDeviceIndex = 0; m_reverseAPIChannelIndex = 0; m_workspaceIndex = 0; + m_hidden = false; } QByteArray RadioClockSettings::serialize() const @@ -82,6 +83,7 @@ QByteArray RadioClockSettings::serialize() const s.writeS32(23, m_workspaceIndex); s.writeBlob(24, m_geometryBytes); + s.writeBool(25, m_hidden); return s.final(); } @@ -146,6 +148,7 @@ bool RadioClockSettings::deserialize(const QByteArray& data) d.readS32(23, &m_workspaceIndex, 0); d.readBlob(24, &m_geometryBytes); + d.readBool(25, &m_hidden, false); return true; } diff --git a/plugins/channelrx/radioclock/radioclocksettings.h b/plugins/channelrx/radioclock/radioclocksettings.h index 80f01ada1..8fde86bd0 100644 --- a/plugins/channelrx/radioclock/radioclocksettings.h +++ b/plugins/channelrx/radioclock/radioclocksettings.h @@ -56,6 +56,7 @@ struct RadioClockSettings Serializable *m_rollupState; int m_workspaceIndex; QByteArray m_geometryBytes; + bool m_hidden; static const int RADIOCLOCK_CHANNEL_SAMPLE_RATE = 1000; static const int m_scopeStreams = 8; diff --git a/plugins/channelrx/remotesink/remotesinkgui.h b/plugins/channelrx/remotesink/remotesinkgui.h index 5c5104a48..10695448c 100644 --- a/plugins/channelrx/remotesink/remotesinkgui.h +++ b/plugins/channelrx/remotesink/remotesinkgui.h @@ -54,6 +54,8 @@ public: virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; } virtual QString getTitle() const { return m_settings.m_title; } virtual QColor getTitleColor() const { return m_settings.m_rgbColor; } + virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } + virtual bool getHidden() const { return m_settings.m_hidden; } private: Ui::RemoteSinkGUI* ui; diff --git a/plugins/channelrx/remotesink/remotesinksettings.cpp b/plugins/channelrx/remotesink/remotesinksettings.cpp index 5cacc97b0..69c507638 100644 --- a/plugins/channelrx/remotesink/remotesinksettings.cpp +++ b/plugins/channelrx/remotesink/remotesinksettings.cpp @@ -54,6 +54,7 @@ void RemoteSinkSettings::resetToDefaults() m_reverseAPIDeviceIndex = 0; m_reverseAPIChannelIndex = 0; m_workspaceIndex = 0; + m_hidden = false; } QByteArray RemoteSinkSettings::serialize() const @@ -86,6 +87,7 @@ QByteArray RemoteSinkSettings::serialize() const s.writeS32(18, m_workspaceIndex); s.writeBlob(19, m_geometryBytes); + s.writeBool(20, m_hidden); return s.final(); } @@ -161,6 +163,7 @@ bool RemoteSinkSettings::deserialize(const QByteArray& data) d.readS32(18, &m_workspaceIndex, 0); d.readBlob(19, &m_geometryBytes); + d.readBool(20, &m_hidden, false); return true; } diff --git a/plugins/channelrx/remotesink/remotesinksettings.h b/plugins/channelrx/remotesink/remotesinksettings.h index 0804097c1..6284d41f3 100644 --- a/plugins/channelrx/remotesink/remotesinksettings.h +++ b/plugins/channelrx/remotesink/remotesinksettings.h @@ -48,6 +48,7 @@ struct RemoteSinkSettings uint16_t m_reverseAPIChannelIndex; int m_workspaceIndex; QByteArray m_geometryBytes; + bool m_hidden; Serializable *m_channelMarker; Serializable *m_rollupState; diff --git a/plugins/channelrx/sigmffilesink/sigmffilesinkgui.h b/plugins/channelrx/sigmffilesink/sigmffilesinkgui.h index 8aadef404..687dc3bf2 100644 --- a/plugins/channelrx/sigmffilesink/sigmffilesinkgui.h +++ b/plugins/channelrx/sigmffilesink/sigmffilesinkgui.h @@ -55,6 +55,8 @@ public: virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; }; virtual QString getTitle() const { return m_settings.m_title; }; virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; + virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } + virtual bool getHidden() const { return m_settings.m_hidden; } public slots: void channelMarkerChangedByCursor(); diff --git a/plugins/channelrx/sigmffilesink/sigmffilesinksettings.cpp b/plugins/channelrx/sigmffilesink/sigmffilesinksettings.cpp index 0ea2a2b39..3bbf9060b 100644 --- a/plugins/channelrx/sigmffilesink/sigmffilesinksettings.cpp +++ b/plugins/channelrx/sigmffilesink/sigmffilesinksettings.cpp @@ -50,6 +50,7 @@ void SigMFFileSinkSettings::resetToDefaults() m_reverseAPIDeviceIndex = 0; m_reverseAPIChannelIndex = 0; m_workspaceIndex = 0; + m_hidden = false; } QByteArray SigMFFileSinkSettings::serialize() const @@ -88,6 +89,7 @@ QByteArray SigMFFileSinkSettings::serialize() const s.writeS32(21, m_workspaceIndex); s.writeBlob(22, m_geometryBytes); + s.writeBool(23, m_hidden); return s.final(); } @@ -159,6 +161,7 @@ bool SigMFFileSinkSettings::deserialize(const QByteArray& data) d.readS32(21, &m_workspaceIndex, 0); d.readBlob(22, &m_geometryBytes); + d.readBool(23, &m_hidden, false); return true; } diff --git a/plugins/channelrx/sigmffilesink/sigmffilesinksettings.h b/plugins/channelrx/sigmffilesink/sigmffilesinksettings.h index 470f00b15..4d3075dde 100644 --- a/plugins/channelrx/sigmffilesink/sigmffilesinksettings.h +++ b/plugins/channelrx/sigmffilesink/sigmffilesinksettings.h @@ -44,6 +44,7 @@ struct SigMFFileSinkSettings uint16_t m_reverseAPIChannelIndex; int m_workspaceIndex; QByteArray m_geometryBytes; + bool m_hidden; Serializable *m_spectrumGUI; Serializable *m_channelMarker; diff --git a/plugins/channelrx/udpsink/udpsinkgui.h b/plugins/channelrx/udpsink/udpsinkgui.h index c06e0f99b..8c58d6dd7 100644 --- a/plugins/channelrx/udpsink/udpsinkgui.h +++ b/plugins/channelrx/udpsink/udpsinkgui.h @@ -55,6 +55,8 @@ public: virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; }; virtual QString getTitle() const { return m_settings.m_title; }; virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; + virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } + virtual bool getHidden() const { return m_settings.m_hidden; } public slots: void channelMarkerChangedByCursor(); diff --git a/plugins/channelrx/udpsink/udpsinksettings.cpp b/plugins/channelrx/udpsink/udpsinksettings.cpp index 385db5077..ea6e1eee0 100644 --- a/plugins/channelrx/udpsink/udpsinksettings.cpp +++ b/plugins/channelrx/udpsink/udpsinksettings.cpp @@ -58,6 +58,7 @@ void UDPSinkSettings::resetToDefaults() m_reverseAPIDeviceIndex = 0; m_reverseAPIChannelIndex = 0; m_workspaceIndex = 0; + m_hidden = false; } QByteArray UDPSinkSettings::serialize() const @@ -102,6 +103,7 @@ QByteArray UDPSinkSettings::serialize() const s.writeS32(30, m_workspaceIndex); s.writeBlob(31, m_geometryBytes); + s.writeBool(32, m_hidden); return s.final(); @@ -201,8 +203,9 @@ bool UDPSinkSettings::deserialize(const QByteArray& data) m_rollupState->deserialize(bytetmp); } - d.readS32(29, &m_workspaceIndex, 0); - d.readBlob(30, &m_geometryBytes); + d.readS32(30, &m_workspaceIndex, 0); + d.readBlob(31, &m_geometryBytes); + d.readBool(32, &m_hidden, false); return true; } diff --git a/plugins/channelrx/udpsink/udpsinksettings.h b/plugins/channelrx/udpsink/udpsinksettings.h index 72196f7ba..3f0b869a6 100644 --- a/plugins/channelrx/udpsink/udpsinksettings.h +++ b/plugins/channelrx/udpsink/udpsinksettings.h @@ -71,6 +71,7 @@ struct UDPSinkSettings uint16_t m_reverseAPIChannelIndex; int m_workspaceIndex; QByteArray m_geometryBytes; + bool m_hidden; Serializable *m_channelMarker; Serializable *m_spectrumGUI; diff --git a/plugins/channeltx/filesource/filesourcegui.h b/plugins/channeltx/filesource/filesourcegui.h index c748afd31..a270bdbae 100644 --- a/plugins/channeltx/filesource/filesourcegui.h +++ b/plugins/channeltx/filesource/filesourcegui.h @@ -51,6 +51,8 @@ public: virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; }; virtual QString getTitle() const { return m_settings.m_title; }; virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; + virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } + virtual bool getHidden() const { return m_settings.m_hidden; } public slots: void channelMarkerChangedByCursor(); diff --git a/plugins/channeltx/filesource/filesourcesettings.cpp b/plugins/channeltx/filesource/filesourcesettings.cpp index b249ae363..0a181dd36 100644 --- a/plugins/channeltx/filesource/filesourcesettings.cpp +++ b/plugins/channeltx/filesource/filesourcesettings.cpp @@ -46,6 +46,7 @@ void FileSourceSettings::resetToDefaults() m_reverseAPIDeviceIndex = 0; m_reverseAPIChannelIndex = 0; m_workspaceIndex = 0; + m_hidden = false; } QByteArray FileSourceSettings::serialize() const @@ -75,6 +76,7 @@ QByteArray FileSourceSettings::serialize() const s.writeS32(16, m_workspaceIndex); s.writeBlob(17, m_geometryBytes); + s.writeBool(18, m_hidden); return s.final(); } @@ -135,6 +137,7 @@ bool FileSourceSettings::deserialize(const QByteArray& data) d.readS32(16, &m_workspaceIndex, 0); d.readBlob(17, &m_geometryBytes); + d.readBool(18, &m_hidden, false); return true; } diff --git a/plugins/channeltx/filesource/filesourcesettings.h b/plugins/channeltx/filesource/filesourcesettings.h index f20a0377f..34c5cf882 100644 --- a/plugins/channeltx/filesource/filesourcesettings.h +++ b/plugins/channeltx/filesource/filesourcesettings.h @@ -40,6 +40,7 @@ struct FileSourceSettings uint16_t m_reverseAPIChannelIndex; int m_workspaceIndex; QByteArray m_geometryBytes; + bool m_hidden; Serializable *m_channelMarker; Serializable *m_rollupState; diff --git a/plugins/channeltx/localsource/localsourcegui.h b/plugins/channeltx/localsource/localsourcegui.h index 69584295e..5ae429b53 100644 --- a/plugins/channeltx/localsource/localsourcegui.h +++ b/plugins/channeltx/localsource/localsourcegui.h @@ -54,6 +54,8 @@ public: virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; }; virtual QString getTitle() const { return m_settings.m_title; }; virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; + virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } + virtual bool getHidden() const { return m_settings.m_hidden; } private: Ui::LocalSourceGUI* ui; diff --git a/plugins/channeltx/localsource/localsourcesettings.cpp b/plugins/channeltx/localsource/localsourcesettings.cpp index 5e33c589d..88754eb73 100644 --- a/plugins/channeltx/localsource/localsourcesettings.cpp +++ b/plugins/channeltx/localsource/localsourcesettings.cpp @@ -45,6 +45,7 @@ void LocalSourceSettings::resetToDefaults() m_reverseAPIDeviceIndex = 0; m_reverseAPIChannelIndex = 0; m_workspaceIndex = 0; + m_hidden = false; } QByteArray LocalSourceSettings::serialize() const @@ -72,6 +73,7 @@ QByteArray LocalSourceSettings::serialize() const s.writeS32(17, m_workspaceIndex); s.writeBlob(18, m_geometryBytes); + s.writeBool(19, m_hidden); return s.final(); } @@ -126,8 +128,9 @@ bool LocalSourceSettings::deserialize(const QByteArray& data) m_channelMarker->deserialize(bytetmp); } - d.readS32(27, &m_workspaceIndex, 0); - d.readBlob(28, &m_geometryBytes); + d.readS32(17, &m_workspaceIndex, 0); + d.readBlob(18, &m_geometryBytes); + d.readBool(19, &m_hidden, false); return true; } diff --git a/plugins/channeltx/localsource/localsourcesettings.h b/plugins/channeltx/localsource/localsourcesettings.h index b0b044d6b..f68b7e47f 100644 --- a/plugins/channeltx/localsource/localsourcesettings.h +++ b/plugins/channeltx/localsource/localsourcesettings.h @@ -39,6 +39,7 @@ struct LocalSourceSettings uint16_t m_reverseAPIChannelIndex; int m_workspaceIndex; QByteArray m_geometryBytes; + bool m_hidden; Serializable *m_channelMarker; Serializable *m_rollupState; diff --git a/plugins/channeltx/mod802.15.4/ieee_802_15_4_modgui.h b/plugins/channeltx/mod802.15.4/ieee_802_15_4_modgui.h index 1f96f2920..59df48f6d 100644 --- a/plugins/channeltx/mod802.15.4/ieee_802_15_4_modgui.h +++ b/plugins/channeltx/mod802.15.4/ieee_802_15_4_modgui.h @@ -61,7 +61,8 @@ public: virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; }; virtual QString getTitle() const { return m_settings.m_title; }; virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; - + virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } + virtual bool getHidden() const { return m_settings.m_hidden; } public slots: void channelMarkerChangedByCursor(); diff --git a/plugins/channeltx/mod802.15.4/ieee_802_15_4_modsettings.cpp b/plugins/channeltx/mod802.15.4/ieee_802_15_4_modsettings.cpp index 9d71d6c9a..935f897a7 100644 --- a/plugins/channeltx/mod802.15.4/ieee_802_15_4_modsettings.cpp +++ b/plugins/channeltx/mod802.15.4/ieee_802_15_4_modsettings.cpp @@ -76,6 +76,7 @@ void IEEE_802_15_4_ModSettings::resetToDefaults() m_udpAddress = "127.0.0.1"; m_udpPort = 9998; m_workspaceIndex = 0; + m_hidden = false; } bool IEEE_802_15_4_ModSettings::setPHY(QString phy) @@ -205,6 +206,7 @@ QByteArray IEEE_802_15_4_ModSettings::serialize() const s.writeS32(39, m_workspaceIndex); s.writeBlob(40, m_geometryBytes); + s.writeBool(41, m_hidden); return s.final(); } @@ -294,6 +296,7 @@ bool IEEE_802_15_4_ModSettings::deserialize(const QByteArray& data) d.readS32(39, &m_workspaceIndex, 0); d.readBlob(40, &m_geometryBytes); + d.readBool(41, &m_hidden, false); return true; } diff --git a/plugins/channeltx/mod802.15.4/ieee_802_15_4_modsettings.h b/plugins/channeltx/mod802.15.4/ieee_802_15_4_modsettings.h index b50b69d6c..160c2d6e5 100644 --- a/plugins/channeltx/mod802.15.4/ieee_802_15_4_modsettings.h +++ b/plugins/channeltx/mod802.15.4/ieee_802_15_4_modsettings.h @@ -80,6 +80,7 @@ struct IEEE_802_15_4_ModSettings static const int m_udpBufferSize = 100000; int m_workspaceIndex; QByteArray m_geometryBytes; + bool m_hidden; IEEE_802_15_4_ModSettings(); void resetToDefaults(); diff --git a/plugins/channeltx/modais/aismodgui.h b/plugins/channeltx/modais/aismodgui.h index 4deff0459..3ceea99bb 100644 --- a/plugins/channeltx/modais/aismodgui.h +++ b/plugins/channeltx/modais/aismodgui.h @@ -55,6 +55,8 @@ public: virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; }; virtual QString getTitle() const { return m_settings.m_title; }; virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; + virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } + virtual bool getHidden() const { return m_settings.m_hidden; } public slots: void channelMarkerChangedByCursor(); diff --git a/plugins/channeltx/modais/aismodsettings.cpp b/plugins/channeltx/modais/aismodsettings.cpp index f489b715c..f14445f1d 100644 --- a/plugins/channeltx/modais/aismodsettings.cpp +++ b/plugins/channeltx/modais/aismodsettings.cpp @@ -70,6 +70,7 @@ void AISModSettings::resetToDefaults() m_udpAddress = "127.0.0.1"; m_udpPort = 9998; m_workspaceIndex = 0; + m_hidden = false; } bool AISModSettings::setMode(QString mode) @@ -183,6 +184,7 @@ QByteArray AISModSettings::serialize() const s.writeS32(41, m_workspaceIndex); s.writeBlob(42, m_geometryBytes); + s.writeBool(43, m_hidden); return s.final(); } @@ -273,6 +275,7 @@ bool AISModSettings::deserialize(const QByteArray& data) d.readS32(41, &m_workspaceIndex, 0); d.readBlob(42, &m_geometryBytes); + d.readBool(43, &m_hidden, false); return true; } diff --git a/plugins/channeltx/modais/aismodsettings.h b/plugins/channeltx/modais/aismodsettings.h index 91e03293d..127d9f3f3 100644 --- a/plugins/channeltx/modais/aismodsettings.h +++ b/plugins/channeltx/modais/aismodsettings.h @@ -89,6 +89,7 @@ struct AISModSettings Serializable *m_rollupState; int m_workspaceIndex; QByteArray m_geometryBytes; + bool m_hidden; // Sample rate is multiple of 9600 baud rate (use even multiple so Gausian filter has odd number of taps) // Is there any benefit to having this higher? diff --git a/plugins/channeltx/modam/ammodgui.h b/plugins/channeltx/modam/ammodgui.h index 36d79ccbd..5577ab9ec 100644 --- a/plugins/channeltx/modam/ammodgui.h +++ b/plugins/channeltx/modam/ammodgui.h @@ -54,6 +54,8 @@ public: virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; }; virtual QString getTitle() const { return m_settings.m_title; }; virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; + virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } + virtual bool getHidden() const { return m_settings.m_hidden; } public slots: void channelMarkerChangedByCursor(); diff --git a/plugins/channeltx/modam/ammodsettings.cpp b/plugins/channeltx/modam/ammodsettings.cpp index 981690912..aa87ed811 100644 --- a/plugins/channeltx/modam/ammodsettings.cpp +++ b/plugins/channeltx/modam/ammodsettings.cpp @@ -53,6 +53,7 @@ void AMModSettings::resetToDefaults() m_reverseAPIDeviceIndex = 0; m_reverseAPIChannelIndex = 0; m_workspaceIndex = 0; + m_hidden = false; } QByteArray AMModSettings::serialize() const @@ -95,6 +96,7 @@ QByteArray AMModSettings::serialize() const s.writeS32(22, m_workspaceIndex); s.writeBlob(23, m_geometryBytes); + s.writeBool(24, m_hidden); return s.final(); } @@ -172,6 +174,7 @@ bool AMModSettings::deserialize(const QByteArray& data) d.readS32(22, &m_workspaceIndex, 0); d.readBlob(23, &m_geometryBytes); + d.readBool(24, &m_hidden, false); return true; } diff --git a/plugins/channeltx/modam/ammodsettings.h b/plugins/channeltx/modam/ammodsettings.h index 4eb1120f5..78c2839e2 100644 --- a/plugins/channeltx/modam/ammodsettings.h +++ b/plugins/channeltx/modam/ammodsettings.h @@ -57,6 +57,7 @@ struct AMModSettings uint16_t m_reverseAPIChannelIndex; int m_workspaceIndex; QByteArray m_geometryBytes; + bool m_hidden; Serializable *m_channelMarker; Serializable *m_cwKeyerGUI; diff --git a/plugins/channeltx/modatv/atvmodgui.h b/plugins/channeltx/modatv/atvmodgui.h index 2a81c73d1..2c6c90604 100644 --- a/plugins/channeltx/modatv/atvmodgui.h +++ b/plugins/channeltx/modatv/atvmodgui.h @@ -53,6 +53,8 @@ public: virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; }; virtual QString getTitle() const { return m_settings.m_title; }; virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; + virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } + virtual bool getHidden() const { return m_settings.m_hidden; } public slots: void channelMarkerChangedByCursor(); diff --git a/plugins/channeltx/modatv/atvmodsettings.cpp b/plugins/channeltx/modatv/atvmodsettings.cpp index 3f5bc0393..c3176cfa6 100644 --- a/plugins/channeltx/modatv/atvmodsettings.cpp +++ b/plugins/channeltx/modatv/atvmodsettings.cpp @@ -59,6 +59,7 @@ void ATVModSettings::resetToDefaults() m_reverseAPIDeviceIndex = 0; m_reverseAPIChannelIndex = 0; m_workspaceIndex = 0; + m_hidden = false; } QByteArray ATVModSettings::serialize() const @@ -100,6 +101,7 @@ QByteArray ATVModSettings::serialize() const s.writeS32(26, m_workspaceIndex); s.writeBlob(27, m_geometryBytes); + s.writeBool(28, m_hidden); return s.final(); } @@ -175,6 +177,7 @@ bool ATVModSettings::deserialize(const QByteArray& data) d.readS32(26, &m_workspaceIndex, 0); d.readBlob(27, &m_geometryBytes); + d.readBool(28, &m_hidden, false); return true; } diff --git a/plugins/channeltx/modatv/atvmodsettings.h b/plugins/channeltx/modatv/atvmodsettings.h index 95173938c..59984d633 100644 --- a/plugins/channeltx/modatv/atvmodsettings.h +++ b/plugins/channeltx/modatv/atvmodsettings.h @@ -91,6 +91,7 @@ struct ATVModSettings uint16_t m_reverseAPIChannelIndex; int m_workspaceIndex; QByteArray m_geometryBytes; + bool m_hidden; Serializable *m_channelMarker; Serializable *m_rollupState; diff --git a/plugins/channeltx/modchirpchat/chirpchatmodgui.h b/plugins/channeltx/modchirpchat/chirpchatmodgui.h index 5a7ae7fa2..78fd53653 100644 --- a/plugins/channeltx/modchirpchat/chirpchatmodgui.h +++ b/plugins/channeltx/modchirpchat/chirpchatmodgui.h @@ -52,6 +52,8 @@ public: virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; }; virtual QString getTitle() const { return m_settings.m_title; }; virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; + virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } + virtual bool getHidden() const { return m_settings.m_hidden; } public slots: void channelMarkerChangedByCursor(); diff --git a/plugins/channeltx/modchirpchat/chirpchatmodsettings.cpp b/plugins/channeltx/modchirpchat/chirpchatmodsettings.cpp index 6d3c28ee6..e2a37cd75 100644 --- a/plugins/channeltx/modchirpchat/chirpchatmodsettings.cpp +++ b/plugins/channeltx/modchirpchat/chirpchatmodsettings.cpp @@ -94,6 +94,8 @@ void ChirpChatModSettings::resetToDefaults() m_reverseAPIDeviceIndex = 0; m_reverseAPIChannelIndex = 0; m_workspaceIndex = 0; + m_hidden = false; + setDefaultTemplates(); } @@ -204,6 +206,7 @@ QByteArray ChirpChatModSettings::serialize() const s.writeS32(60, m_workspaceIndex); s.writeBlob(61, m_geometryBytes); + s.writeBool(62, m_hidden); return s.final(); } @@ -311,6 +314,7 @@ bool ChirpChatModSettings::deserialize(const QByteArray& data) d.readS32(60, &m_workspaceIndex, 0); d.readBlob(61, &m_geometryBytes); + d.readBool(62, &m_hidden, false); return true; } diff --git a/plugins/channeltx/modchirpchat/chirpchatmodsettings.h b/plugins/channeltx/modchirpchat/chirpchatmodsettings.h index 710055d42..15e46323e 100644 --- a/plugins/channeltx/modchirpchat/chirpchatmodsettings.h +++ b/plugins/channeltx/modchirpchat/chirpchatmodsettings.h @@ -90,6 +90,7 @@ struct ChirpChatModSettings uint16_t m_reverseAPIChannelIndex; int m_workspaceIndex; QByteArray m_geometryBytes; + bool m_hidden; Serializable *m_channelMarker; Serializable *m_rollupState; diff --git a/plugins/channeltx/moddatv/datvmodgui.h b/plugins/channeltx/moddatv/datvmodgui.h index 0259aafa8..ea24d775f 100644 --- a/plugins/channeltx/moddatv/datvmodgui.h +++ b/plugins/channeltx/moddatv/datvmodgui.h @@ -54,6 +54,8 @@ public: virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; }; virtual QString getTitle() const { return m_settings.m_title; }; virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; + virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } + virtual bool getHidden() const { return m_settings.m_hidden; } public slots: void channelMarkerChangedByCursor(); diff --git a/plugins/channeltx/moddatv/datvmodsettings.cpp b/plugins/channeltx/moddatv/datvmodsettings.cpp index 9428bbbd5..ab175acd9 100644 --- a/plugins/channeltx/moddatv/datvmodsettings.cpp +++ b/plugins/channeltx/moddatv/datvmodsettings.cpp @@ -58,6 +58,7 @@ void DATVModSettings::resetToDefaults() m_reverseAPIDeviceIndex = 0; m_reverseAPIChannelIndex = 0; m_workspaceIndex = 0; + m_hidden = false; } QByteArray DATVModSettings::serialize() const @@ -96,6 +97,7 @@ QByteArray DATVModSettings::serialize() const s.writeS32(30, m_workspaceIndex); s.writeBlob(31, m_geometryBytes); + s.writeBool(32, m_hidden); return s.final(); } @@ -169,6 +171,7 @@ bool DATVModSettings::deserialize(const QByteArray& data) d.readS32(30, &m_workspaceIndex, 0); d.readBlob(31, &m_geometryBytes); + d.readBool(32, &m_hidden, false); return true; } diff --git a/plugins/channeltx/moddatv/datvmodsettings.h b/plugins/channeltx/moddatv/datvmodsettings.h index 985558e48..757d4c78b 100644 --- a/plugins/channeltx/moddatv/datvmodsettings.h +++ b/plugins/channeltx/moddatv/datvmodsettings.h @@ -99,6 +99,7 @@ struct DATVModSettings Serializable *m_rollupState; int m_workspaceIndex; QByteArray m_geometryBytes; + bool m_hidden; DATVModSettings(); void resetToDefaults(); diff --git a/plugins/channeltx/modfreedv/freedvmodgui.h b/plugins/channeltx/modfreedv/freedvmodgui.h index ed7277ed9..30494d4c4 100644 --- a/plugins/channeltx/modfreedv/freedvmodgui.h +++ b/plugins/channeltx/modfreedv/freedvmodgui.h @@ -55,6 +55,8 @@ public: virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; }; virtual QString getTitle() const { return m_settings.m_title; }; virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; + virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } + virtual bool getHidden() const { return m_settings.m_hidden; } public slots: void channelMarkerChangedByCursor(); diff --git a/plugins/channeltx/modfreedv/freedvmodsettings.cpp b/plugins/channeltx/modfreedv/freedvmodsettings.cpp index 1514fd198..f2b1ea565 100644 --- a/plugins/channeltx/modfreedv/freedvmodsettings.cpp +++ b/plugins/channeltx/modfreedv/freedvmodsettings.cpp @@ -52,6 +52,7 @@ void FreeDVModSettings::resetToDefaults() m_reverseAPIDeviceIndex = 0; m_reverseAPIChannelIndex = 0; m_workspaceIndex = 0; + m_hidden = false; } QByteArray FreeDVModSettings::serialize() const @@ -97,6 +98,7 @@ QByteArray FreeDVModSettings::serialize() const s.writeS32(29, m_workspaceIndex); s.writeBlob(30, m_geometryBytes); + s.writeBool(31, m_hidden); return s.final(); } @@ -187,6 +189,7 @@ bool FreeDVModSettings::deserialize(const QByteArray& data) d.readS32(29, &m_workspaceIndex, 0); d.readBlob(30, &m_geometryBytes); + d.readBool(31, &m_hidden, false); return true; } diff --git a/plugins/channeltx/modfreedv/freedvmodsettings.h b/plugins/channeltx/modfreedv/freedvmodsettings.h index 1195c894f..4f7bcf96d 100644 --- a/plugins/channeltx/modfreedv/freedvmodsettings.h +++ b/plugins/channeltx/modfreedv/freedvmodsettings.h @@ -68,6 +68,7 @@ struct FreeDVModSettings uint16_t m_reverseAPIChannelIndex; int m_workspaceIndex; QByteArray m_geometryBytes; + bool m_hidden; Serializable *m_channelMarker; Serializable *m_spectrumGUI; diff --git a/plugins/channeltx/modnfm/nfmmodgui.h b/plugins/channeltx/modnfm/nfmmodgui.h index 5d386b285..10af4b8c0 100644 --- a/plugins/channeltx/modnfm/nfmmodgui.h +++ b/plugins/channeltx/modnfm/nfmmodgui.h @@ -54,6 +54,8 @@ public: virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; }; virtual QString getTitle() const { return m_settings.m_title; }; virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; + virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } + virtual bool getHidden() const { return m_settings.m_hidden; } public slots: void channelMarkerChangedByCursor(); diff --git a/plugins/channeltx/modnfm/nfmmodsettings.cpp b/plugins/channeltx/modnfm/nfmmodsettings.cpp index 8344c59bf..956466aaa 100644 --- a/plugins/channeltx/modnfm/nfmmodsettings.cpp +++ b/plugins/channeltx/modnfm/nfmmodsettings.cpp @@ -80,6 +80,7 @@ void NFMModSettings::resetToDefaults() m_reverseAPIDeviceIndex = 0; m_reverseAPIChannelIndex = 0; m_workspaceIndex = 0; + m_hidden = false; } QByteArray NFMModSettings::serialize() const @@ -128,6 +129,7 @@ QByteArray NFMModSettings::serialize() const s.writeS32(28, m_workspaceIndex); s.writeBlob(29, m_geometryBytes); + s.writeBool(30, m_hidden); return s.final(); } @@ -214,6 +216,7 @@ bool NFMModSettings::deserialize(const QByteArray& data) d.readS32(28, &m_workspaceIndex, 0); d.readBlob(29, &m_geometryBytes); + d.readBool(30, &m_hidden, false); return true; } diff --git a/plugins/channeltx/modnfm/nfmmodsettings.h b/plugins/channeltx/modnfm/nfmmodsettings.h index bc74e4c99..9ce5fbf68 100644 --- a/plugins/channeltx/modnfm/nfmmodsettings.h +++ b/plugins/channeltx/modnfm/nfmmodsettings.h @@ -69,6 +69,7 @@ struct NFMModSettings uint16_t m_reverseAPIChannelIndex; int m_workspaceIndex; QByteArray m_geometryBytes; + bool m_hidden; Serializable *m_channelMarker; Serializable *m_cwKeyerGUI; diff --git a/plugins/channeltx/modpacket/packetmodgui.h b/plugins/channeltx/modpacket/packetmodgui.h index a875801e7..dbc2c1779 100644 --- a/plugins/channeltx/modpacket/packetmodgui.h +++ b/plugins/channeltx/modpacket/packetmodgui.h @@ -54,6 +54,8 @@ public: virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; }; virtual QString getTitle() const { return m_settings.m_title; }; virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; + virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } + virtual bool getHidden() const { return m_settings.m_hidden; } public slots: void channelMarkerChangedByCursor(); diff --git a/plugins/channeltx/modpacket/packetmodsettings.cpp b/plugins/channeltx/modpacket/packetmodsettings.cpp index 4ddb2a678..5939717f2 100644 --- a/plugins/channeltx/modpacket/packetmodsettings.cpp +++ b/plugins/channeltx/modpacket/packetmodsettings.cpp @@ -86,6 +86,7 @@ void PacketModSettings::resetToDefaults() m_udpAddress = "127.0.0.1"; m_udpPort = 9998; m_workspaceIndex = 0; + m_hidden = false; } bool PacketModSettings::setMode(QString mode) @@ -234,6 +235,7 @@ QByteArray PacketModSettings::serialize() const s.writeS32(55, m_workspaceIndex); s.writeBlob(56, m_geometryBytes); + s.writeBool(57, m_hidden); return s.final(); } @@ -338,6 +340,7 @@ bool PacketModSettings::deserialize(const QByteArray& data) d.readS32(55, &m_workspaceIndex, 0); d.readBlob(56, &m_geometryBytes); + d.readBool(57, &m_hidden, false); return true; } diff --git a/plugins/channeltx/modpacket/packetmodsettings.h b/plugins/channeltx/modpacket/packetmodsettings.h index c4dad2789..25d79ad52 100644 --- a/plugins/channeltx/modpacket/packetmodsettings.h +++ b/plugins/channeltx/modpacket/packetmodsettings.h @@ -85,6 +85,7 @@ struct PacketModSettings Serializable *m_rollupState; int m_workspaceIndex; QByteArray m_geometryBytes; + bool m_hidden; PacketModSettings(); void resetToDefaults(); diff --git a/plugins/channeltx/modssb/ssbmodgui.h b/plugins/channeltx/modssb/ssbmodgui.h index 3f4e21b8a..8ac6de0bd 100644 --- a/plugins/channeltx/modssb/ssbmodgui.h +++ b/plugins/channeltx/modssb/ssbmodgui.h @@ -55,6 +55,8 @@ public: virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; }; virtual QString getTitle() const { return m_settings.m_title; }; virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; + virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } + virtual bool getHidden() const { return m_settings.m_hidden; } public slots: void channelMarkerChangedByCursor(); diff --git a/plugins/channeltx/modssb/ssbmodsettings.cpp b/plugins/channeltx/modssb/ssbmodsettings.cpp index 14961ea31..3cc48adeb 100644 --- a/plugins/channeltx/modssb/ssbmodsettings.cpp +++ b/plugins/channeltx/modssb/ssbmodsettings.cpp @@ -76,6 +76,7 @@ void SSBModSettings::resetToDefaults() m_reverseAPIDeviceIndex = 0; m_reverseAPIChannelIndex = 0; m_workspaceIndex = 0; + m_hidden = false; } QByteArray SSBModSettings::serialize() const @@ -130,6 +131,7 @@ QByteArray SSBModSettings::serialize() const s.writeS32(32, m_workspaceIndex); s.writeBlob(33, m_geometryBytes); + s.writeBool(34, m_hidden); return s.final(); } @@ -225,6 +227,7 @@ bool SSBModSettings::deserialize(const QByteArray& data) d.readS32(32, &m_workspaceIndex, 0); d.readBlob(33, &m_geometryBytes); + d.readBool(34, &m_hidden, false); return true; } diff --git a/plugins/channeltx/modssb/ssbmodsettings.h b/plugins/channeltx/modssb/ssbmodsettings.h index d1e40115b..5327a8106 100644 --- a/plugins/channeltx/modssb/ssbmodsettings.h +++ b/plugins/channeltx/modssb/ssbmodsettings.h @@ -72,6 +72,7 @@ struct SSBModSettings uint16_t m_reverseAPIChannelIndex; int m_workspaceIndex; QByteArray m_geometryBytes; + bool m_hidden; Serializable *m_channelMarker; Serializable *m_spectrumGUI; diff --git a/plugins/channeltx/modwfm/wfmmodgui.h b/plugins/channeltx/modwfm/wfmmodgui.h index 8188452dc..928173855 100644 --- a/plugins/channeltx/modwfm/wfmmodgui.h +++ b/plugins/channeltx/modwfm/wfmmodgui.h @@ -52,6 +52,8 @@ public: virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; }; virtual QString getTitle() const { return m_settings.m_title; }; virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; + virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } + virtual bool getHidden() const { return m_settings.m_hidden; } public slots: void channelMarkerChangedByCursor(); diff --git a/plugins/channeltx/modwfm/wfmmodsettings.cpp b/plugins/channeltx/modwfm/wfmmodsettings.cpp index bff6853a9..ccf7501ca 100644 --- a/plugins/channeltx/modwfm/wfmmodsettings.cpp +++ b/plugins/channeltx/modwfm/wfmmodsettings.cpp @@ -61,6 +61,7 @@ void WFMModSettings::resetToDefaults() m_reverseAPIDeviceIndex = 0; m_reverseAPIChannelIndex = 0; m_workspaceIndex = 0; + m_hidden = false; } QByteArray WFMModSettings::serialize() const @@ -104,6 +105,7 @@ QByteArray WFMModSettings::serialize() const s.writeS32(23, m_workspaceIndex); s.writeBlob(24, m_geometryBytes); + s.writeBool(25, m_hidden); return s.final(); } @@ -183,6 +185,7 @@ bool WFMModSettings::deserialize(const QByteArray& data) d.readS32(23, &m_workspaceIndex, 0); d.readBlob(24, &m_geometryBytes); + d.readBool(25, &m_hidden, false); return true; } diff --git a/plugins/channeltx/modwfm/wfmmodsettings.h b/plugins/channeltx/modwfm/wfmmodsettings.h index c17fff830..9d5edd10b 100644 --- a/plugins/channeltx/modwfm/wfmmodsettings.h +++ b/plugins/channeltx/modwfm/wfmmodsettings.h @@ -61,6 +61,7 @@ struct WFMModSettings uint16_t m_reverseAPIChannelIndex; int m_workspaceIndex; QByteArray m_geometryBytes; + bool m_hidden; Serializable *m_channelMarker; Serializable *m_cwKeyerGUI; diff --git a/plugins/channeltx/remotesource/remotesourcegui.h b/plugins/channeltx/remotesource/remotesourcegui.h index 340d7ad86..630204fb7 100644 --- a/plugins/channeltx/remotesource/remotesourcegui.h +++ b/plugins/channeltx/remotesource/remotesourcegui.h @@ -53,6 +53,8 @@ public: virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; }; virtual QString getTitle() const { return m_settings.m_title; }; virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; + virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } + virtual bool getHidden() const { return m_settings.m_hidden; } public slots: void channelMarkerChangedByCursor(); diff --git a/plugins/channeltx/remotesource/remotesourcesettings.cpp b/plugins/channeltx/remotesource/remotesourcesettings.cpp index d8a5d7b96..77124000b 100644 --- a/plugins/channeltx/remotesource/remotesourcesettings.cpp +++ b/plugins/channeltx/remotesource/remotesourcesettings.cpp @@ -45,6 +45,7 @@ void RemoteSourceSettings::resetToDefaults() m_reverseAPIDeviceIndex = 0; m_reverseAPIChannelIndex = 0; m_workspaceIndex = 0; + m_hidden = false; } QByteArray RemoteSourceSettings::serialize() const @@ -74,6 +75,7 @@ QByteArray RemoteSourceSettings::serialize() const s.writeS32(15, m_workspaceIndex); s.writeBlob(16, m_geometryBytes); + s.writeBool(17, m_hidden); return s.final(); } @@ -138,6 +140,7 @@ bool RemoteSourceSettings::deserialize(const QByteArray& data) d.readS32(15, &m_workspaceIndex, 0); d.readBlob(16, &m_geometryBytes); + d.readBool(17, &m_hidden, false); return true; } diff --git a/plugins/channeltx/remotesource/remotesourcesettings.h b/plugins/channeltx/remotesource/remotesourcesettings.h index 1bb40ffdd..a58039ad2 100644 --- a/plugins/channeltx/remotesource/remotesourcesettings.h +++ b/plugins/channeltx/remotesource/remotesourcesettings.h @@ -39,6 +39,7 @@ struct RemoteSourceSettings uint16_t m_reverseAPIChannelIndex; int m_workspaceIndex; QByteArray m_geometryBytes; + bool m_hidden; Serializable *m_channelMarker; Serializable *m_rollupState; diff --git a/plugins/channeltx/udpsource/udpsourcegui.h b/plugins/channeltx/udpsource/udpsourcegui.h index 372fd6b99..a009ee03a 100644 --- a/plugins/channeltx/udpsource/udpsourcegui.h +++ b/plugins/channeltx/udpsource/udpsourcegui.h @@ -55,6 +55,8 @@ public: virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; }; virtual QString getTitle() const { return m_settings.m_title; }; virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; + virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } + virtual bool getHidden() const { return m_settings.m_hidden; } public slots: void channelMarkerChangedByCursor(); diff --git a/plugins/channeltx/udpsource/udpsourcesettings.cpp b/plugins/channeltx/udpsource/udpsourcesettings.cpp index b270d7707..346cb996c 100644 --- a/plugins/channeltx/udpsource/udpsourcesettings.cpp +++ b/plugins/channeltx/udpsource/udpsourcesettings.cpp @@ -61,6 +61,7 @@ void UDPSourceSettings::resetToDefaults() m_reverseAPIDeviceIndex = 0; m_reverseAPIChannelIndex = 0; m_workspaceIndex = 0; + m_hidden = false; } QByteArray UDPSourceSettings::serialize() const @@ -105,6 +106,7 @@ QByteArray UDPSourceSettings::serialize() const s.writeS32(28, m_workspaceIndex); s.writeBlob(29, m_geometryBytes); + s.writeBool(30, m_hidden); return s.final(); } @@ -201,6 +203,7 @@ bool UDPSourceSettings::deserialize(const QByteArray& data) d.readS32(28, &m_workspaceIndex, 0); d.readBlob(29, &m_geometryBytes); + d.readBool(30, &m_hidden, false); return true; } diff --git a/plugins/channeltx/udpsource/udpsourcesettings.h b/plugins/channeltx/udpsource/udpsourcesettings.h index 1b02f8fcf..3aa3b5429 100644 --- a/plugins/channeltx/udpsource/udpsourcesettings.h +++ b/plugins/channeltx/udpsource/udpsourcesettings.h @@ -69,6 +69,7 @@ struct UDPSourceSettings uint16_t m_reverseAPIChannelIndex; int m_workspaceIndex; QByteArray m_geometryBytes; + bool m_hidden; Serializable *m_channelMarker; Serializable *m_spectrumGUI; diff --git a/sdrgui/channel/channelgui.h b/sdrgui/channel/channelgui.h index 4b8424314..c305b0aa5 100644 --- a/sdrgui/channel/channelgui.h +++ b/sdrgui/channel/channelgui.h @@ -64,6 +64,8 @@ public: virtual QByteArray getGeometryBytes() const = 0; virtual QString getTitle() const = 0; virtual QColor getTitleColor() const = 0; + virtual void zetHidden(bool hidden) = 0; + virtual bool getHidden() const = 0; virtual MessageQueue* getInputMessageQueue() = 0; diff --git a/sdrgui/device/deviceuiset.cpp b/sdrgui/device/deviceuiset.cpp index 74f813e4a..da3373072 100644 --- a/sdrgui/device/deviceuiset.cpp +++ b/sdrgui/device/deviceuiset.cpp @@ -306,6 +306,10 @@ void DeviceUISet::loadRxChannelSettings(const Preset *preset, PluginAPI *pluginA currentWorkspace->addToMdiArea((QMdiSubWindow*) rxChannelGUI); } + if (rxChannelGUI->getHidden()) { + rxChannelGUI->hide(); + } + rxChannelGUI->restoreGeometry(rxChannelGUI->getGeometryBytes()); rxChannelGUI->setDeviceType(ChannelGUI::DeviceRx); rxChannelGUI->setDeviceSetIndex(m_deviceSetIndex); @@ -336,6 +340,7 @@ void DeviceUISet::saveRxChannelSettings(Preset *preset) const ChannelGUI *channelGUI = m_channelInstanceRegistrations[i].m_gui; qDebug("DeviceUISet::saveRxChannelSettings: saving channel [%s]", qPrintable(m_channelInstanceRegistrations[i].m_channelAPI->getURI())); channelGUI->setGeometryBytes(channelGUI->saveGeometry()); + channelGUI->zetHidden(channelGUI->isHidden()); preset->addChannel(m_channelInstanceRegistrations[i].m_channelAPI->getURI(), channelGUI->serialize()); } } @@ -415,6 +420,10 @@ void DeviceUISet::loadTxChannelSettings(const Preset *preset, PluginAPI *pluginA currentWorkspace->addToMdiArea((QMdiSubWindow*) txChannelGUI); } + if (txChannelGUI->getHidden()) { + txChannelGUI->hide(); + } + txChannelGUI->restoreGeometry(txChannelGUI->getGeometryBytes()); txChannelGUI->setDeviceType(ChannelGUI::DeviceRx); txChannelGUI->setDeviceSetIndex(m_deviceSetIndex); @@ -446,6 +455,7 @@ void DeviceUISet::saveTxChannelSettings(Preset *preset) const ChannelGUI *channelGUI = m_channelInstanceRegistrations[i].m_gui; qDebug("DeviceUISet::saveTxChannelSettings: saving channel [%s]", qPrintable(m_channelInstanceRegistrations[i].m_channelAPI->getURI())); channelGUI->setGeometryBytes(channelGUI->saveGeometry()); + channelGUI->zetHidden(channelGUI->isHidden()); preset->addChannel(m_channelInstanceRegistrations[i].m_channelAPI->getURI(), channelGUI->serialize()); } } @@ -526,6 +536,10 @@ void DeviceUISet::loadMIMOChannelSettings(const Preset *preset, PluginAPI *plugi currentWorkspace->addToMdiArea((QMdiSubWindow*) mimoChannelGUI); } + if (mimoChannelGUI->getHidden()) { + mimoChannelGUI->hide(); + } + mimoChannelGUI->restoreGeometry(mimoChannelGUI->getGeometryBytes()); mimoChannelGUI->setDeviceType(ChannelGUI::DeviceRx); mimoChannelGUI->setDeviceSetIndex(m_deviceSetIndex); @@ -556,6 +570,7 @@ void DeviceUISet::saveMIMOChannelSettings(Preset *preset) const ChannelGUI *channelGUI = m_channelInstanceRegistrations[i].m_gui; qDebug("DeviceUISet::saveMIMOChannelSettings: saving channel [%s]", qPrintable(m_channelInstanceRegistrations[i].m_channelAPI->getURI())); channelGUI->setGeometryBytes(channelGUI->saveGeometry()); + channelGUI->zetHidden(channelGUI->isHidden()); preset->addChannel(m_channelInstanceRegistrations[i].m_channelAPI->getURI(), channelGUI->serialize()); } } From af13b31d85afc9a9361a103ad1db6655f7494e92 Mon Sep 17 00:00:00 2001 From: f4exb Date: Wed, 13 Apr 2022 11:08:21 +0200 Subject: [PATCH 012/115] Massive UI revamping (v7): display channel absolute frequency in status bar. Manage shift frequency limits --- .../channelrx/chanalyzer/chanalyzergui.cpp | 13 +++++- plugins/channelrx/chanalyzer/chanalyzergui.h | 2 + plugins/channelrx/demodadsb/adsbdemodgui.cpp | 16 ++++++- plugins/channelrx/demodadsb/adsbdemodgui.h | 3 ++ plugins/channelrx/demodais/aisdemod.cpp | 4 ++ plugins/channelrx/demodais/aisdemodgui.cpp | 19 ++++++++ plugins/channelrx/demodais/aisdemodgui.h | 3 ++ plugins/channelrx/demodam/amdemod.cpp | 4 ++ plugins/channelrx/demodam/amdemodgui.cpp | 25 +++++++++-- plugins/channelrx/demodam/amdemodgui.h | 3 ++ plugins/channelrx/demodapt/aptdemod.cpp | 3 +- plugins/channelrx/demodapt/aptdemodgui.cpp | 12 ++++++ plugins/channelrx/demodapt/aptdemodgui.h | 2 + plugins/channelrx/demodatv/atvdemod.cpp | 6 +-- plugins/channelrx/demodatv/atvdemodgui.cpp | 12 ++++++ plugins/channelrx/demodatv/atvdemodgui.h | 2 + plugins/channelrx/demodbfm/bfmdemod.cpp | 4 ++ plugins/channelrx/demodbfm/bfmdemodgui.cpp | 21 ++++++++- plugins/channelrx/demodbfm/bfmdemodgui.h | 3 ++ .../demodchirpchat/chirpchatdemod.cpp | 6 +-- .../demodchirpchat/chirpchatdemodgui.cpp | 14 +++++- .../demodchirpchat/chirpchatdemodgui.h | 2 + plugins/channelrx/demoddab/dabdemod.cpp | 6 +-- plugins/channelrx/demoddab/dabdemodgui.cpp | 15 +++++++ plugins/channelrx/demoddab/dabdemodgui.h | 2 + plugins/channelrx/demoddatv/datvdemod.cpp | 5 +++ plugins/channelrx/demoddatv/datvdemodgui.cpp | 43 +++++++++++++------ plugins/channelrx/demoddatv/datvdemodgui.h | 3 ++ plugins/channelrx/demoddsd/dsddemod.cpp | 4 ++ plugins/channelrx/demoddsd/dsddemodgui.cpp | 21 ++++++++- plugins/channelrx/demoddsd/dsddemodgui.h | 3 ++ plugins/channelrx/demodfreedv/freedvdemod.cpp | 4 ++ .../channelrx/demodfreedv/freedvdemodgui.cpp | 20 ++++++++- .../channelrx/demodfreedv/freedvdemodgui.h | 3 ++ plugins/channelrx/demodnfm/nfmdemod.cpp | 4 ++ plugins/channelrx/demodnfm/nfmdemodgui.cpp | 21 ++++++++- plugins/channelrx/demodnfm/nfmdemodgui.h | 3 ++ plugins/channelrx/demodpacket/packetdemod.cpp | 6 +-- .../channelrx/demodpacket/packetdemodgui.cpp | 12 ++++++ .../channelrx/demodpacket/packetdemodgui.h | 2 + plugins/channelrx/demodpager/pagerdemod.cpp | 4 ++ .../channelrx/demodpager/pagerdemodgui.cpp | 19 ++++++++ plugins/channelrx/demodpager/pagerdemodgui.h | 3 ++ .../demodradiosonde/radiosondedemod.cpp | 4 ++ .../demodradiosonde/radiosondedemodgui.cpp | 19 ++++++++ .../demodradiosonde/radiosondedemodgui.h | 3 ++ plugins/channelrx/demodssb/ssbdemod.cpp | 4 ++ plugins/channelrx/demodssb/ssbdemodgui.cpp | 20 ++++++++- plugins/channelrx/demodssb/ssbdemodgui.h | 3 ++ plugins/channelrx/demodvor/vordemod.cpp | 6 +-- plugins/channelrx/demodvor/vordemodgui.cpp | 8 ++++ plugins/channelrx/demodvor/vordemodgui.h | 2 + plugins/channelrx/demodvorsc/vordemodsc.cpp | 6 +-- .../channelrx/demodvorsc/vordemodscgui.cpp | 12 ++++++ plugins/channelrx/demodvorsc/vordemodscgui.h | 2 + plugins/channelrx/demodwfm/wfmdemod.cpp | 4 ++ plugins/channelrx/demodwfm/wfmdemodgui.cpp | 22 +++++++++- plugins/channelrx/demodwfm/wfmdemodgui.h | 3 ++ plugins/channelrx/filesink/filesink.cpp | 6 +-- plugins/channelrx/filesink/filesinkgui.cpp | 13 ++++++ plugins/channelrx/filesink/filesinkgui.h | 2 + plugins/channelrx/freqtracker/freqtracker.cpp | 6 +-- .../channelrx/freqtracker/freqtrackergui.cpp | 13 +++++- .../channelrx/freqtracker/freqtrackergui.h | 2 + plugins/channelrx/localsink/localsink.cpp | 7 +-- plugins/channelrx/localsink/localsink.h | 20 --------- plugins/channelrx/localsink/localsinkgui.cpp | 15 ++++++- plugins/channelrx/localsink/localsinkgui.h | 2 + plugins/channelrx/noisefigure/noisefigure.cpp | 6 +-- .../channelrx/noisefigure/noisefiguregui.cpp | 12 ++++++ .../channelrx/noisefigure/noisefiguregui.h | 2 + .../radioastronomy/radioastronomygui.cpp | 14 ++++++ .../radioastronomy/radioastronomygui.h | 2 + plugins/channelrx/radioclock/radioclock.cpp | 4 ++ .../channelrx/radioclock/radioclockgui.cpp | 19 ++++++++ plugins/channelrx/radioclock/radioclockgui.h | 3 ++ plugins/channelrx/remotesink/remotesink.cpp | 6 +-- .../channelrx/remotesink/remotesinkgui.cpp | 10 +++++ plugins/channelrx/remotesink/remotesinkgui.h | 1 + .../channelrx/sigmffilesink/sigmffilesink.cpp | 6 +-- .../sigmffilesink/sigmffilesinkgui.cpp | 12 ++++++ .../sigmffilesink/sigmffilesinkgui.h | 2 + plugins/channelrx/udpsink/udpsink.cpp | 4 ++ plugins/channelrx/udpsink/udpsinkgui.cpp | 23 +++++++++- plugins/channelrx/udpsink/udpsinkgui.h | 3 ++ plugins/channeltx/filesource/filesource.cpp | 4 +- plugins/channeltx/filesource/filesource.h | 20 --------- .../channeltx/filesource/filesourcegui.cpp | 16 ++++++- plugins/channeltx/filesource/filesourcegui.h | 2 + plugins/channeltx/localsource/localsource.cpp | 7 +-- plugins/channeltx/localsource/localsource.h | 20 --------- .../channeltx/localsource/localsourcegui.cpp | 18 ++++++-- .../channeltx/localsource/localsourcegui.h | 1 + .../mod802.15.4/ieee_802_15_4_mod.cpp | 6 +-- .../mod802.15.4/ieee_802_15_4_modgui.cpp | 13 +++++- .../mod802.15.4/ieee_802_15_4_modgui.h | 2 + plugins/channeltx/modais/aismod.cpp | 4 ++ plugins/channeltx/modais/aismodgui.cpp | 21 ++++++++- plugins/channeltx/modais/aismodgui.h | 3 ++ plugins/channeltx/modam/ammod.cpp | 4 ++ plugins/channeltx/modam/ammodgui.cpp | 21 ++++++++- plugins/channeltx/modam/ammodgui.h | 3 ++ plugins/channeltx/modatv/atvmod.cpp | 4 ++ plugins/channeltx/modatv/atvmodgui.cpp | 21 ++++++++- plugins/channeltx/modatv/atvmodgui.h | 3 ++ .../channeltx/modchirpchat/chirpchatmod.cpp | 6 +-- .../modchirpchat/chirpchatmodgui.cpp | 14 +++++- .../channeltx/modchirpchat/chirpchatmodgui.h | 2 + plugins/channeltx/moddatv/datvmod.cpp | 4 ++ plugins/channeltx/moddatv/datvmodgui.cpp | 22 +++++++++- plugins/channeltx/moddatv/datvmodgui.h | 3 ++ plugins/channeltx/modfreedv/freedvmod.cpp | 4 ++ plugins/channeltx/modfreedv/freedvmodgui.cpp | 20 ++++++++- plugins/channeltx/modfreedv/freedvmodgui.h | 3 ++ plugins/channeltx/modnfm/nfmmod.cpp | 4 ++ plugins/channeltx/modnfm/nfmmodgui.cpp | 21 ++++++++- plugins/channeltx/modnfm/nfmmodgui.h | 3 ++ plugins/channeltx/modpacket/packetmod.cpp | 4 ++ plugins/channeltx/modpacket/packetmodgui.cpp | 23 +++++++++- plugins/channeltx/modpacket/packetmodgui.h | 3 ++ plugins/channeltx/modssb/ssbmod.cpp | 4 ++ plugins/channeltx/modssb/ssbmodgui.cpp | 20 ++++++++- plugins/channeltx/modssb/ssbmodgui.h | 3 ++ plugins/channeltx/modwfm/wfmmod.cpp | 4 ++ plugins/channeltx/modwfm/wfmmodgui.cpp | 21 ++++++++- plugins/channeltx/modwfm/wfmmodgui.h | 3 ++ .../channeltx/remotesource/remotesource.cpp | 7 +-- plugins/channeltx/remotesource/remotesource.h | 21 --------- .../remotesource/remotesourcegui.cpp | 19 ++++++-- .../channeltx/remotesource/remotesourcegui.h | 2 + plugins/channeltx/udpsource/udpsource.cpp | 4 ++ plugins/channeltx/udpsource/udpsourcegui.cpp | 21 ++++++++- plugins/channeltx/udpsource/udpsourcegui.h | 3 ++ sdrgui/channel/channelgui.cpp | 25 ++++++++++- sdrgui/channel/channelgui.h | 3 ++ 135 files changed, 999 insertions(+), 205 deletions(-) diff --git a/plugins/channelrx/chanalyzer/chanalyzergui.cpp b/plugins/channelrx/chanalyzer/chanalyzergui.cpp index 5ddf44f33..fb839e645 100644 --- a/plugins/channelrx/chanalyzer/chanalyzergui.cpp +++ b/plugins/channelrx/chanalyzer/chanalyzergui.cpp @@ -99,6 +99,7 @@ void ChannelAnalyzerGUI::displaySettings() ui->rrcRolloffText->setText(rolloffStr); getRollupContents()->restoreState(m_rollupState); + updateAbsoluteCenterFrequency(); blockApplySettings(false); } @@ -233,6 +234,10 @@ bool ChannelAnalyzerGUI::handleMessage(const Message& message) { DSPSignalNotification& cmd = (DSPSignalNotification&) message; m_basebandSampleRate = cmd.getSampleRate(); + m_deviceCenterFrequency = cmd.getCenterFrequency(); + ui->deltaFrequency->setValueRange(false, 8, -m_basebandSampleRate/2, m_basebandSampleRate/2); + ui->deltaFrequencyLabel->setToolTip(tr("Range %1 %L2 Hz").arg(QChar(0xB1)).arg(m_basebandSampleRate/2)); + updateAbsoluteCenterFrequency(); qDebug("ChannelAnalyzerGUI::handleMessage: DSPSignalNotification: m_basebandSampleRate: %d", m_basebandSampleRate); setSinkSampleRate(); @@ -275,6 +280,7 @@ void ChannelAnalyzerGUI::channelMarkerChangedByCursor() { ui->deltaFrequency->setValue(m_channelMarker.getCenterFrequency()); m_settings.m_inputFrequencyOffset = m_channelMarker.getCenterFrequency(); + updateAbsoluteCenterFrequency(); applySettings(); } @@ -388,6 +394,7 @@ void ChannelAnalyzerGUI::on_deltaFrequency_changed(qint64 value) { m_channelMarker.setCenterFrequency(value); m_settings.m_inputFrequencyOffset = m_channelMarker.getCenterFrequency(); + updateAbsoluteCenterFrequency(); applySettings(); } @@ -468,7 +475,6 @@ void ChannelAnalyzerGUI::onMenuDialogCalled(const QPoint& p) dialog.move(p); dialog.exec(); - m_settings.m_inputFrequencyOffset = m_channelMarker.getCenterFrequency(); m_settings.m_rgbColor = m_channelMarker.getColor().rgb(); m_settings.m_title = m_channelMarker.getTitle(); m_settings.m_useReverseAPI = dialog.useReverseAPI(); @@ -703,3 +709,8 @@ void ChannelAnalyzerGUI::makeUIConnections() QObject::connect(ui->lowCut, &QSlider::valueChanged, this, &ChannelAnalyzerGUI::on_lowCut_valueChanged); QObject::connect(ui->ssb, &QCheckBox::toggled, this, &ChannelAnalyzerGUI::on_ssb_toggled); } + +void ChannelAnalyzerGUI::updateAbsoluteCenterFrequency() +{ + setStatusFrequency(m_deviceCenterFrequency + m_settings.m_inputFrequencyOffset); +} diff --git a/plugins/channelrx/chanalyzer/chanalyzergui.h b/plugins/channelrx/chanalyzer/chanalyzergui.h index 1126b5fc3..bfd8016ed 100644 --- a/plugins/channelrx/chanalyzer/chanalyzergui.h +++ b/plugins/channelrx/chanalyzer/chanalyzergui.h @@ -69,6 +69,7 @@ private: ChannelMarker m_channelMarker; RollupState m_rollupState; ChannelAnalyzerSettings m_settings; + qint64 m_deviceCenterFrequency; bool m_doApplySettings; int m_basebandSampleRate; //!< sample rate after final in-channel decimation (spanlog2) MovingAverageUtil m_channelPowerAvg; @@ -93,6 +94,7 @@ private: void setSpectrumDisplay(); bool handleMessage(const Message& message); void makeUIConnections(); + void updateAbsoluteCenterFrequency(); void leaveEvent(QEvent*); void enterEvent(QEvent*); diff --git a/plugins/channelrx/demodadsb/adsbdemodgui.cpp b/plugins/channelrx/demodadsb/adsbdemodgui.cpp index 303019598..ac1e0f0fa 100644 --- a/plugins/channelrx/demodadsb/adsbdemodgui.cpp +++ b/plugins/channelrx/demodadsb/adsbdemodgui.cpp @@ -2132,7 +2132,7 @@ QString ADSBDemodGUI::subAircraftString(Aircraft *aircraft, const QString &strin bool ADSBDemodGUI::handleMessage(const Message& message) { -if (DSPSignalNotification::match(message)) + if (DSPSignalNotification::match(message)) { DSPSignalNotification& notif = (DSPSignalNotification&) message; int sr = notif.getSampleRate(); @@ -2144,6 +2144,11 @@ if (DSPSignalNotification::match(message)) ui->warning->setText(""); } getRollupContents()->arrangeRollups(); + m_deviceCenterFrequency = notif.getCenterFrequency(); + m_basebandSampleRate = sr; + ui->deltaFrequency->setValueRange(false, 7, -sr/2, sr/2); + ui->deltaFrequencyLabel->setToolTip(tr("Range %1 %L2 Hz").arg(QChar(0xB1)).arg(sr/2)); + updateAbsoluteCenterFrequency(); return true; } else if (ADSBDemodReport::MsgReportADSB::match(message)) @@ -2211,6 +2216,7 @@ void ADSBDemodGUI::on_deltaFrequency_changed(qint64 value) { m_channelMarker.setCenterFrequency(value); m_settings.m_inputFrequencyOffset = m_channelMarker.getCenterFrequency(); + updateAbsoluteCenterFrequency(); applySettings(); } @@ -2776,7 +2782,6 @@ void ADSBDemodGUI::onMenuDialogCalled(const QPoint &p) dialog.move(p); dialog.exec(); - m_settings.m_inputFrequencyOffset = m_channelMarker.getCenterFrequency(); m_settings.m_rgbColor = m_channelMarker.getColor().rgb(); m_settings.m_title = m_channelMarker.getTitle(); m_settings.m_useReverseAPI = dialog.useReverseAPI(); @@ -3675,6 +3680,8 @@ ADSBDemodGUI::ADSBDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Baseb m_pluginAPI(pluginAPI), m_deviceUISet(deviceUISet), m_channelMarker(this), + m_deviceCenterFrequency(0), + m_basebandSampleRate(1), m_basicSettingsShown(false), m_doApplySettings(true), m_tickCount(0), @@ -4691,3 +4698,8 @@ void ADSBDemodGUI::makeUIConnections() QObject::connect(ui->logFilename, &QToolButton::clicked, this, &ADSBDemodGUI::on_logFilename_clicked); QObject::connect(ui->logOpen, &QToolButton::clicked, this, &ADSBDemodGUI::on_logOpen_clicked); } + +void ADSBDemodGUI::updateAbsoluteCenterFrequency() +{ + setStatusFrequency(m_deviceCenterFrequency + m_settings.m_inputFrequencyOffset); +} diff --git a/plugins/channelrx/demodadsb/adsbdemodgui.h b/plugins/channelrx/demodadsb/adsbdemodgui.h index c37be1e15..d3d1fa96b 100644 --- a/plugins/channelrx/demodadsb/adsbdemodgui.h +++ b/plugins/channelrx/demodadsb/adsbdemodgui.h @@ -785,6 +785,8 @@ private: ChannelMarker m_channelMarker; RollupState m_rollupState; ADSBDemodSettings m_settings; + qint64 m_deviceCenterFrequency; + int m_basebandSampleRate; bool m_basicSettingsShown; bool m_doApplySettings; @@ -848,6 +850,7 @@ private: void displayStreamIndex(); bool handleMessage(const Message& message); void makeUIConnections(); + void updateAbsoluteCenterFrequency(); void updatePosition(Aircraft *aircraft); bool updateLocalPosition(Aircraft *aircraft, double latitude, double longitude, bool surfacePosition); diff --git a/plugins/channelrx/demodais/aisdemod.cpp b/plugins/channelrx/demodais/aisdemod.cpp index c24d3d601..9079b2b93 100644 --- a/plugins/channelrx/demodais/aisdemod.cpp +++ b/plugins/channelrx/demodais/aisdemod.cpp @@ -151,6 +151,10 @@ bool AISDemod::handleMessage(const Message& cmd) qDebug() << "AISDemod::handleMessage: DSPSignalNotification"; m_basebandSink->getInputMessageQueue()->push(rep); + if (getMessageQueueToGUI()) { + getMessageQueueToGUI()->push(new DSPSignalNotification(notif)); + } + return true; } else if (MsgMessage::match(cmd)) diff --git a/plugins/channelrx/demodais/aisdemodgui.cpp b/plugins/channelrx/demodais/aisdemodgui.cpp index ab01d438a..2d9c805bf 100644 --- a/plugins/channelrx/demodais/aisdemodgui.cpp +++ b/plugins/channelrx/demodais/aisdemodgui.cpp @@ -217,6 +217,16 @@ bool AISDemodGUI::handleMessage(const Message& message) messageReceived(report.getMessage(), report.getDateTime()); return true; } + else if (DSPSignalNotification::match(message)) + { + DSPSignalNotification& notif = (DSPSignalNotification&) message; + m_deviceCenterFrequency = notif.getCenterFrequency(); + m_basebandSampleRate = notif.getSampleRate(); + ui->deltaFrequency->setValueRange(false, 7, -m_basebandSampleRate/2, m_basebandSampleRate/2); + ui->deltaFrequencyLabel->setToolTip(tr("Range %1 %L2 Hz").arg(QChar(0xB1)).arg(m_basebandSampleRate/2)); + updateAbsoluteCenterFrequency(); + return true; + } return false; } @@ -250,6 +260,7 @@ void AISDemodGUI::on_deltaFrequency_changed(qint64 value) { m_channelMarker.setCenterFrequency(value); m_settings.m_inputFrequencyOffset = m_channelMarker.getCenterFrequency(); + updateAbsoluteCenterFrequency(); applySettings(); } @@ -426,6 +437,8 @@ AISDemodGUI::AISDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Baseban m_pluginAPI(pluginAPI), m_deviceUISet(deviceUISet), m_channelMarker(this), + m_deviceCenterFrequency(0), + m_basebandSampleRate(1), m_doApplySettings(true), m_tickCount(0) { @@ -617,6 +630,7 @@ void AISDemodGUI::displaySettings() filter(); getRollupContents()->restoreState(m_rollupState); + updateAbsoluteCenterFrequency(); blockApplySettings(false); } @@ -778,3 +792,8 @@ void AISDemodGUI::makeUIConnections() QObject::connect(ui->logFilename, &QToolButton::clicked, this, &AISDemodGUI::on_logFilename_clicked); QObject::connect(ui->logOpen, &QToolButton::clicked, this, &AISDemodGUI::on_logOpen_clicked); } + +void AISDemodGUI::updateAbsoluteCenterFrequency() +{ + setStatusFrequency(m_deviceCenterFrequency + m_settings.m_inputFrequencyOffset); +} diff --git a/plugins/channelrx/demodais/aisdemodgui.h b/plugins/channelrx/demodais/aisdemodgui.h index 727ac72eb..fb977d1f0 100644 --- a/plugins/channelrx/demodais/aisdemodgui.h +++ b/plugins/channelrx/demodais/aisdemodgui.h @@ -78,6 +78,8 @@ private: ChannelMarker m_channelMarker; RollupState m_rollupState; AISDemodSettings m_settings; + qint64 m_deviceCenterFrequency; + int m_basebandSampleRate; bool m_doApplySettings; ScopeVis* m_scopeVis; @@ -98,6 +100,7 @@ private: void messageReceived(const QByteArray& message, const QDateTime& dateTime); bool handleMessage(const Message& message); void makeUIConnections(); + void updateAbsoluteCenterFrequency(); void leaveEvent(QEvent*); void enterEvent(QEvent*); diff --git a/plugins/channelrx/demodam/amdemod.cpp b/plugins/channelrx/demodam/amdemod.cpp index 2bbfbd814..b420cf5b5 100644 --- a/plugins/channelrx/demodam/amdemod.cpp +++ b/plugins/channelrx/demodam/amdemod.cpp @@ -167,6 +167,10 @@ bool AMDemod::handleMessage(const Message& cmd) qDebug() << "AMDemod::handleMessage: DSPSignalNotification"; m_basebandSink->getInputMessageQueue()->push(rep); + if (getMessageQueueToGUI()) { + getMessageQueueToGUI()->push(new DSPSignalNotification(notif)); + } + return true; } else if (MainCore::MsgChannelDemodQuery::match(cmd)) diff --git a/plugins/channelrx/demodam/amdemodgui.cpp b/plugins/channelrx/demodam/amdemodgui.cpp index 0b7f8c723..7a3eda71d 100644 --- a/plugins/channelrx/demodam/amdemodgui.cpp +++ b/plugins/channelrx/demodam/amdemodgui.cpp @@ -24,17 +24,17 @@ #include "device/deviceuiset.h" #include "dsp/dspengine.h" -#include "ui_amdemodgui.h" +#include "dsp/dspcommands.h" #include "plugin/pluginapi.h" #include "util/simpleserializer.h" #include "util/db.h" #include "gui/basicchannelsettingsdialog.h" #include "gui/devicestreamselectiondialog.h" -#include "dsp/dspengine.h" #include "gui/crightclickenabler.h" #include "gui/audioselectdialog.h" #include "maincore.h" +#include "ui_amdemodgui.h" #include "amdemod.h" AMDemodGUI* AMDemodGUI::create(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel) @@ -85,6 +85,17 @@ bool AMDemodGUI::handleMessage(const Message& message) blockApplySettings(false); return true; } + else if (DSPSignalNotification::match(message)) + { + DSPSignalNotification& notif = (DSPSignalNotification&) message; + m_deviceCenterFrequency = notif.getCenterFrequency(); + m_basebandSampleRate = notif.getSampleRate(); + ui->deltaFrequency->setValueRange(false, 7, -m_basebandSampleRate/2, m_basebandSampleRate/2); + ui->deltaFrequencyLabel->setToolTip(tr("Range %1 %L2 Hz").arg(QChar(0xB1)).arg(m_basebandSampleRate/2)); + updateAbsoluteCenterFrequency(); + + return true; + } return false; } @@ -118,6 +129,7 @@ void AMDemodGUI::on_deltaFrequency_changed(qint64 value) { m_channelMarker.setCenterFrequency(value); m_settings.m_inputFrequencyOffset = m_channelMarker.getCenterFrequency(); + updateAbsoluteCenterFrequency(); applySettings(); } @@ -199,7 +211,6 @@ void AMDemodGUI::onMenuDialogCalled(const QPoint &p) dialog.move(p); dialog.exec(); - m_settings.m_inputFrequencyOffset = m_channelMarker.getCenterFrequency(); m_settings.m_rgbColor = m_channelMarker.getColor().rgb(); m_settings.m_title = m_channelMarker.getTitle(); m_settings.m_useReverseAPI = dialog.useReverseAPI(); @@ -238,6 +249,8 @@ AMDemodGUI::AMDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandS m_pluginAPI(pluginAPI), m_deviceUISet(deviceUISet), m_channelMarker(this), + m_deviceCenterFrequency(0), + m_basebandSampleRate(1), m_doApplySettings(true), m_squelchOpen(false), m_audioSampleRate(-1), @@ -378,6 +391,7 @@ void AMDemodGUI::displaySettings() displayStreamIndex(); getRollupContents()->restoreState(m_rollupState); + updateAbsoluteCenterFrequency(); blockApplySettings(false); } @@ -493,3 +507,8 @@ void AMDemodGUI::makeUIConnections() QObject::connect(ui->squelch, &QSlider::valueChanged, this, &AMDemodGUI::on_squelch_valueChanged); QObject::connect(ui->audioMute, &QToolButton::toggled, this, &AMDemodGUI::on_audioMute_toggled); } + +void AMDemodGUI::updateAbsoluteCenterFrequency() +{ + setStatusFrequency(m_deviceCenterFrequency + m_settings.m_inputFrequencyOffset); +} diff --git a/plugins/channelrx/demodam/amdemodgui.h b/plugins/channelrx/demodam/amdemodgui.h index bfb834b78..7e838efc0 100644 --- a/plugins/channelrx/demodam/amdemodgui.h +++ b/plugins/channelrx/demodam/amdemodgui.h @@ -51,6 +51,8 @@ private: ChannelMarker m_channelMarker; RollupState m_rollupState; AMDemodSettings m_settings; + qint64 m_deviceCenterFrequency; + int m_basebandSampleRate; bool m_doApplySettings; AMDemod* m_amDemod; @@ -72,6 +74,7 @@ private: void displayStreamIndex(); bool handleMessage(const Message& message); void makeUIConnections(); + void updateAbsoluteCenterFrequency(); void leaveEvent(QEvent*); void enterEvent(QEvent*); diff --git a/plugins/channelrx/demodapt/aptdemod.cpp b/plugins/channelrx/demodapt/aptdemod.cpp index 7e0380edb..96b01b626 100644 --- a/plugins/channelrx/demodapt/aptdemod.cpp +++ b/plugins/channelrx/demodapt/aptdemod.cpp @@ -209,8 +209,9 @@ bool APTDemod::handleMessage(const Message& cmd) qDebug() << "APTDemod::handleMessage: DSPSignalNotification"; m_basebandSink->getInputMessageQueue()->push(rep); // Forward to GUI if any - if (m_guiMessageQueue) + if (m_guiMessageQueue) { m_guiMessageQueue->push(new DSPSignalNotification(notif)); + } return true; } diff --git a/plugins/channelrx/demodapt/aptdemodgui.cpp b/plugins/channelrx/demodapt/aptdemodgui.cpp index 1aa2d942f..43720e084 100644 --- a/plugins/channelrx/demodapt/aptdemodgui.cpp +++ b/plugins/channelrx/demodapt/aptdemodgui.cpp @@ -274,7 +274,11 @@ bool APTDemodGUI::handleMessage(const Message& message) else if (DSPSignalNotification::match(message)) { DSPSignalNotification& notif = (DSPSignalNotification&) message; + m_deviceCenterFrequency = notif.getCenterFrequency(); m_basebandSampleRate = notif.getSampleRate(); + ui->deltaFrequency->setValueRange(false, 7, -m_basebandSampleRate/2, m_basebandSampleRate/2); + ui->deltaFrequencyLabel->setToolTip(tr("Range %1 %L2 Hz").arg(QChar(0xB1)).arg(m_basebandSampleRate/2)); + updateAbsoluteCenterFrequency(); return true; } @@ -310,6 +314,7 @@ void APTDemodGUI::on_deltaFrequency_changed(qint64 value) { m_channelMarker.setCenterFrequency(value); m_settings.m_inputFrequencyOffset = m_channelMarker.getCenterFrequency(); + updateAbsoluteCenterFrequency(); applySettings(); } @@ -592,6 +597,7 @@ APTDemodGUI::APTDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Baseban m_pluginAPI(pluginAPI), m_deviceUISet(deviceUISet), m_channelMarker(this), + m_deviceCenterFrequency(0), m_doApplySettings(true), m_tickCount(0), m_scene(nullptr), @@ -779,6 +785,7 @@ void APTDemodGUI::displaySettings() displayStreamIndex(); getRollupContents()->restoreState(m_rollupState); + updateAbsoluteCenterFrequency(); blockApplySettings(false); } @@ -912,3 +919,8 @@ void APTDemodGUI::makeUIConnections() QObject::connect(ui->zoomOut, &QToolButton::clicked, this, &APTDemodGUI::on_zoomOut_clicked); QObject::connect(ui->zoomAll, &ButtonSwitch::clicked, this, &APTDemodGUI::on_zoomAll_clicked); } + +void APTDemodGUI::updateAbsoluteCenterFrequency() +{ + setStatusFrequency(m_deviceCenterFrequency + m_settings.m_inputFrequencyOffset); +} diff --git a/plugins/channelrx/demodapt/aptdemodgui.h b/plugins/channelrx/demodapt/aptdemodgui.h index 52981e464..ecd76ef13 100644 --- a/plugins/channelrx/demodapt/aptdemodgui.h +++ b/plugins/channelrx/demodapt/aptdemodgui.h @@ -95,6 +95,7 @@ private: ChannelMarker m_channelMarker; RollupState m_rollupState; APTDemodSettings m_settings; + qint64 m_deviceCenterFrequency; bool m_doApplySettings; APTDemod* m_aptDemod; @@ -124,6 +125,7 @@ private: void displayStreamIndex(); bool handleMessage(const Message& message); void makeUIConnections(); + void updateAbsoluteCenterFrequency(); void deleteImageFromMap(const QString &name); void resetDecoder(); diff --git a/plugins/channelrx/demodatv/atvdemod.cpp b/plugins/channelrx/demodatv/atvdemod.cpp index 58eddf8b7..7770a6348 100644 --- a/plugins/channelrx/demodatv/atvdemod.cpp +++ b/plugins/channelrx/demodatv/atvdemod.cpp @@ -127,10 +127,8 @@ bool ATVDemod::handleMessage(const Message& cmd) m_basebandSink->getInputMessageQueue()->push(notifToSink); // Forward to GUI - if (getMessageQueueToGUI()) - { - DSPSignalNotification *notifToGUI = new DSPSignalNotification(notif); - getMessageQueueToGUI()->push(notifToGUI); + if (getMessageQueueToGUI()) { + getMessageQueueToGUI()->push(new DSPSignalNotification(notif)); } return true; diff --git a/plugins/channelrx/demodatv/atvdemodgui.cpp b/plugins/channelrx/demodatv/atvdemodgui.cpp index 8c7b72861..e76ff36ed 100644 --- a/plugins/channelrx/demodatv/atvdemodgui.cpp +++ b/plugins/channelrx/demodatv/atvdemodgui.cpp @@ -121,6 +121,7 @@ void ATVDemodGUI::displaySettings() applySampleRate(); getRollupContents()->restoreState(m_rollupState); + updateAbsoluteCenterFrequency(); m_doApplySettings = true; } @@ -168,6 +169,10 @@ bool ATVDemodGUI::handleMessage(const Message& message) { DSPSignalNotification& notif = (DSPSignalNotification&) message; m_basebandSampleRate = notif.getSampleRate(); + m_deviceCenterFrequency = notif.getCenterFrequency(); + ui->deltaFrequency->setValueRange(false, 8, -m_basebandSampleRate/2, m_basebandSampleRate/2); + ui->deltaFrequencyLabel->setToolTip(tr("Range %1 %L2 Hz").arg(QChar(0xB1)).arg(m_basebandSampleRate/2)); + updateAbsoluteCenterFrequency(); applySampleRate(); return true; @@ -219,6 +224,7 @@ ATVDemodGUI::ATVDemodGUI(PluginAPI* objPluginAPI, DeviceUISet *deviceUISet, Base m_pluginAPI(objPluginAPI), m_deviceUISet(deviceUISet), m_channelMarker(this), + m_deviceCenterFrequency(0), m_doApplySettings(false), m_intTickCount(0), m_basebandSampleRate(48000) @@ -499,6 +505,7 @@ void ATVDemodGUI::on_deltaFrequency_changed(qint64 value) { m_settings.m_inputFrequencyOffset = value; m_channelMarker.setCenterFrequency(value); + updateAbsoluteCenterFrequency(); applySettings(); } @@ -592,3 +599,8 @@ void ATVDemodGUI::makeUIConnections() QObject::connect(ui->amScaleOffset, &QDial::valueChanged, this, &ATVDemodGUI::on_amScaleOffset_valueChanged); QObject::connect(ui->screenTabWidget, &QTabWidget::currentChanged, this, &ATVDemodGUI::on_screenTabWidget_currentChanged); } + +void ATVDemodGUI::updateAbsoluteCenterFrequency() +{ + setStatusFrequency(m_deviceCenterFrequency + m_settings.m_inputFrequencyOffset); +} diff --git a/plugins/channelrx/demodatv/atvdemodgui.h b/plugins/channelrx/demodatv/atvdemodgui.h index 793015d60..e66415e89 100644 --- a/plugins/channelrx/demodatv/atvdemodgui.h +++ b/plugins/channelrx/demodatv/atvdemodgui.h @@ -71,6 +71,7 @@ private: RollupState m_rollupState; ATVDemod* m_atvDemod; ATVDemodSettings m_settings; + qint64 m_deviceCenterFrequency; bool m_doApplySettings; @@ -97,6 +98,7 @@ private: void topTimeUpdate(); bool handleMessage(const Message& objMessage); void makeUIConnections(); + void updateAbsoluteCenterFrequency(); void leaveEvent(QEvent*); void enterEvent(QEvent*); diff --git a/plugins/channelrx/demodbfm/bfmdemod.cpp b/plugins/channelrx/demodbfm/bfmdemod.cpp index 64d8fad7c..2afb6de93 100644 --- a/plugins/channelrx/demodbfm/bfmdemod.cpp +++ b/plugins/channelrx/demodbfm/bfmdemod.cpp @@ -151,6 +151,10 @@ bool BFMDemod::handleMessage(const Message& cmd) qDebug() << "BFMDemod::handleMessage: DSPSignalNotification"; m_basebandSink->getInputMessageQueue()->push(rep); + if (getMessageQueueToGUI()) { + getMessageQueueToGUI()->push(new DSPSignalNotification(notif)); + } + return true; } else diff --git a/plugins/channelrx/demodbfm/bfmdemodgui.cpp b/plugins/channelrx/demodbfm/bfmdemodgui.cpp index 936ed0146..dfb2fd838 100644 --- a/plugins/channelrx/demodbfm/bfmdemodgui.cpp +++ b/plugins/channelrx/demodbfm/bfmdemodgui.cpp @@ -29,6 +29,7 @@ #include "dsp/dspengine.h" #include "dsp/spectrumvis.h" +#include "dsp/dspcommands.h" #include "gui/glspectrum.h" #include "plugin/pluginapi.h" #include "util/simpleserializer.h" @@ -110,6 +111,16 @@ bool BFMDemodGUI::handleMessage(const Message& message) blockApplySettings(false); return true; } + else if (DSPSignalNotification::match(message)) + { + DSPSignalNotification& notif = (DSPSignalNotification&) message; + m_deviceCenterFrequency = notif.getCenterFrequency(); + m_basebandSampleRate = notif.getSampleRate(); + ui->deltaFrequency->setValueRange(false, 8, -m_basebandSampleRate/2, m_basebandSampleRate/2); + ui->deltaFrequencyLabel->setToolTip(tr("Range %1 %L2 Hz").arg(QChar(0xB1)).arg(m_basebandSampleRate/2)); + updateAbsoluteCenterFrequency(); + return true; + } else { return false; @@ -145,6 +156,7 @@ void BFMDemodGUI::on_deltaFrequency_changed(qint64 value) { m_channelMarker.setCenterFrequency(value); m_settings.m_inputFrequencyOffset = m_channelMarker.getCenterFrequency(); + updateAbsoluteCenterFrequency(); applySettings(); } @@ -322,7 +334,6 @@ void BFMDemodGUI::onMenuDialogCalled(const QPoint &p) dialog.move(p); dialog.exec(); - m_settings.m_inputFrequencyOffset = m_channelMarker.getCenterFrequency(); m_settings.m_rgbColor = m_channelMarker.getColor().rgb(); m_settings.m_title = m_channelMarker.getTitle(); m_settings.m_useReverseAPI = dialog.useReverseAPI(); @@ -361,6 +372,8 @@ BFMDemodGUI::BFMDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Baseban m_pluginAPI(pluginAPI), m_deviceUISet(deviceUISet), m_channelMarker(this), + m_deviceCenterFrequency(0), + m_basebandSampleRate(1), m_rdsTimerCount(0), m_radiotext_AB_flag(false), m_rate(625000) @@ -483,6 +496,7 @@ void BFMDemodGUI::displaySettings() displayStreamIndex(); getRollupContents()->restoreState(m_rollupState); + updateAbsoluteCenterFrequency(); blockApplySettings(false); } @@ -847,3 +861,8 @@ void BFMDemodGUI::makeUIConnections() QObject::connect(ui->rds, &ButtonSwitch::clicked, this, &BFMDemodGUI::on_rds_clicked); QObject::connect(ui->clearData, &QPushButton::clicked, this, &BFMDemodGUI::on_clearData_clicked); } + +void BFMDemodGUI::updateAbsoluteCenterFrequency() +{ + setStatusFrequency(m_deviceCenterFrequency + m_settings.m_inputFrequencyOffset); +} diff --git a/plugins/channelrx/demodbfm/bfmdemodgui.h b/plugins/channelrx/demodbfm/bfmdemodgui.h index c6d69ae75..79892b3a8 100644 --- a/plugins/channelrx/demodbfm/bfmdemodgui.h +++ b/plugins/channelrx/demodbfm/bfmdemodgui.h @@ -68,6 +68,8 @@ private: ChannelMarker m_channelMarker; RollupState m_rollupState; BFMDemodSettings m_settings; + qint64 m_deviceCenterFrequency; + int m_basebandSampleRate; bool m_doApplySettings; int m_rdsTimerCount; bool m_radiotext_AB_flag; @@ -90,6 +92,7 @@ private: void rdsUpdateFixedFields(); bool handleMessage(const Message& message); void makeUIConnections(); + void updateAbsoluteCenterFrequency(); void leaveEvent(QEvent*); void enterEvent(QEvent*); diff --git a/plugins/channelrx/demodchirpchat/chirpchatdemod.cpp b/plugins/channelrx/demodchirpchat/chirpchatdemod.cpp index c03c26096..17d261310 100644 --- a/plugins/channelrx/demodchirpchat/chirpchatdemod.cpp +++ b/plugins/channelrx/demodchirpchat/chirpchatdemod.cpp @@ -285,10 +285,8 @@ bool ChirpChatDemod::handleMessage(const Message& cmd) qDebug() << "ChirpChatDemod::handleMessage: DSPSignalNotification: m_basebandSampleRate: " << m_basebandSampleRate; m_basebandSink->getInputMessageQueue()->push(rep); - if (getMessageQueueToGUI()) - { - DSPSignalNotification* repToGUI = new DSPSignalNotification(notif); // make a copy - getMessageQueueToGUI()->push(repToGUI); + if (getMessageQueueToGUI()) { + getMessageQueueToGUI()->push(new DSPSignalNotification(notif)); // make a copy } return true; diff --git a/plugins/channelrx/demodchirpchat/chirpchatdemodgui.cpp b/plugins/channelrx/demodchirpchat/chirpchatdemodgui.cpp index 211c1c2f4..412a12e14 100644 --- a/plugins/channelrx/demodchirpchat/chirpchatdemodgui.cpp +++ b/plugins/channelrx/demodchirpchat/chirpchatdemodgui.cpp @@ -81,6 +81,7 @@ bool ChirpChatDemodGUI::handleMessage(const Message& message) if (DSPSignalNotification::match(message)) { DSPSignalNotification& notif = (DSPSignalNotification&) message; + m_deviceCenterFrequency = notif.getCenterFrequency(); int basebandSampleRate = notif.getSampleRate(); qDebug() << "ChirpChatDemodGUI::handleMessage: DSPSignalNotification: m_basebandSampleRate: " << basebandSampleRate; @@ -90,6 +91,10 @@ bool ChirpChatDemodGUI::handleMessage(const Message& message) setBandwidths(); } + ui->deltaFrequency->setValueRange(false, 7, -m_basebandSampleRate/2, m_basebandSampleRate/2); + ui->deltaFrequencyLabel->setToolTip(tr("Range %1 %L2 Hz").arg(QChar(0xB1)).arg(m_basebandSampleRate/2)); + updateAbsoluteCenterFrequency(); + return true; } else if (ChirpChatDemod::MsgReportDecodeBytes::match(message)) @@ -151,6 +156,7 @@ void ChirpChatDemodGUI::on_deltaFrequency_changed(qint64 value) { m_channelMarker.setCenterFrequency(value); m_settings.m_inputFrequencyOffset = m_channelMarker.getCenterFrequency(); + updateAbsoluteCenterFrequency(); applySettings(); } @@ -340,7 +346,6 @@ void ChirpChatDemodGUI::onMenuDialogCalled(const QPoint &p) dialog.move(p); dialog.exec(); - m_settings.m_inputFrequencyOffset = m_channelMarker.getCenterFrequency(); m_settings.m_rgbColor = m_channelMarker.getColor().rgb(); m_settings.m_title = m_channelMarker.getTitle(); m_settings.m_useReverseAPI = dialog.useReverseAPI(); @@ -379,6 +384,7 @@ ChirpChatDemodGUI::ChirpChatDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUI m_pluginAPI(pluginAPI), m_deviceUISet(deviceUISet), m_channelMarker(this), + m_deviceCenterFrequency(0), m_basebandSampleRate(250000), m_doApplySettings(true), m_tickCount(0) @@ -504,6 +510,7 @@ void ChirpChatDemodGUI::displaySettings() displaySquelch(); getRollupContents()->restoreState(m_rollupState); + updateAbsoluteCenterFrequency(); blockApplySettings(false); } @@ -793,3 +800,8 @@ void ChirpChatDemodGUI::makeUIConnections() QObject::connect(ui->udpAddress, &QLineEdit::editingFinished, this, &ChirpChatDemodGUI::on_udpAddress_editingFinished); QObject::connect(ui->udpPort, &QLineEdit::editingFinished, this, &ChirpChatDemodGUI::on_udpPort_editingFinished); } + +void ChirpChatDemodGUI::updateAbsoluteCenterFrequency() +{ + setStatusFrequency(m_deviceCenterFrequency + m_settings.m_inputFrequencyOffset); +} diff --git a/plugins/channelrx/demodchirpchat/chirpchatdemodgui.h b/plugins/channelrx/demodchirpchat/chirpchatdemodgui.h index 3d62b5bff..ea5a547cc 100644 --- a/plugins/channelrx/demodchirpchat/chirpchatdemodgui.h +++ b/plugins/channelrx/demodchirpchat/chirpchatdemodgui.h @@ -97,6 +97,7 @@ private: ChannelMarker m_channelMarker; RollupState m_rollupState; ChirpChatDemodSettings m_settings; + qint64 m_deviceCenterFrequency; int m_basebandSampleRate; bool m_doApplySettings; @@ -124,6 +125,7 @@ private: void resetLoRaStatus(); bool handleMessage(const Message& message); void makeUIConnections(); + void updateAbsoluteCenterFrequency(); }; #endif // INCLUDE_CHIRPCHATDEMODGUI_H diff --git a/plugins/channelrx/demoddab/dabdemod.cpp b/plugins/channelrx/demoddab/dabdemod.cpp index 245ccfb47..e42c47928 100644 --- a/plugins/channelrx/demoddab/dabdemod.cpp +++ b/plugins/channelrx/demoddab/dabdemod.cpp @@ -162,10 +162,8 @@ bool DABDemod::handleMessage(const Message& cmd) qDebug() << "DABDemod::handleMessage: DSPSignalNotification"; m_basebandSink->getInputMessageQueue()->push(rep); // Forward to GUI if any - if (m_guiMessageQueue) - { - rep = new DSPSignalNotification(notif); - m_guiMessageQueue->push(rep); + if (m_guiMessageQueue) { + m_guiMessageQueue->push(new DSPSignalNotification(notif)); } return true; diff --git a/plugins/channelrx/demoddab/dabdemodgui.cpp b/plugins/channelrx/demoddab/dabdemodgui.cpp index ebc036720..6c7feeddc 100644 --- a/plugins/channelrx/demoddab/dabdemodgui.cpp +++ b/plugins/channelrx/demoddab/dabdemodgui.cpp @@ -200,15 +200,22 @@ bool DABDemodGUI::handleMessage(const Message& message) else if (DSPSignalNotification::match(message)) { DSPSignalNotification& notif = (DSPSignalNotification&) message; + m_deviceCenterFrequency = notif.getCenterFrequency(); m_basebandSampleRate = notif.getSampleRate(); + ui->deltaFrequency->setValueRange(false, 7, -m_basebandSampleRate/2, m_basebandSampleRate/2); + ui->deltaFrequencyLabel->setToolTip(tr("Range %1 %L2 Hz").arg(QChar(0xB1)).arg(m_basebandSampleRate/2)); bool srTooLow = m_basebandSampleRate < 2048000; ui->warning->setVisible(srTooLow); + if (srTooLow) { ui->warning->setText("Sample rate must be >= 2048000"); } else { ui->warning->setText(""); } + getRollupContents()->arrangeRollups(); + updateAbsoluteCenterFrequency(); + return true; } else if (DABDemod::MsgDABEnsembleName::match(message)) @@ -318,6 +325,7 @@ void DABDemodGUI::on_deltaFrequency_changed(qint64 value) { m_channelMarker.setCenterFrequency(value); m_settings.m_inputFrequencyOffset = m_channelMarker.getCenterFrequency(); + updateAbsoluteCenterFrequency(); applySettings(); } @@ -439,6 +447,7 @@ DABDemodGUI::DABDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Baseban m_pluginAPI(pluginAPI), m_deviceUISet(deviceUISet), m_channelMarker(this), + m_deviceCenterFrequency(0), m_doApplySettings(true), m_tickCount(0), m_channelFreq(0.0) @@ -574,6 +583,7 @@ void DABDemodGUI::displaySettings() filter(); getRollupContents()->restoreState(m_rollupState); + updateAbsoluteCenterFrequency(); blockApplySettings(false); } @@ -708,3 +718,8 @@ void DABDemodGUI::makeUIConnections() QObject::connect(ui->programs, &QTableWidget::cellDoubleClicked, this, &DABDemodGUI::on_programs_cellDoubleClicked); QObject::connect(ui->channel, QOverload::of(&QComboBox::currentIndexChanged), this, &DABDemodGUI::on_channel_currentIndexChanged); } + +void DABDemodGUI::updateAbsoluteCenterFrequency() +{ + setStatusFrequency(m_deviceCenterFrequency + m_settings.m_inputFrequencyOffset); +} diff --git a/plugins/channelrx/demoddab/dabdemodgui.h b/plugins/channelrx/demoddab/dabdemodgui.h index 9b080e1ba..2ce282c2c 100644 --- a/plugins/channelrx/demoddab/dabdemodgui.h +++ b/plugins/channelrx/demoddab/dabdemodgui.h @@ -73,6 +73,7 @@ private: ChannelMarker m_channelMarker; RollupState m_rollupState; DABDemodSettings m_settings; + qint64 m_deviceCenterFrequency; bool m_doApplySettings; DABDemod* m_dabDemod; @@ -93,6 +94,7 @@ private: void addProgramName(const DABDemod::MsgDABProgramName& program); bool handleMessage(const Message& message); void makeUIConnections(); + void updateAbsoluteCenterFrequency(); void leaveEvent(QEvent*); void enterEvent(QEvent*); diff --git a/plugins/channelrx/demoddatv/datvdemod.cpp b/plugins/channelrx/demoddatv/datvdemod.cpp index b1c67c74d..988508501 100644 --- a/plugins/channelrx/demoddatv/datvdemod.cpp +++ b/plugins/channelrx/demoddatv/datvdemod.cpp @@ -138,6 +138,11 @@ bool DATVDemod::handleMessage(const Message& cmd) DSPSignalNotification* notifToSink = new DSPSignalNotification(notif); // make a copy m_basebandSink->getInputMessageQueue()->push(notifToSink); + // Forward to GUI if any + if (getMessageQueueToGUI()) { + getMessageQueueToGUI()->push(new DSPSignalNotification(notif)); + } + return true; } else diff --git a/plugins/channelrx/demoddatv/datvdemodgui.cpp b/plugins/channelrx/demoddatv/datvdemodgui.cpp index 5af728860..5f5d08454 100644 --- a/plugins/channelrx/demoddatv/datvdemodgui.cpp +++ b/plugins/channelrx/demoddatv/datvdemodgui.cpp @@ -23,16 +23,17 @@ #include "device/deviceuiset.h" #include "dsp/dspengine.h" +#include "dsp/dspcommands.h" #include "plugin/pluginapi.h" #include "util/simpleserializer.h" #include "util/db.h" -#include "ui_datvdemodgui.h" #include "gui/crightclickenabler.h" #include "gui/audioselectdialog.h" #include "gui/basicchannelsettingsdialog.h" #include "gui/devicestreamselectiondialog.h" #include "mainwindow.h" +#include "ui_datvdemodgui.h" #include "datvdemodreport.h" #include "datvdvbs2ldpcdialog.h" #include "datvdemodgui.h" @@ -100,6 +101,16 @@ bool DATVDemodGUI::handleMessage(const Message& message) displaySettings(); return true; } + else if (DSPSignalNotification::match(message)) + { + DSPSignalNotification& notif = (DSPSignalNotification&) message; + m_deviceCenterFrequency = notif.getCenterFrequency(); + m_basebandSampleRate = notif.getSampleRate(); + ui->deltaFrequency->setValueRange(false, 8, -m_basebandSampleRate/2, m_basebandSampleRate/2); + ui->deltaFrequencyLabel->setToolTip(tr("Range %1 %L2 Hz").arg(QChar(0xB1)).arg(m_basebandSampleRate/2)); + updateAbsoluteCenterFrequency(); + return true; + } else { return false; @@ -154,7 +165,6 @@ void DATVDemodGUI::onMenuDialogCalled(const QPoint &p) dialog.move(p); dialog.exec(); - m_settings.m_centerFrequency = m_objChannelMarker.getCenterFrequency(); m_settings.m_rgbColor = m_objChannelMarker.getColor().rgb(); m_settings.m_title = m_objChannelMarker.getTitle(); m_settings.m_useReverseAPI = dialog.useReverseAPI(); @@ -188,16 +198,18 @@ void DATVDemodGUI::onMenuDialogCalled(const QPoint &p) } DATVDemodGUI::DATVDemodGUI(PluginAPI* objPluginAPI, DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel, QWidget* objParent) : - ChannelGUI(objParent), - ui(new Ui::DATVDemodGUI), - m_objPluginAPI(objPluginAPI), - m_deviceUISet(deviceUISet), - m_objChannelMarker(this), - m_blnBasicSettingsShown(false), - m_blnDoApplySettings(true), - m_modcodModulationIndex(-1), - m_modcodCodeRateIndex(-1), - m_cstlnSetByModcod(false) + ChannelGUI(objParent), + ui(new Ui::DATVDemodGUI), + m_objPluginAPI(objPluginAPI), + m_deviceUISet(deviceUISet), + m_objChannelMarker(this), + m_deviceCenterFrequency(0), + m_basebandSampleRate(1), + m_blnBasicSettingsShown(false), + m_blnDoApplySettings(true), + m_modcodModulationIndex(-1), + m_modcodCodeRateIndex(-1), + m_cstlnSetByModcod(false) { ui->setupUi(getRollupContents()); getRollupContents()->arrangeRollups(); @@ -400,6 +412,7 @@ void DATVDemodGUI::displaySettings() } getRollupContents()->restoreState(m_rollupState); + updateAbsoluteCenterFrequency(); blockApplySettings(false); } @@ -779,6 +792,7 @@ void DATVDemodGUI::on_deltaFrequency_changed(qint64 value) { m_objChannelMarker.setCenterFrequency(value); m_settings.m_centerFrequency = m_objChannelMarker.getCenterFrequency(); + updateAbsoluteCenterFrequency(); applySettings(); } @@ -952,3 +966,8 @@ void DATVDemodGUI::makeUIConnections() QObject::connect(ui->udpTSPort, &QLineEdit::editingFinished, this, &DATVDemodGUI::on_udpTSPort_editingFinished); QObject::connect(ui->playerEnable, &QCheckBox::clicked, this, &DATVDemodGUI::on_playerEnable_clicked); } + +void DATVDemodGUI::updateAbsoluteCenterFrequency() +{ + setStatusFrequency(m_deviceCenterFrequency + m_settings.m_centerFrequency); +} diff --git a/plugins/channelrx/demoddatv/datvdemodgui.h b/plugins/channelrx/demoddatv/datvdemodgui.h index 5ef25996c..a3028f206 100644 --- a/plugins/channelrx/demoddatv/datvdemodgui.h +++ b/plugins/channelrx/demoddatv/datvdemodgui.h @@ -114,6 +114,8 @@ private: DATVDemod* m_datvDemod; MessageQueue m_inputMessageQueue; DATVDemodSettings m_settings; + qint64 m_deviceCenterFrequency; + int m_basebandSampleRate; QTimer m_objTimer; qint64 m_intPreviousDecodedData; @@ -146,6 +148,7 @@ private: void enterEvent(QEvent*); bool handleMessage(const Message& objMessage); void makeUIConnections(); + void updateAbsoluteCenterFrequency(); }; #endif // INCLUDE_DATVDEMODGUI_H diff --git a/plugins/channelrx/demoddsd/dsddemod.cpp b/plugins/channelrx/demoddsd/dsddemod.cpp index 05e75178a..051c6fc66 100644 --- a/plugins/channelrx/demoddsd/dsddemod.cpp +++ b/plugins/channelrx/demoddsd/dsddemod.cpp @@ -149,6 +149,10 @@ bool DSDDemod::handleMessage(const Message& cmd) DSPSignalNotification* rep = new DSPSignalNotification(notif); // make a copy qDebug() << "DSDDemod::handleMessage: DSPSignalNotification"; m_basebandSink->getInputMessageQueue()->push(rep); + // Forward to GUI if any + if (getMessageQueueToGUI()) { + getMessageQueueToGUI()->push(new DSPSignalNotification(notif)); + } return true; } diff --git a/plugins/channelrx/demoddsd/dsddemodgui.cpp b/plugins/channelrx/demoddsd/dsddemodgui.cpp index ae2e33071..5d1213cf2 100644 --- a/plugins/channelrx/demoddsd/dsddemodgui.cpp +++ b/plugins/channelrx/demoddsd/dsddemodgui.cpp @@ -22,6 +22,7 @@ #include "ui_dsddemodgui.h" #include "dsp/scopevisxy.h" +#include "dsp/dspcommands.h" #include "plugin/pluginapi.h" #include "util/simpleserializer.h" #include "util/db.h" @@ -94,6 +95,16 @@ bool DSDDemodGUI::handleMessage(const Message& message) blockApplySettings(false); return true; } + else if (DSPSignalNotification::match(message)) + { + DSPSignalNotification& notif = (DSPSignalNotification&) message; + m_deviceCenterFrequency = notif.getCenterFrequency(); + m_basebandSampleRate = notif.getSampleRate(); + ui->deltaFrequency->setValueRange(false, 7, -m_basebandSampleRate/2, m_basebandSampleRate/2); + ui->deltaFrequencyLabel->setToolTip(tr("Range %1 %L2 Hz").arg(QChar(0xB1)).arg(m_basebandSampleRate/2)); + updateAbsoluteCenterFrequency(); + return true; + } else { return false; @@ -117,6 +128,7 @@ void DSDDemodGUI::on_deltaFrequency_changed(qint64 value) { m_channelMarker.setCenterFrequency(value); m_settings.m_inputFrequencyOffset = m_channelMarker.getCenterFrequency(); + updateAbsoluteCenterFrequency(); applySettings(); } @@ -266,7 +278,6 @@ void DSDDemodGUI::onMenuDialogCalled(const QPoint &p) dialog.move(p); dialog.exec(); - m_settings.m_inputFrequencyOffset = m_channelMarker.getCenterFrequency(); m_settings.m_rgbColor = m_channelMarker.getColor().rgb(); m_settings.m_title = m_channelMarker.getTitle(); m_settings.m_useReverseAPI = dialog.useReverseAPI(); @@ -311,6 +322,8 @@ DSDDemodGUI::DSDDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Baseban m_pluginAPI(pluginAPI), m_deviceUISet(deviceUISet), m_channelMarker(this), + m_deviceCenterFrequency(0), + m_basebandSampleRate(1), m_doApplySettings(true), m_enableCosineFiltering(false), m_syncOrConstellation(false), @@ -468,6 +481,7 @@ void DSDDemodGUI::displaySettings() displayStreamIndex(); getRollupContents()->restoreState(m_rollupState); + updateAbsoluteCenterFrequency(); blockApplySettings(false); } @@ -640,3 +654,8 @@ void DSDDemodGUI::makeUIConnections() QObject::connect(ui->symbolPLLLock, &QToolButton::toggled, this, &DSDDemodGUI::on_symbolPLLLock_toggled); QObject::connect(ui->viewStatusLog, &QPushButton::clicked, this, &DSDDemodGUI::on_viewStatusLog_clicked); } + +void DSDDemodGUI::updateAbsoluteCenterFrequency() +{ + setStatusFrequency(m_deviceCenterFrequency + m_settings.m_inputFrequencyOffset); +} diff --git a/plugins/channelrx/demoddsd/dsddemodgui.h b/plugins/channelrx/demoddsd/dsddemodgui.h index 57f072a73..55453875f 100644 --- a/plugins/channelrx/demoddsd/dsddemodgui.h +++ b/plugins/channelrx/demoddsd/dsddemodgui.h @@ -81,6 +81,8 @@ private: ChannelMarker m_channelMarker; RollupState m_rollupState; DSDDemodSettings m_settings; + qint64 m_deviceCenterFrequency; + int m_basebandSampleRate; bool m_doApplySettings; ScopeVisXY* m_scopeVisXY; @@ -113,6 +115,7 @@ private: void updateMyPosition(); bool handleMessage(const Message& message); void makeUIConnections(); + void updateAbsoluteCenterFrequency(); void leaveEvent(QEvent*); void enterEvent(QEvent*); diff --git a/plugins/channelrx/demodfreedv/freedvdemod.cpp b/plugins/channelrx/demodfreedv/freedvdemod.cpp index e8e92b373..b5ff705e5 100644 --- a/plugins/channelrx/demodfreedv/freedvdemod.cpp +++ b/plugins/channelrx/demodfreedv/freedvdemod.cpp @@ -151,6 +151,10 @@ bool FreeDVDemod::handleMessage(const Message& cmd) DSPSignalNotification* rep = new DSPSignalNotification(notif); // make a copy qDebug() << "FreeDVDemod::handleMessage: DSPSignalNotification"; m_basebandSink->getInputMessageQueue()->push(rep); + // Forward to GUI if any + if (getMessageQueueToGUI()) { + getMessageQueueToGUI()->push(new DSPSignalNotification(notif)); + } return true; } diff --git a/plugins/channelrx/demodfreedv/freedvdemodgui.cpp b/plugins/channelrx/demodfreedv/freedvdemodgui.cpp index 00455c0b1..75ce5b79a 100644 --- a/plugins/channelrx/demodfreedv/freedvdemodgui.cpp +++ b/plugins/channelrx/demodfreedv/freedvdemodgui.cpp @@ -95,6 +95,16 @@ bool FreeDVDemodGUI::handleMessage(const Message& message) applyBandwidths(5 - ui->spanLog2->value()); // will update spectrum details with new sample rate return true; } + else if (DSPSignalNotification::match(message)) + { + DSPSignalNotification& notif = (DSPSignalNotification&) message; + m_deviceCenterFrequency = notif.getCenterFrequency(); + m_basebandSampleRate = notif.getSampleRate(); + ui->deltaFrequency->setValueRange(false, 7, -m_basebandSampleRate/2, m_basebandSampleRate/2); + ui->deltaFrequencyLabel->setToolTip(tr("Range %1 %L2 Hz").arg(QChar(0xB1)).arg(m_basebandSampleRate/2)); + updateAbsoluteCenterFrequency(); + return true; + } else { return false; @@ -130,6 +140,7 @@ void FreeDVDemodGUI::on_deltaFrequency_changed(qint64 value) { m_channelMarker.setCenterFrequency(value); m_settings.m_inputFrequencyOffset = m_channelMarker.getCenterFrequency(); + updateAbsoluteCenterFrequency(); applySettings(); } @@ -199,7 +210,6 @@ void FreeDVDemodGUI::onMenuDialogCalled(const QPoint &p) dialog.move(p); dialog.exec(); - m_settings.m_inputFrequencyOffset = m_channelMarker.getCenterFrequency(); m_settings.m_rgbColor = m_channelMarker.getColor().rgb(); m_settings.m_title = m_channelMarker.getTitle(); m_settings.m_useReverseAPI = dialog.useReverseAPI(); @@ -247,6 +257,8 @@ FreeDVDemodGUI::FreeDVDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, B m_pluginAPI(pluginAPI), m_deviceUISet(deviceUISet), m_channelMarker(this), + m_deviceCenterFrequency(0), + m_basebandSampleRate(1), m_doApplySettings(true), m_spectrumRate(6000), m_audioBinaural(false), @@ -398,6 +410,7 @@ void FreeDVDemodGUI::displaySettings() displayStreamIndex(); getRollupContents()->restoreState(m_rollupState); + updateAbsoluteCenterFrequency(); blockApplySettings(false); } @@ -502,3 +515,8 @@ void FreeDVDemodGUI::makeUIConnections() QObject::connect(ui->agc, &ButtonSwitch::toggled, this, &FreeDVDemodGUI::on_agc_toggled); QObject::connect(ui->audioMute, &QToolButton::toggled, this, &FreeDVDemodGUI::on_audioMute_toggled); } + +void FreeDVDemodGUI::updateAbsoluteCenterFrequency() +{ + setStatusFrequency(m_deviceCenterFrequency + m_settings.m_inputFrequencyOffset); +} diff --git a/plugins/channelrx/demodfreedv/freedvdemodgui.h b/plugins/channelrx/demodfreedv/freedvdemodgui.h index f50c72fca..c68bfaa15 100644 --- a/plugins/channelrx/demodfreedv/freedvdemodgui.h +++ b/plugins/channelrx/demodfreedv/freedvdemodgui.h @@ -70,6 +70,8 @@ private: ChannelMarker m_channelMarker; RollupState m_rollupState; FreeDVDemodSettings m_settings; + qint64 m_deviceCenterFrequency; + int m_basebandSampleRate; bool m_doApplySettings; int m_spectrumRate; bool m_audioBinaural; @@ -97,6 +99,7 @@ private: void displayStreamIndex(); bool handleMessage(const Message& message); void makeUIConnections(); + void updateAbsoluteCenterFrequency(); void leaveEvent(QEvent*); void enterEvent(QEvent*); diff --git a/plugins/channelrx/demodnfm/nfmdemod.cpp b/plugins/channelrx/demodnfm/nfmdemod.cpp index b73140806..1239c0e36 100644 --- a/plugins/channelrx/demodnfm/nfmdemod.cpp +++ b/plugins/channelrx/demodnfm/nfmdemod.cpp @@ -151,6 +151,10 @@ bool NFMDemod::handleMessage(const Message& cmd) DSPSignalNotification* rep = new DSPSignalNotification(notif); // make a copy qDebug() << "NFMDemod::handleMessage: DSPSignalNotification"; m_basebandSink->getInputMessageQueue()->push(rep); + // Forward to GUI if any + if (getMessageQueueToGUI()) { + getMessageQueueToGUI()->push(new DSPSignalNotification(notif)); + } return true; } diff --git a/plugins/channelrx/demodnfm/nfmdemodgui.cpp b/plugins/channelrx/demodnfm/nfmdemodgui.cpp index b7cd4dd0b..3825bb7ef 100644 --- a/plugins/channelrx/demodnfm/nfmdemodgui.cpp +++ b/plugins/channelrx/demodnfm/nfmdemodgui.cpp @@ -13,6 +13,7 @@ #include "gui/audioselectdialog.h" #include "dsp/dspengine.h" #include "dsp/dcscodes.h" +#include "dsp/dspcommands.h" #include "maincore.h" #include "nfmdemodreport.h" @@ -80,6 +81,16 @@ bool NFMDemodGUI::handleMessage(const Message& message) blockApplySettings(false); return true; } + else if (DSPSignalNotification::match(message)) + { + DSPSignalNotification& notif = (DSPSignalNotification&) message; + m_deviceCenterFrequency = notif.getCenterFrequency(); + m_basebandSampleRate = notif.getSampleRate(); + ui->deltaFrequency->setValueRange(false, 7, -m_basebandSampleRate/2, m_basebandSampleRate/2); + ui->deltaFrequencyLabel->setToolTip(tr("Range %1 %L2 Hz").arg(QChar(0xB1)).arg(m_basebandSampleRate/2)); + updateAbsoluteCenterFrequency(); + return true; + } return false; } @@ -113,6 +124,7 @@ void NFMDemodGUI::on_deltaFrequency_changed(qint64 value) { m_channelMarker.setCenterFrequency(value); m_settings.m_inputFrequencyOffset = m_channelMarker.getCenterFrequency(); + updateAbsoluteCenterFrequency(); applySettings(); } @@ -297,7 +309,6 @@ void NFMDemodGUI::onMenuDialogCalled(const QPoint &p) dialog.move(p); dialog.exec(); - m_settings.m_inputFrequencyOffset = m_channelMarker.getCenterFrequency(); m_settings.m_rgbColor = m_channelMarker.getColor().rgb(); m_settings.m_title = m_channelMarker.getTitle(); m_settings.m_useReverseAPI = dialog.useReverseAPI(); @@ -336,6 +347,8 @@ NFMDemodGUI::NFMDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Baseban m_pluginAPI(pluginAPI), m_deviceUISet(deviceUISet), m_channelMarker(this), + m_deviceCenterFrequency(0), + m_basebandSampleRate(1), m_basicSettingsShown(false), m_doApplySettings(true), m_squelchOpen(false), @@ -514,6 +527,7 @@ void NFMDemodGUI::displaySettings() displayStreamIndex(); getRollupContents()->restoreState(m_rollupState); + updateAbsoluteCenterFrequency(); blockApplySettings(false); } @@ -634,3 +648,8 @@ void NFMDemodGUI::makeUIConnections() QObject::connect(ui->highPassFilter, &ButtonSwitch::toggled, this, &NFMDemodGUI::on_highPassFilter_toggled); QObject::connect(ui->audioMute, &QToolButton::toggled, this, &NFMDemodGUI::on_audioMute_toggled); } + +void NFMDemodGUI::updateAbsoluteCenterFrequency() +{ + setStatusFrequency(m_deviceCenterFrequency + m_settings.m_inputFrequencyOffset); +} diff --git a/plugins/channelrx/demodnfm/nfmdemodgui.h b/plugins/channelrx/demodnfm/nfmdemodgui.h index 0514d59aa..6ae22810b 100644 --- a/plugins/channelrx/demodnfm/nfmdemodgui.h +++ b/plugins/channelrx/demodnfm/nfmdemodgui.h @@ -50,6 +50,8 @@ private: ChannelMarker m_channelMarker; RollupState m_rollupState; NFMDemodSettings m_settings; + qint64 m_deviceCenterFrequency; + int m_basebandSampleRate; bool m_basicSettingsShown; bool m_doApplySettings; @@ -72,6 +74,7 @@ private: void setDcsCode(unsigned int dcsCode); bool handleMessage(const Message& message); void makeUIConnections(); + void updateAbsoluteCenterFrequency(); void leaveEvent(QEvent*); void enterEvent(QEvent*); diff --git a/plugins/channelrx/demodpacket/packetdemod.cpp b/plugins/channelrx/demodpacket/packetdemod.cpp index d4eafb1d5..94886d91f 100644 --- a/plugins/channelrx/demodpacket/packetdemod.cpp +++ b/plugins/channelrx/demodpacket/packetdemod.cpp @@ -153,10 +153,8 @@ bool PacketDemod::handleMessage(const Message& cmd) qDebug() << "PacketDemod::handleMessage: DSPSignalNotification"; m_basebandSink->getInputMessageQueue()->push(rep); // Forward to GUI if any - if (m_guiMessageQueue) - { - rep = new DSPSignalNotification(notif); - m_guiMessageQueue->push(rep); + if (m_guiMessageQueue) { + m_guiMessageQueue->push(new DSPSignalNotification(notif)); } return true; diff --git a/plugins/channelrx/demodpacket/packetdemodgui.cpp b/plugins/channelrx/demodpacket/packetdemodgui.cpp index ce32d954b..6a344d1ab 100644 --- a/plugins/channelrx/demodpacket/packetdemodgui.cpp +++ b/plugins/channelrx/demodpacket/packetdemodgui.cpp @@ -218,7 +218,11 @@ bool PacketDemodGUI::handleMessage(const Message& message) else if (DSPSignalNotification::match(message)) { DSPSignalNotification& notif = (DSPSignalNotification&) message; + m_deviceCenterFrequency = notif.getCenterFrequency(); m_basebandSampleRate = notif.getSampleRate(); + ui->deltaFrequency->setValueRange(false, 7, -m_basebandSampleRate/2, m_basebandSampleRate/2); + ui->deltaFrequencyLabel->setToolTip(tr("Range %1 %L2 Hz").arg(QChar(0xB1)).arg(m_basebandSampleRate/2)); + updateAbsoluteCenterFrequency(); return true; } else if (MainCore::MsgPacket::match(message)) @@ -260,6 +264,7 @@ void PacketDemodGUI::on_deltaFrequency_changed(qint64 value) { m_channelMarker.setCenterFrequency(value); m_settings.m_inputFrequencyOffset = m_channelMarker.getCenterFrequency(); + updateAbsoluteCenterFrequency(); applySettings(); } @@ -424,6 +429,7 @@ PacketDemodGUI::PacketDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, B m_pluginAPI(pluginAPI), m_deviceUISet(deviceUISet), m_channelMarker(this), + m_deviceCenterFrequency(0), m_doApplySettings(true), m_tickCount(0) { @@ -558,6 +564,7 @@ void PacketDemodGUI::displaySettings() filter(); getRollupContents()->restoreState(m_rollupState); + updateAbsoluteCenterFrequency(); blockApplySettings(false); } @@ -704,3 +711,8 @@ void PacketDemodGUI::makeUIConnections() QObject::connect(ui->logFilename, &QToolButton::clicked, this, &PacketDemodGUI::on_logFilename_clicked); QObject::connect(ui->logOpen, &QToolButton::clicked, this, &PacketDemodGUI::on_logOpen_clicked); } + +void PacketDemodGUI::updateAbsoluteCenterFrequency() +{ + setStatusFrequency(m_deviceCenterFrequency + m_settings.m_inputFrequencyOffset); +} diff --git a/plugins/channelrx/demodpacket/packetdemodgui.h b/plugins/channelrx/demodpacket/packetdemodgui.h index 56fe1e3d7..e7ed265ed 100644 --- a/plugins/channelrx/demodpacket/packetdemodgui.h +++ b/plugins/channelrx/demodpacket/packetdemodgui.h @@ -78,6 +78,7 @@ private: ChannelMarker m_channelMarker; RollupState m_rollupState; PacketDemodSettings m_settings; + qint64 m_deviceCenterFrequency; bool m_doApplySettings; PacketDemod* m_packetDemod; @@ -97,6 +98,7 @@ private: void packetReceived(QByteArray packet); bool handleMessage(const Message& message); void makeUIConnections(); + void updateAbsoluteCenterFrequency(); void leaveEvent(QEvent*); void enterEvent(QEvent*); diff --git a/plugins/channelrx/demodpager/pagerdemod.cpp b/plugins/channelrx/demodpager/pagerdemod.cpp index 773acc50d..8f7552375 100644 --- a/plugins/channelrx/demodpager/pagerdemod.cpp +++ b/plugins/channelrx/demodpager/pagerdemod.cpp @@ -145,6 +145,10 @@ bool PagerDemod::handleMessage(const Message& cmd) DSPSignalNotification* rep = new DSPSignalNotification(notif); // make a copy qDebug() << "PagerDemod::handleMessage: DSPSignalNotification"; m_basebandSink->getInputMessageQueue()->push(rep); + // Forward to GUI if any + if (getMessageQueueToGUI()) { + getMessageQueueToGUI()->push(new DSPSignalNotification(notif)); + } return true; } diff --git a/plugins/channelrx/demodpager/pagerdemodgui.cpp b/plugins/channelrx/demodpager/pagerdemodgui.cpp index 6ef17a027..14472b25c 100644 --- a/plugins/channelrx/demodpager/pagerdemodgui.cpp +++ b/plugins/channelrx/demodpager/pagerdemodgui.cpp @@ -277,6 +277,16 @@ bool PagerDemodGUI::handleMessage(const Message& message) report.getEvenParityErrors(), report.getBCHParityErrors()); return true; } + else if (DSPSignalNotification::match(message)) + { + DSPSignalNotification& notif = (DSPSignalNotification&) message; + m_deviceCenterFrequency = notif.getCenterFrequency(); + m_basebandSampleRate = notif.getSampleRate(); + ui->deltaFrequency->setValueRange(false, 7, -m_basebandSampleRate/2, m_basebandSampleRate/2); + ui->deltaFrequencyLabel->setToolTip(tr("Range %1 %L2 Hz").arg(QChar(0xB1)).arg(m_basebandSampleRate/2)); + updateAbsoluteCenterFrequency(); + return true; + } return false; } @@ -310,6 +320,7 @@ void PagerDemodGUI::on_deltaFrequency_changed(qint64 value) { m_channelMarker.setCenterFrequency(value); m_settings.m_inputFrequencyOffset = m_channelMarker.getCenterFrequency(); + updateAbsoluteCenterFrequency(); applySettings(); } @@ -475,6 +486,8 @@ PagerDemodGUI::PagerDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Bas m_pluginAPI(pluginAPI), m_deviceUISet(deviceUISet), m_channelMarker(this), + m_deviceCenterFrequency(0), + m_basebandSampleRate(1), m_doApplySettings(true), m_tickCount(0) { @@ -650,6 +663,7 @@ void PagerDemodGUI::displaySettings() filter(); getRollupContents()->restoreState(m_rollupState); + updateAbsoluteCenterFrequency(); blockApplySettings(false); } @@ -819,3 +833,8 @@ void PagerDemodGUI::makeUIConnections() QObject::connect(ui->logFilename, &QToolButton::clicked, this, &PagerDemodGUI::on_logFilename_clicked); QObject::connect(ui->logOpen, &QToolButton::clicked, this, &PagerDemodGUI::on_logOpen_clicked); } + +void PagerDemodGUI::updateAbsoluteCenterFrequency() +{ + setStatusFrequency(m_deviceCenterFrequency + m_settings.m_inputFrequencyOffset); +} diff --git a/plugins/channelrx/demodpager/pagerdemodgui.h b/plugins/channelrx/demodpager/pagerdemodgui.h index 4549f8078..5e9c3b718 100644 --- a/plugins/channelrx/demodpager/pagerdemodgui.h +++ b/plugins/channelrx/demodpager/pagerdemodgui.h @@ -72,6 +72,8 @@ private: ChannelMarker m_channelMarker; RollupState m_rollupState; PagerDemodSettings m_settings; + qint64 m_deviceCenterFrequency; + int m_basebandSampleRate; bool m_doApplySettings; ScopeVis* m_scopeVis; @@ -93,6 +95,7 @@ private: int evenParityErrors, int bchParityErrors); bool handleMessage(const Message& message); void makeUIConnections(); + void updateAbsoluteCenterFrequency(); void leaveEvent(QEvent*); void enterEvent(QEvent*); diff --git a/plugins/channelrx/demodradiosonde/radiosondedemod.cpp b/plugins/channelrx/demodradiosonde/radiosondedemod.cpp index e559c95a4..6964c3d2b 100644 --- a/plugins/channelrx/demodradiosonde/radiosondedemod.cpp +++ b/plugins/channelrx/demodradiosonde/radiosondedemod.cpp @@ -149,6 +149,10 @@ bool RadiosondeDemod::handleMessage(const Message& cmd) DSPSignalNotification* rep = new DSPSignalNotification(notif); // make a copy qDebug() << "RadiosondeDemod::handleMessage: DSPSignalNotification"; m_basebandSink->getInputMessageQueue()->push(rep); + // Forward to GUI if any + if (getMessageQueueToGUI()) { + getMessageQueueToGUI()->push(new DSPSignalNotification(notif)); + } return true; } diff --git a/plugins/channelrx/demodradiosonde/radiosondedemodgui.cpp b/plugins/channelrx/demodradiosonde/radiosondedemodgui.cpp index 87074c6e3..0cf731d3a 100644 --- a/plugins/channelrx/demodradiosonde/radiosondedemodgui.cpp +++ b/plugins/channelrx/demodradiosonde/radiosondedemodgui.cpp @@ -324,6 +324,16 @@ bool RadiosondeDemodGUI::handleMessage(const Message& frame) frameReceived(report.getMessage(), report.getDateTime(), report.getErrorsCorrected(), report.getThreshold()); return true; } + else if (DSPSignalNotification::match(frame)) + { + const DSPSignalNotification& notif = (const DSPSignalNotification&) frame; + m_deviceCenterFrequency = notif.getCenterFrequency(); + m_basebandSampleRate = notif.getSampleRate(); + ui->deltaFrequency->setValueRange(false, 7, -m_basebandSampleRate/2, m_basebandSampleRate/2); + ui->deltaFrequencyLabel->setToolTip(tr("Range %1 %L2 Hz").arg(QChar(0xB1)).arg(m_basebandSampleRate/2)); + updateAbsoluteCenterFrequency(); + return true; + } return false; } @@ -357,6 +367,7 @@ void RadiosondeDemodGUI::on_deltaFrequency_changed(qint64 value) { m_channelMarker.setCenterFrequency(value); m_settings.m_inputFrequencyOffset = m_channelMarker.getCenterFrequency(); + updateAbsoluteCenterFrequency(); applySettings(); } @@ -532,6 +543,8 @@ RadiosondeDemodGUI::RadiosondeDemodGUI(PluginAPI* pluginAPI, DeviceUISet *device m_pluginAPI(pluginAPI), m_deviceUISet(deviceUISet), m_channelMarker(this), + m_deviceCenterFrequency(0), + m_basebandSampleRate(1), m_doApplySettings(true), m_tickCount(0) { @@ -754,6 +767,7 @@ void RadiosondeDemodGUI::displaySettings() filter(); getRollupContents()->restoreState(m_rollupState); + updateAbsoluteCenterFrequency(); blockApplySettings(false); } @@ -914,3 +928,8 @@ void RadiosondeDemodGUI::makeUIConnections() QObject::connect(ui->logFilename, &QToolButton::clicked, this, &RadiosondeDemodGUI::on_logFilename_clicked); QObject::connect(ui->logOpen, &QToolButton::clicked, this, &RadiosondeDemodGUI::on_logOpen_clicked); } + +void RadiosondeDemodGUI::updateAbsoluteCenterFrequency() +{ + setStatusFrequency(m_deviceCenterFrequency + m_settings.m_inputFrequencyOffset); +} diff --git a/plugins/channelrx/demodradiosonde/radiosondedemodgui.h b/plugins/channelrx/demodradiosonde/radiosondedemodgui.h index 173a7a74b..4f44d1fe8 100644 --- a/plugins/channelrx/demodradiosonde/radiosondedemodgui.h +++ b/plugins/channelrx/demodradiosonde/radiosondedemodgui.h @@ -74,6 +74,8 @@ private: ChannelMarker m_channelMarker; RollupState m_rollupState; RadiosondeDemodSettings m_settings; + qint64 m_deviceCenterFrequency; + int m_basebandSampleRate; bool m_doApplySettings; ScopeVis* m_scopeVis; @@ -96,6 +98,7 @@ private: void frameReceived(const QByteArray& frame, const QDateTime& dateTime, int errorsCorrected, int threshold); bool handleMessage(const Message& message); void makeUIConnections(); + void updateAbsoluteCenterFrequency(); void leaveEvent(QEvent*); void enterEvent(QEvent*); diff --git a/plugins/channelrx/demodssb/ssbdemod.cpp b/plugins/channelrx/demodssb/ssbdemod.cpp index 658b03e2c..a3ebb7910 100644 --- a/plugins/channelrx/demodssb/ssbdemod.cpp +++ b/plugins/channelrx/demodssb/ssbdemod.cpp @@ -145,6 +145,10 @@ bool SSBDemod::handleMessage(const Message& cmd) DSPSignalNotification* rep = new DSPSignalNotification(notif); // make a copy qDebug() << "SSBDemod::handleMessage: DSPSignalNotification"; m_basebandSink->getInputMessageQueue()->push(rep); + // Forwatd to GUI if any + if (getMessageQueueToGUI()) { + getMessageQueueToGUI()->push(new DSPSignalNotification(notif)); + } return true; } diff --git a/plugins/channelrx/demodssb/ssbdemodgui.cpp b/plugins/channelrx/demodssb/ssbdemodgui.cpp index 511ca5358..2449d299b 100644 --- a/plugins/channelrx/demodssb/ssbdemodgui.cpp +++ b/plugins/channelrx/demodssb/ssbdemodgui.cpp @@ -84,6 +84,16 @@ bool SSBDemodGUI::handleMessage(const Message& message) blockApplySettings(false); return true; } + else if (DSPSignalNotification::match(message)) + { + const DSPSignalNotification& notif = (const DSPSignalNotification&) message; + m_deviceCenterFrequency = notif.getCenterFrequency(); + m_basebandSampleRate = notif.getSampleRate(); + ui->deltaFrequency->setValueRange(false, 7, -m_basebandSampleRate/2, m_basebandSampleRate/2); + ui->deltaFrequencyLabel->setToolTip(tr("Range %1 %L2 Hz").arg(QChar(0xB1)).arg(m_basebandSampleRate/2)); + updateAbsoluteCenterFrequency(); + return true; + } else { return false; @@ -139,6 +149,7 @@ void SSBDemodGUI::on_deltaFrequency_changed(qint64 value) { m_channelMarker.setCenterFrequency(value); m_settings.m_inputFrequencyOffset = m_channelMarker.getCenterFrequency(); + updateAbsoluteCenterFrequency(); applySettings(); } @@ -239,7 +250,6 @@ void SSBDemodGUI::onMenuDialogCalled(const QPoint &p) dialog.move(p); dialog.exec(); - m_settings.m_inputFrequencyOffset = m_channelMarker.getCenterFrequency(); m_settings.m_rgbColor = m_channelMarker.getColor().rgb(); m_settings.m_title = m_channelMarker.getTitle(); m_settings.m_useReverseAPI = dialog.useReverseAPI(); @@ -287,6 +297,8 @@ SSBDemodGUI::SSBDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Baseban m_pluginAPI(pluginAPI), m_deviceUISet(deviceUISet), m_channelMarker(this), + m_deviceCenterFrequency(0), + m_basebandSampleRate(1), m_doApplySettings(true), m_spectrumRate(6000), m_audioBinaural(false), @@ -581,6 +593,7 @@ void SSBDemodGUI::displaySettings() displayStreamIndex(); getRollupContents()->restoreState(m_rollupState); + updateAbsoluteCenterFrequency(); blockApplySettings(false); } @@ -696,3 +709,8 @@ void SSBDemodGUI::makeUIConnections() QObject::connect(ui->spanLog2, &QSlider::valueChanged, this, &SSBDemodGUI::on_spanLog2_valueChanged); QObject::connect(ui->flipSidebands, &QPushButton::clicked, this, &SSBDemodGUI::on_flipSidebands_clicked); } + +void SSBDemodGUI::updateAbsoluteCenterFrequency() +{ + setStatusFrequency(m_deviceCenterFrequency + m_settings.m_inputFrequencyOffset); +} diff --git a/plugins/channelrx/demodssb/ssbdemodgui.h b/plugins/channelrx/demodssb/ssbdemodgui.h index c520777fc..f5da7c3ed 100644 --- a/plugins/channelrx/demodssb/ssbdemodgui.h +++ b/plugins/channelrx/demodssb/ssbdemodgui.h @@ -53,6 +53,8 @@ private: ChannelMarker m_channelMarker; RollupState m_rollupState; SSBDemodSettings m_settings; + qint64 m_deviceCenterFrequency; + int m_basebandSampleRate; bool m_doApplySettings; int m_spectrumRate; bool m_audioBinaural; @@ -82,6 +84,7 @@ private: void displayAGCThresholdGate(int value); bool handleMessage(const Message& message); void makeUIConnections(); + void updateAbsoluteCenterFrequency(); void leaveEvent(QEvent*); void enterEvent(QEvent*); diff --git a/plugins/channelrx/demodvor/vordemod.cpp b/plugins/channelrx/demodvor/vordemod.cpp index e81f613c7..efb88b475 100644 --- a/plugins/channelrx/demodvor/vordemod.cpp +++ b/plugins/channelrx/demodvor/vordemod.cpp @@ -150,10 +150,8 @@ bool VORDemod::handleMessage(const Message& cmd) qDebug() << "VORDemod::handleMessage: DSPSignalNotification"; m_basebandSink->getInputMessageQueue()->push(rep); // Forward to GUI if any - if (m_guiMessageQueue) - { - rep = new DSPSignalNotification(notif); - m_guiMessageQueue->push(rep); + if (m_guiMessageQueue) { + m_guiMessageQueue->push(new DSPSignalNotification(notif)); } return true; diff --git a/plugins/channelrx/demodvor/vordemodgui.cpp b/plugins/channelrx/demodvor/vordemodgui.cpp index 5ae5d0720..54cb5904b 100644 --- a/plugins/channelrx/demodvor/vordemodgui.cpp +++ b/plugins/channelrx/demodvor/vordemodgui.cpp @@ -764,7 +764,9 @@ bool VORDemodGUI::handleMessage(const Message& message) else if (DSPSignalNotification::match(message)) { DSPSignalNotification& notif = (DSPSignalNotification&) message; + m_deviceCenterFrequency = notif.getCenterFrequency(); m_basebandSampleRate = notif.getSampleRate(); + updateAbsoluteCenterFrequency(); return true; } else if (VORDemodReport::MsgReportRadial::match(message)) @@ -1160,6 +1162,7 @@ VORDemodGUI::VORDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Baseban m_pluginAPI(pluginAPI), m_deviceUISet(deviceUISet), m_channelMarker(this), + m_deviceCenterFrequency(0), m_doApplySettings(true), m_squelchOpen(false), m_tickCount(0), @@ -1441,3 +1444,8 @@ void VORDemodGUI::makeUIConnections() QObject::connect(ui->getOpenAIPVORDB, &QPushButton::clicked, this, &VORDemodGUI::on_getOpenAIPVORDB_clicked); QObject::connect(ui->magDecAdjust, &QPushButton::clicked, this, &VORDemodGUI::on_magDecAdjust_clicked); } + +void VORDemodGUI::updateAbsoluteCenterFrequency() +{ + setStatusFrequency(m_deviceCenterFrequency); +} diff --git a/plugins/channelrx/demodvor/vordemodgui.h b/plugins/channelrx/demodvor/vordemodgui.h index a1909572a..875cfdf9c 100644 --- a/plugins/channelrx/demodvor/vordemodgui.h +++ b/plugins/channelrx/demodvor/vordemodgui.h @@ -236,6 +236,7 @@ private: ChannelMarker m_channelMarker; RollupState m_rollupState; VORDemodSettings m_settings; + qint64 m_deviceCenterFrequency; bool m_doApplySettings; VORDemod* m_vorDemod; @@ -263,6 +264,7 @@ private: void displayStreamIndex(); bool handleMessage(const Message& message); void makeUIConnections(); + void updateAbsoluteCenterFrequency(); void leaveEvent(QEvent*); void enterEvent(QEvent*); diff --git a/plugins/channelrx/demodvorsc/vordemodsc.cpp b/plugins/channelrx/demodvorsc/vordemodsc.cpp index a373394ba..d4d3ea223 100644 --- a/plugins/channelrx/demodvorsc/vordemodsc.cpp +++ b/plugins/channelrx/demodvorsc/vordemodsc.cpp @@ -152,10 +152,8 @@ bool VORDemodSC::handleMessage(const Message& cmd) qDebug() << "VORDemodSC::handleMessage: DSPSignalNotification"; m_basebandSink->getInputMessageQueue()->push(rep); // Forward to GUI if any - if (m_guiMessageQueue) - { - rep = new DSPSignalNotification(notif); - m_guiMessageQueue->push(rep); + if (m_guiMessageQueue) { + m_guiMessageQueue->push(new DSPSignalNotification(notif)); } return true; diff --git a/plugins/channelrx/demodvorsc/vordemodscgui.cpp b/plugins/channelrx/demodvorsc/vordemodscgui.cpp index 361152963..9966497f8 100644 --- a/plugins/channelrx/demodvorsc/vordemodscgui.cpp +++ b/plugins/channelrx/demodvorsc/vordemodscgui.cpp @@ -91,7 +91,11 @@ bool VORDemodSCGUI::handleMessage(const Message& message) else if (DSPSignalNotification::match(message)) { DSPSignalNotification& notif = (DSPSignalNotification&) message; + m_deviceCenterFrequency = notif.getCenterFrequency(); m_basebandSampleRate = notif.getSampleRate(); + ui->deltaFrequency->setValueRange(false, 7, -m_basebandSampleRate/2, m_basebandSampleRate/2); + ui->deltaFrequencyLabel->setToolTip(tr("Range %1 %L2 Hz").arg(QChar(0xB1)).arg(m_basebandSampleRate/2)); + updateAbsoluteCenterFrequency(); return true; } else if (VORDemodSCReport::MsgReportRadial::match(message)) @@ -188,6 +192,7 @@ void VORDemodSCGUI::on_deltaFrequency_changed(qint64 value) { m_channelMarker.setCenterFrequency(value); m_settings.m_inputFrequencyOffset = m_channelMarker.getCenterFrequency(); + updateAbsoluteCenterFrequency(); applySettings(); } @@ -277,6 +282,7 @@ VORDemodSCGUI::VORDemodSCGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Bas ui(new Ui::VORDemodSCGUI), m_pluginAPI(pluginAPI), m_deviceUISet(deviceUISet), + m_deviceCenterFrequency(0), m_channelMarker(this), m_doApplySettings(true), m_squelchOpen(false), @@ -377,6 +383,7 @@ void VORDemodSCGUI::displaySettings() displayStreamIndex(); getRollupContents()->restoreState(m_rollupState); + updateAbsoluteCenterFrequency(); blockApplySettings(false); } @@ -456,3 +463,8 @@ void VORDemodSCGUI::makeUIConnections() QObject::connect(ui->squelch, &QDial::valueChanged, this, &VORDemodSCGUI::on_squelch_valueChanged); QObject::connect(ui->audioMute, &QToolButton::toggled, this, &VORDemodSCGUI::on_audioMute_toggled); } + +void VORDemodSCGUI::updateAbsoluteCenterFrequency() +{ + setStatusFrequency(m_deviceCenterFrequency + m_settings.m_inputFrequencyOffset); +} diff --git a/plugins/channelrx/demodvorsc/vordemodscgui.h b/plugins/channelrx/demodvorsc/vordemodscgui.h index c8fc4b8aa..a1534a077 100644 --- a/plugins/channelrx/demodvorsc/vordemodscgui.h +++ b/plugins/channelrx/demodvorsc/vordemodscgui.h @@ -68,6 +68,7 @@ private: ChannelMarker m_channelMarker; RollupState m_rollupState; VORDemodSCSettings m_settings; + qint64 m_deviceCenterFrequency; bool m_doApplySettings; VORDemodSC* m_vorDemod; @@ -85,6 +86,7 @@ private: void displayStreamIndex(); bool handleMessage(const Message& message); void makeUIConnections(); + void updateAbsoluteCenterFrequency(); void leaveEvent(QEvent*); void enterEvent(QEvent*); diff --git a/plugins/channelrx/demodwfm/wfmdemod.cpp b/plugins/channelrx/demodwfm/wfmdemod.cpp index c6c824f49..dfda76957 100644 --- a/plugins/channelrx/demodwfm/wfmdemod.cpp +++ b/plugins/channelrx/demodwfm/wfmdemod.cpp @@ -148,6 +148,10 @@ bool WFMDemod::handleMessage(const Message& cmd) DSPSignalNotification* rep = new DSPSignalNotification(notif); // make a copy qDebug() << "WFMDemod::handleMessage: DSPSignalNotification"; m_basebandSink->getInputMessageQueue()->push(rep); + // Forwatd to GUI if any + if (getMessageQueueToGUI()) { + getMessageQueueToGUI()->push(new DSPSignalNotification(notif)); + } return true; } diff --git a/plugins/channelrx/demodwfm/wfmdemodgui.cpp b/plugins/channelrx/demodwfm/wfmdemodgui.cpp index 723c4b43f..0e0862d69 100644 --- a/plugins/channelrx/demodwfm/wfmdemodgui.cpp +++ b/plugins/channelrx/demodwfm/wfmdemodgui.cpp @@ -8,6 +8,7 @@ #include "ui_wfmdemodgui.h" #include "dsp/dspengine.h" +#include "dsp/dspcommands.h" #include "plugin/pluginapi.h" #include "util/simpleserializer.h" #include "util/db.h" @@ -56,7 +57,6 @@ bool WFMDemodGUI::deserialize(const QByteArray& data) bool WFMDemodGUI::handleMessage(const Message& message) { - (void) message; if (WFMDemod::MsgConfigureWFMDemod::match(message)) { qDebug("WFMDemodGUI::handleMessage: WFMDemod::MsgConfigureWFMDemod"); @@ -67,6 +67,16 @@ bool WFMDemodGUI::handleMessage(const Message& message) blockApplySettings(false); return true; } + else if (DSPSignalNotification::match(message)) + { + const DSPSignalNotification& notif = (const DSPSignalNotification&) message; + m_deviceCenterFrequency = notif.getCenterFrequency(); + m_basebandSampleRate = notif.getSampleRate(); + ui->deltaFrequency->setValueRange(false, 8, -m_basebandSampleRate/2, m_basebandSampleRate/2); + ui->deltaFrequencyLabel->setToolTip(tr("Range %1 %L2 Hz").arg(QChar(0xB1)).arg(m_basebandSampleRate/2)); + updateAbsoluteCenterFrequency(); + return true; + } else { return false; @@ -102,6 +112,7 @@ void WFMDemodGUI::on_deltaFrequency_changed(qint64 value) { m_channelMarker.setCenterFrequency(value); m_settings.m_inputFrequencyOffset = m_channelMarker.getCenterFrequency(); + updateAbsoluteCenterFrequency(); applySettings(); } @@ -162,7 +173,6 @@ void WFMDemodGUI::onMenuDialogCalled(const QPoint &p) dialog.move(p); dialog.exec(); - m_settings.m_inputFrequencyOffset = m_channelMarker.getCenterFrequency(); m_settings.m_rgbColor = m_channelMarker.getColor().rgb(); m_settings.m_title = m_channelMarker.getTitle(); m_settings.m_useReverseAPI = dialog.useReverseAPI(); @@ -201,6 +211,8 @@ WFMDemodGUI::WFMDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Baseban m_pluginAPI(pluginAPI), m_deviceUISet(deviceUISet), m_channelMarker(this), + m_deviceCenterFrequency(0), + m_basebandSampleRate(1), m_basicSettingsShown(false), m_squelchOpen(false), m_audioSampleRate(-1) @@ -299,6 +311,7 @@ void WFMDemodGUI::displaySettings() displayStreamIndex(); getRollupContents()->restoreState(m_rollupState); + updateAbsoluteCenterFrequency(); blockApplySettings(false); } @@ -375,3 +388,8 @@ void WFMDemodGUI::makeUIConnections() QObject::connect(ui->squelch, &QSlider::valueChanged, this, &WFMDemodGUI::on_squelch_valueChanged); QObject::connect(ui->audioMute, &QToolButton::toggled, this, &WFMDemodGUI::on_audioMute_toggled); } + +void WFMDemodGUI::updateAbsoluteCenterFrequency() +{ + setStatusFrequency(m_deviceCenterFrequency + m_settings.m_inputFrequencyOffset); +} diff --git a/plugins/channelrx/demodwfm/wfmdemodgui.h b/plugins/channelrx/demodwfm/wfmdemodgui.h index 5894b8253..3c83d13d8 100644 --- a/plugins/channelrx/demodwfm/wfmdemodgui.h +++ b/plugins/channelrx/demodwfm/wfmdemodgui.h @@ -48,6 +48,8 @@ private: ChannelMarker m_channelMarker; RollupState m_rollupState; WFMDemodSettings m_settings; + qint64 m_deviceCenterFrequency; + int m_basebandSampleRate; bool m_basicSettingsShown; bool m_doApplySettings; bool m_audioMute; @@ -66,6 +68,7 @@ private: void displayStreamIndex(); bool handleMessage(const Message& message); void makeUIConnections(); + void updateAbsoluteCenterFrequency(); void leaveEvent(QEvent*); void enterEvent(QEvent*); diff --git a/plugins/channelrx/filesink/filesink.cpp b/plugins/channelrx/filesink/filesink.cpp index cc7c17a23..f9491275b 100644 --- a/plugins/channelrx/filesink/filesink.cpp +++ b/plugins/channelrx/filesink/filesink.cpp @@ -170,10 +170,8 @@ bool FileSink::handleMessage(const Message& cmd) DSPSignalNotification *notif = new DSPSignalNotification(cfg); m_basebandSink->getInputMessageQueue()->push(notif); - if (getMessageQueueToGUI()) - { - DSPSignalNotification *notifToGUI = new DSPSignalNotification(cfg); - getMessageQueueToGUI()->push(notifToGUI); + if (getMessageQueueToGUI()) { + getMessageQueueToGUI()->push(new DSPSignalNotification(cfg)); } return true; diff --git a/plugins/channelrx/filesink/filesinkgui.cpp b/plugins/channelrx/filesink/filesinkgui.cpp index 0c4b6890b..cf1993061 100644 --- a/plugins/channelrx/filesink/filesinkgui.cpp +++ b/plugins/channelrx/filesink/filesinkgui.cpp @@ -76,7 +76,11 @@ bool FileSinkGUI::handleMessage(const Message& message) if (DSPSignalNotification::match(message)) { DSPSignalNotification notif = (const DSPSignalNotification&) message; + m_deviceCenterFrequency = notif.getCenterFrequency(); m_basebandSampleRate = notif.getSampleRate(); + ui->deltaFrequency->setValueRange(false, 8, -m_basebandSampleRate/2, m_basebandSampleRate/2); + ui->deltaFrequencyLabel->setToolTip(tr("Range %1 %L2 Hz").arg(QChar(0xB1)).arg(m_basebandSampleRate/2)); + updateAbsoluteCenterFrequency(); displayRate(); if (m_fixedPosition) @@ -178,6 +182,7 @@ FileSinkGUI::FileSinkGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Baseban m_pluginAPI(pluginAPI), m_deviceUISet(deviceUISet), m_channelMarker(this), + m_deviceCenterFrequency(0), m_running(false), m_fixedShiftIndex(0), m_basebandSampleRate(0), @@ -284,6 +289,7 @@ void FileSinkGUI::displaySettings() setPosFromFrequency(); getRollupContents()->restoreState(m_rollupState); + updateAbsoluteCenterFrequency(); blockApplySettings(false); } @@ -410,6 +416,7 @@ void FileSinkGUI::on_deltaFrequency_changed(qint64 value) { m_channelMarker.setCenterFrequency(value); m_settings.m_inputFrequencyOffset = m_channelMarker.getCenterFrequency(); + updateAbsoluteCenterFrequency(); setPosFromFrequency(); applySettings(); } @@ -542,6 +549,7 @@ void FileSinkGUI::setFrequencyFromPos() m_channelMarker.setCenterFrequency(inputFrequencyOffset); ui->deltaFrequency->setValue(m_channelMarker.getCenterFrequency()); m_settings.m_inputFrequencyOffset = m_channelMarker.getCenterFrequency(); + updateAbsoluteCenterFrequency(); } void FileSinkGUI::setPosFromFrequency() @@ -609,3 +617,8 @@ void FileSinkGUI::makeUIConnections() QObject::connect(ui->record, &ButtonSwitch::toggled, this, &FileSinkGUI::on_record_toggled); QObject::connect(ui->showFileDialog, &QPushButton::clicked, this, &FileSinkGUI::on_showFileDialog_clicked); } + +void FileSinkGUI::updateAbsoluteCenterFrequency() +{ + setStatusFrequency(m_deviceCenterFrequency + m_settings.m_inputFrequencyOffset); +} diff --git a/plugins/channelrx/filesink/filesinkgui.h b/plugins/channelrx/filesink/filesinkgui.h index daec1f789..10a8da955 100644 --- a/plugins/channelrx/filesink/filesinkgui.h +++ b/plugins/channelrx/filesink/filesinkgui.h @@ -69,6 +69,7 @@ private: ChannelMarker m_channelMarker; RollupState m_rollupState; FileSinkSettings m_settings; + qint64 m_deviceCenterFrequency; bool m_running; int m_fixedShiftIndex; int m_basebandSampleRate; @@ -97,6 +98,7 @@ private: QString displayScaled(uint64_t value, int precision); bool handleMessage(const Message& message); void makeUIConnections(); + void updateAbsoluteCenterFrequency(); void leaveEvent(QEvent*); void enterEvent(QEvent*); diff --git a/plugins/channelrx/freqtracker/freqtracker.cpp b/plugins/channelrx/freqtracker/freqtracker.cpp index d885a4c29..934e9f146 100644 --- a/plugins/channelrx/freqtracker/freqtracker.cpp +++ b/plugins/channelrx/freqtracker/freqtracker.cpp @@ -142,10 +142,8 @@ bool FreqTracker::handleMessage(const Message& cmd) qDebug() << "FreqTracker::handleMessage: DSPSignalNotification"; m_basebandSink->getInputMessageQueue()->push(rep); - if (getMessageQueueToGUI()) - { - DSPSignalNotification *msg = new DSPSignalNotification(notif); - getMessageQueueToGUI()->push(msg); + if (getMessageQueueToGUI()) { + getMessageQueueToGUI()->push(new DSPSignalNotification(notif)); } return true; diff --git a/plugins/channelrx/freqtracker/freqtrackergui.cpp b/plugins/channelrx/freqtracker/freqtrackergui.cpp index 1b91b8388..ba57f429e 100644 --- a/plugins/channelrx/freqtracker/freqtrackergui.cpp +++ b/plugins/channelrx/freqtracker/freqtrackergui.cpp @@ -90,7 +90,11 @@ bool FreqTrackerGUI::handleMessage(const Message& message) else if (DSPSignalNotification::match(message)) { DSPSignalNotification& cfg = (DSPSignalNotification&) message; + m_deviceCenterFrequency = cfg.getCenterFrequency(); m_basebandSampleRate = cfg.getSampleRate(); + ui->deltaFrequency->setValueRange(false, 7, -m_basebandSampleRate/2, m_basebandSampleRate/2); + ui->deltaFrequencyLabel->setToolTip(tr("Range %1 %L2 Hz").arg(QChar(0xB1)).arg(m_basebandSampleRate/2)); + updateAbsoluteCenterFrequency(); int sinkSampleRate = m_basebandSampleRate / (1<channelSampleRateText->setText(tr("%1k").arg(QString::number(sinkSampleRate / 1000.0f, 'g', 5))); displaySpectrumBandwidth(m_settings.m_spanLog2); @@ -147,6 +151,7 @@ void FreqTrackerGUI::on_deltaFrequency_changed(qint64 value) { m_channelMarker.setCenterFrequency(value); m_settings.m_inputFrequencyOffset = m_channelMarker.getCenterFrequency(); + updateAbsoluteCenterFrequency(); applySettings(); } @@ -268,7 +273,6 @@ void FreqTrackerGUI::onMenuDialogCalled(const QPoint &p) dialog.move(p); dialog.exec(); - m_settings.m_inputFrequencyOffset = m_channelMarker.getCenterFrequency(); m_settings.m_rgbColor = m_channelMarker.getColor().rgb(); m_settings.m_title = m_channelMarker.getTitle(); m_settings.m_useReverseAPI = dialog.useReverseAPI(); @@ -308,6 +312,7 @@ FreqTrackerGUI::FreqTrackerGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, B m_deviceUISet(deviceUISet), m_channelMarker(this), m_pllChannelMarker(this), + m_deviceCenterFrequency(0), m_basebandSampleRate(0), m_doApplySettings(true), m_squelchOpen(false), @@ -438,6 +443,7 @@ void FreqTrackerGUI::displaySettings() displayStreamIndex(); getRollupContents()->restoreState(m_rollupState); + updateAbsoluteCenterFrequency(); blockApplySettings(false); } @@ -535,3 +541,8 @@ void FreqTrackerGUI::makeUIConnections() QObject::connect(ui->squelch, &QSlider::valueChanged, this, &FreqTrackerGUI::on_squelch_valueChanged); QObject::connect(ui->squelchGate, &QDial::valueChanged, this, &FreqTrackerGUI::on_squelchGate_valueChanged); } + +void FreqTrackerGUI::updateAbsoluteCenterFrequency() +{ + setStatusFrequency(m_deviceCenterFrequency + m_settings.m_inputFrequencyOffset); +} diff --git a/plugins/channelrx/freqtracker/freqtrackergui.h b/plugins/channelrx/freqtracker/freqtrackergui.h index 81990dd69..eb3cac976 100644 --- a/plugins/channelrx/freqtracker/freqtrackergui.h +++ b/plugins/channelrx/freqtracker/freqtrackergui.h @@ -70,6 +70,7 @@ private: ChannelMarker m_pllChannelMarker; RollupState m_rollupState; FreqTrackerSettings m_settings; + qint64 m_deviceCenterFrequency; int m_basebandSampleRate; bool m_doApplySettings; @@ -90,6 +91,7 @@ private: void displayStreamIndex(); bool handleMessage(const Message& message); void makeUIConnections(); + void updateAbsoluteCenterFrequency(); void leaveEvent(QEvent*); void enterEvent(QEvent*); diff --git a/plugins/channelrx/localsink/localsink.cpp b/plugins/channelrx/localsink/localsink.cpp index dd0db8cba..abfd2f096 100644 --- a/plugins/channelrx/localsink/localsink.cpp +++ b/plugins/channelrx/localsink/localsink.cpp @@ -41,7 +41,6 @@ #include "localsink.h" MESSAGE_CLASS_DEFINITION(LocalSink::MsgConfigureLocalSink, Message) -MESSAGE_CLASS_DEFINITION(LocalSink::MsgBasebandSampleRateNotification, Message) const char* const LocalSink::m_channelIdURI = "sdrangel.channel.localsink"; const char* const LocalSink::m_channelId = "LocalSink"; @@ -138,10 +137,8 @@ bool LocalSink::handleMessage(const Message& cmd) DSPSignalNotification *msg = new DSPSignalNotification(notif.getSampleRate(), notif.getCenterFrequency()); m_basebandSink->getInputMessageQueue()->push(msg); - if (getMessageQueueToGUI()) - { - MsgBasebandSampleRateNotification *msg = MsgBasebandSampleRateNotification::create(notif.getSampleRate()); - getMessageQueueToGUI()->push(msg); + if (getMessageQueueToGUI()) { + getMessageQueueToGUI()->push(new DSPSignalNotification(notif)); } return true; diff --git a/plugins/channelrx/localsink/localsink.h b/plugins/channelrx/localsink/localsink.h index 41cc56a77..9dce4752a 100644 --- a/plugins/channelrx/localsink/localsink.h +++ b/plugins/channelrx/localsink/localsink.h @@ -61,26 +61,6 @@ public: { } }; - class MsgBasebandSampleRateNotification : public Message { - MESSAGE_CLASS_DECLARATION - - public: - static MsgBasebandSampleRateNotification* create(int sampleRate) { - return new MsgBasebandSampleRateNotification(sampleRate); - } - - int getSampleRate() const { return m_sampleRate; } - - private: - - MsgBasebandSampleRateNotification(int sampleRate) : - Message(), - m_sampleRate(sampleRate) - { } - - int m_sampleRate; - }; - LocalSink(DeviceAPI *deviceAPI); virtual ~LocalSink(); virtual void destroy() { delete this; } diff --git a/plugins/channelrx/localsink/localsinkgui.cpp b/plugins/channelrx/localsink/localsinkgui.cpp index 905dbab15..558c270c2 100644 --- a/plugins/channelrx/localsink/localsinkgui.cpp +++ b/plugins/channelrx/localsink/localsinkgui.cpp @@ -21,6 +21,7 @@ #include "gui/basicchannelsettingsdialog.h" #include "gui/devicestreamselectiondialog.h" #include "dsp/hbfilterchainconverter.h" +#include "dsp/dspcommands.h" #include "mainwindow.h" #include "localsinkgui.h" @@ -69,11 +70,13 @@ bool LocalSinkGUI::deserialize(const QByteArray& data) bool LocalSinkGUI::handleMessage(const Message& message) { - if (LocalSink::MsgBasebandSampleRateNotification::match(message)) + if (DSPSignalNotification::match(message)) { - LocalSink::MsgBasebandSampleRateNotification& notif = (LocalSink::MsgBasebandSampleRateNotification&) message; + DSPSignalNotification& notif = (DSPSignalNotification&) message; //m_channelMarker.setBandwidth(notif.getSampleRate()); + m_deviceCenterFrequency = notif.getCenterFrequency(); m_basebandSampleRate = notif.getSampleRate(); + updateAbsoluteCenterFrequency(); displayRateAndShift(); return true; } @@ -98,6 +101,7 @@ LocalSinkGUI::LocalSinkGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Baseb ui(new Ui::LocalSinkGUI), m_pluginAPI(pluginAPI), m_deviceUISet(deviceUISet), + m_deviceCenterFrequency(0), m_basebandSampleRate(0), m_tickCount(0) { @@ -363,6 +367,7 @@ void LocalSinkGUI::applyPosition() m_shiftFrequencyFactor = HBFilterChainConverter::convertToString(m_settings.m_log2Decim, m_settings.m_filterChainHash, s); ui->filterChainText->setText(s); + updateAbsoluteCenterFrequency(); displayRateAndShift(); applySettings(); } @@ -382,3 +387,9 @@ void LocalSinkGUI::makeUIConnections() QObject::connect(ui->localDevicesRefresh, &QPushButton::clicked, this, &LocalSinkGUI::on_localDevicesRefresh_clicked); QObject::connect(ui->localDevicePlay, &ButtonSwitch::toggled, this, &LocalSinkGUI::on_localDevicePlay_toggled); } + +void LocalSinkGUI::updateAbsoluteCenterFrequency() +{ + int shift = m_shiftFrequencyFactor * m_basebandSampleRate; + setStatusFrequency(m_deviceCenterFrequency + shift); +} diff --git a/plugins/channelrx/localsink/localsinkgui.h b/plugins/channelrx/localsink/localsinkgui.h index 614f64dfd..a0f8c2b1c 100644 --- a/plugins/channelrx/localsink/localsinkgui.h +++ b/plugins/channelrx/localsink/localsinkgui.h @@ -64,6 +64,7 @@ private: ChannelMarker m_channelMarker; RollupState m_rollupState; LocalSinkSettings m_settings; + qint64 m_deviceCenterFrequency; int m_basebandSampleRate; double m_shiftFrequencyFactor; //!< Channel frequency shift factor bool m_doApplySettings; @@ -85,6 +86,7 @@ private: int getLocalDeviceIndexInCombo(int localDeviceIndex); bool handleMessage(const Message& message); void makeUIConnections(); + void updateAbsoluteCenterFrequency(); void leaveEvent(QEvent*); void enterEvent(QEvent*); diff --git a/plugins/channelrx/noisefigure/noisefigure.cpp b/plugins/channelrx/noisefigure/noisefigure.cpp index 4c6851bf0..0953a028c 100644 --- a/plugins/channelrx/noisefigure/noisefigure.cpp +++ b/plugins/channelrx/noisefigure/noisefigure.cpp @@ -445,10 +445,8 @@ bool NoiseFigure::handleMessage(const Message& cmd) qDebug() << "NoiseFigure::handleMessage: DSPSignalNotification"; m_basebandSink->getInputMessageQueue()->push(rep); // Forward to GUI if any - if (m_guiMessageQueue) - { - rep = new DSPSignalNotification(notif); - m_guiMessageQueue->push(rep); + if (m_guiMessageQueue) { + m_guiMessageQueue->push(new DSPSignalNotification(notif)); } return true; diff --git a/plugins/channelrx/noisefigure/noisefiguregui.cpp b/plugins/channelrx/noisefigure/noisefiguregui.cpp index f70b07ceb..0f9a9c23f 100644 --- a/plugins/channelrx/noisefigure/noisefiguregui.cpp +++ b/plugins/channelrx/noisefigure/noisefiguregui.cpp @@ -269,7 +269,11 @@ bool NoiseFigureGUI::handleMessage(const Message& message) else if (DSPSignalNotification::match(message)) { DSPSignalNotification& notif = (DSPSignalNotification&) message; + m_deviceCenterFrequency = notif.getSampleRate(); m_basebandSampleRate = notif.getSampleRate(); + ui->deltaFrequency->setValueRange(false, 7, -m_basebandSampleRate/2, m_basebandSampleRate/2); + ui->deltaFrequencyLabel->setToolTip(tr("Range %1 %L2 Hz").arg(QChar(0xB1)).arg(m_basebandSampleRate/2)); + updateAbsoluteCenterFrequency(); updateBW(); return true; } @@ -323,6 +327,7 @@ void NoiseFigureGUI::on_deltaFrequency_changed(qint64 value) { m_channelMarker.setCenterFrequency(value); m_settings.m_inputFrequencyOffset = m_channelMarker.getCenterFrequency(); + updateAbsoluteCenterFrequency(); applySettings(); } @@ -587,6 +592,7 @@ NoiseFigureGUI::NoiseFigureGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, B m_pluginAPI(pluginAPI), m_deviceUISet(deviceUISet), m_channelMarker(this), + m_deviceCenterFrequency(0), m_doApplySettings(true), m_basebandSampleRate(1000000), m_tickCount(0), @@ -747,6 +753,7 @@ void NoiseFigureGUI::displaySettings() } getRollupContents()->restoreState(m_rollupState); + updateAbsoluteCenterFrequency(); blockApplySettings(false); } @@ -810,3 +817,8 @@ void NoiseFigureGUI::makeUIConnections() QObject::connect(ui->openReference, &QToolButton::clicked, this, &NoiseFigureGUI::on_openReference_clicked); QObject::connect(ui->clearReference, &QToolButton::clicked, this, &NoiseFigureGUI::on_clearReference_clicked); } + +void NoiseFigureGUI::updateAbsoluteCenterFrequency() +{ + setStatusFrequency(m_deviceCenterFrequency + m_settings.m_inputFrequencyOffset); +} diff --git a/plugins/channelrx/noisefigure/noisefiguregui.h b/plugins/channelrx/noisefigure/noisefiguregui.h index 95981be46..c04a7e12e 100644 --- a/plugins/channelrx/noisefigure/noisefiguregui.h +++ b/plugins/channelrx/noisefigure/noisefiguregui.h @@ -75,6 +75,7 @@ private: ChannelMarker m_channelMarker; RollupState m_rollupState; NoiseFigureSettings m_settings; + qint64 m_deviceCenterFrequency; bool m_doApplySettings; NoiseFigure* m_noiseFigure; @@ -101,6 +102,7 @@ private: void displayStreamIndex(); bool handleMessage(const Message& message); void makeUIConnections(); + void updateAbsoluteCenterFrequency(); void leaveEvent(QEvent*); void enterEvent(QEvent*); diff --git a/plugins/channelrx/radioastronomy/radioastronomygui.cpp b/plugins/channelrx/radioastronomy/radioastronomygui.cpp index 1fd786728..fb73a4a6a 100644 --- a/plugins/channelrx/radioastronomy/radioastronomygui.cpp +++ b/plugins/channelrx/radioastronomy/radioastronomygui.cpp @@ -973,10 +973,16 @@ bool RadioAstronomyGUI::handleMessage(const Message& message) DSPSignalNotification& notif = (DSPSignalNotification&) message; m_basebandSampleRate = notif.getSampleRate(); m_centerFrequency = notif.getCenterFrequency(); + ui->deltaFrequency->setValueRange(false, 7, -m_basebandSampleRate/2, m_basebandSampleRate/2); + ui->deltaFrequencyLabel->setToolTip(tr("Range %1 %L2 Hz").arg(QChar(0xB1)).arg(m_basebandSampleRate/2)); + updateAbsoluteCenterFrequency(); + if (m_settings.m_tempGalLink) { calcGalacticBackgroundTemp(); } + updateTSys0(); + return true; } else if (RadioAstronomy::MsgReportAvailableFeatures::match(message)) @@ -1199,6 +1205,7 @@ void RadioAstronomyGUI::on_deltaFrequency_changed(qint64 value) { m_channelMarker.setCenterFrequency(value); m_settings.m_inputFrequencyOffset = m_channelMarker.getCenterFrequency(); + updateAbsoluteCenterFrequency(); applySettings(); } @@ -1997,6 +2004,7 @@ RadioAstronomyGUI::RadioAstronomyGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUI m_pluginAPI(pluginAPI), m_deviceUISet(deviceUISet), m_channelMarker(this), + m_deviceCenterFrequency(0), m_doApplySettings(true), m_basebandSampleRate(0), m_centerFrequency(0), @@ -2555,6 +2563,7 @@ void RadioAstronomyGUI::displaySettings() } getRollupContents()->restoreState(m_rollupState); + updateAbsoluteCenterFrequency(); blockApplySettings(false); getRollupContents()->arrangeRollups(); } @@ -6145,3 +6154,8 @@ void RadioAstronomyGUI::makeUIConnections() QObject::connect(ui->powerColourPalette, QOverload::of(&QComboBox::currentIndexChanged), this, &RadioAstronomyGUI::on_powerColourPalette_currentIndexChanged); QObject::connect(ui->powerTable, &QTableWidget::cellDoubleClicked, this, &RadioAstronomyGUI::on_powerTable_cellDoubleClicked); } + +void RadioAstronomyGUI::updateAbsoluteCenterFrequency() +{ + setStatusFrequency(m_deviceCenterFrequency + m_settings.m_inputFrequencyOffset); +} diff --git a/plugins/channelrx/radioastronomy/radioastronomygui.h b/plugins/channelrx/radioastronomy/radioastronomygui.h index e40761d1c..d82e265d4 100644 --- a/plugins/channelrx/radioastronomy/radioastronomygui.h +++ b/plugins/channelrx/radioastronomy/radioastronomygui.h @@ -218,6 +218,7 @@ private: ChannelMarker m_channelMarker; RollupState m_rollupState; RadioAstronomySettings m_settings; + qint64 m_deviceCenterFrequency; bool m_doApplySettings; QList m_availableFeatures; @@ -333,6 +334,7 @@ private: void updateRotatorList(const QList& rotators); bool handleMessage(const Message& message); void makeUIConnections(); + void updateAbsoluteCenterFrequency(); double degreesToSteradian(double deg) const; double hpbwToSteradians(double hpbw) const; diff --git a/plugins/channelrx/radioclock/radioclock.cpp b/plugins/channelrx/radioclock/radioclock.cpp index f4c56359d..14b9437ef 100644 --- a/plugins/channelrx/radioclock/radioclock.cpp +++ b/plugins/channelrx/radioclock/radioclock.cpp @@ -165,6 +165,10 @@ bool RadioClock::handleMessage(const Message& cmd) DSPSignalNotification* rep = new DSPSignalNotification(notif); // make a copy qDebug() << "RadioClock::handleMessage: DSPSignalNotification"; m_basebandSink->getInputMessageQueue()->push(rep); + // Forward to GUI if any + if (getMessageQueueToGUI()) { + getMessageQueueToGUI()->push(new DSPSignalNotification(notif)); + } return true; } diff --git a/plugins/channelrx/radioclock/radioclockgui.cpp b/plugins/channelrx/radioclock/radioclockgui.cpp index 19000cd14..f660370ac 100644 --- a/plugins/channelrx/radioclock/radioclockgui.cpp +++ b/plugins/channelrx/radioclock/radioclockgui.cpp @@ -132,6 +132,16 @@ bool RadioClockGUI::handleMessage(const Message& message) ui->status->setText(report.getStatus()); return true; } + else if (DSPSignalNotification::match(message)) + { + const DSPSignalNotification& notif = (const DSPSignalNotification&) message; + m_deviceCenterFrequency = notif.getCenterFrequency(); + m_basebandSampleRate = notif.getSampleRate(); + ui->deltaFrequency->setValueRange(false, 7, -m_basebandSampleRate/2, m_basebandSampleRate/2); + ui->deltaFrequencyLabel->setToolTip(tr("Range %1 %L2 Hz").arg(QChar(0xB1)).arg(m_basebandSampleRate/2)); + updateAbsoluteCenterFrequency(); + return true; + } return false; } @@ -165,6 +175,7 @@ void RadioClockGUI::on_deltaFrequency_changed(qint64 value) { m_channelMarker.setCenterFrequency(value); m_settings.m_inputFrequencyOffset = m_channelMarker.getCenterFrequency(); + updateAbsoluteCenterFrequency(); applySettings(); } @@ -266,6 +277,8 @@ RadioClockGUI::RadioClockGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Bas m_pluginAPI(pluginAPI), m_deviceUISet(deviceUISet), m_channelMarker(this), + m_deviceCenterFrequency(0), + m_basebandSampleRate(1), m_doApplySettings(true), m_tickCount(0) { @@ -372,6 +385,7 @@ void RadioClockGUI::displaySettings() displayStreamIndex(); getRollupContents()->restoreState(m_rollupState); + updateAbsoluteCenterFrequency(); blockApplySettings(false); } @@ -422,3 +436,8 @@ void RadioClockGUI::makeUIConnections() QObject::connect(ui->modulation, QOverload::of(&QComboBox::currentIndexChanged), this, &RadioClockGUI::on_modulation_currentIndexChanged); QObject::connect(ui->timezone, QOverload::of(&QComboBox::currentIndexChanged), this, &RadioClockGUI::on_timezone_currentIndexChanged); } + +void RadioClockGUI::updateAbsoluteCenterFrequency() +{ + setStatusFrequency(m_deviceCenterFrequency + m_settings.m_inputFrequencyOffset); +} diff --git a/plugins/channelrx/radioclock/radioclockgui.h b/plugins/channelrx/radioclock/radioclockgui.h index fe39104f9..c5691d9bc 100644 --- a/plugins/channelrx/radioclock/radioclockgui.h +++ b/plugins/channelrx/radioclock/radioclockgui.h @@ -71,6 +71,8 @@ private: ChannelMarker m_channelMarker; RollupState m_rollupState; RadioClockSettings m_settings; + qint64 m_deviceCenterFrequency; + int m_basebandSampleRate; bool m_doApplySettings; ScopeVis* m_scopeVis; @@ -89,6 +91,7 @@ private: void displayStreamIndex(); bool handleMessage(const Message& message); void makeUIConnections(); + void updateAbsoluteCenterFrequency(); void displayDateTime(); diff --git a/plugins/channelrx/remotesink/remotesink.cpp b/plugins/channelrx/remotesink/remotesink.cpp index 232b35a0f..00e6ef750 100644 --- a/plugins/channelrx/remotesink/remotesink.cpp +++ b/plugins/channelrx/remotesink/remotesink.cpp @@ -155,10 +155,8 @@ bool RemoteSink::handleMessage(const Message& cmd) m_basebandSink->getInputMessageQueue()->push(msgToBaseband); // Forward to the GUI - if (getMessageQueueToGUI()) - { - DSPSignalNotification* msgToGUI = new DSPSignalNotification(notif); // make a copy - getMessageQueueToGUI()->push(msgToGUI); + if (getMessageQueueToGUI()) { + getMessageQueueToGUI()->push(new DSPSignalNotification(notif)); } return true; diff --git a/plugins/channelrx/remotesink/remotesinkgui.cpp b/plugins/channelrx/remotesink/remotesinkgui.cpp index fee089eb3..c2c114c4c 100644 --- a/plugins/channelrx/remotesink/remotesinkgui.cpp +++ b/plugins/channelrx/remotesink/remotesinkgui.cpp @@ -79,6 +79,7 @@ bool RemoteSinkGUI::handleMessage(const Message& message) else if (DSPSignalNotification::match(message)) { DSPSignalNotification& cfg = (DSPSignalNotification&) message; + m_deviceCenterFrequency = cfg.getCenterFrequency(); m_basebandSampleRate = cfg.getSampleRate(); qDebug("RemoteSinkGUI::handleMessage: DSPSignalNotification: m_basebandSampleRate: %d", m_basebandSampleRate); displayRateAndShift(); @@ -97,6 +98,7 @@ RemoteSinkGUI::RemoteSinkGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Bas m_pluginAPI(pluginAPI), m_deviceUISet(deviceUISet), m_basebandSampleRate(0), + m_deviceCenterFrequency(0), m_tickCount(0) { ui->setupUi(getRollupContents()); @@ -176,6 +178,7 @@ void RemoteSinkGUI::displaySettings() applyDecimation(); displayStreamIndex(); getRollupContents()->restoreState(m_rollupState); + updateAbsoluteCenterFrequency(); blockApplySettings(false); } @@ -366,6 +369,7 @@ void RemoteSinkGUI::applyPosition() m_shiftFrequencyFactor = HBFilterChainConverter::convertToString(m_settings.m_log2Decim, m_settings.m_filterChainHash, s); ui->filterChainText->setText(s); + updateAbsoluteCenterFrequency(); displayRateAndShift(); applySettings(); } @@ -387,3 +391,9 @@ void RemoteSinkGUI::makeUIConnections() QObject::connect(ui->nbFECBlocks, &QDial::valueChanged, this, &RemoteSinkGUI::on_nbFECBlocks_valueChanged); QObject::connect(ui->nbTxBytes, QOverload::of(&QComboBox::currentIndexChanged), this, &RemoteSinkGUI::on_nbTxBytes_currentIndexChanged); } + +void RemoteSinkGUI::updateAbsoluteCenterFrequency() +{ + int shift = m_shiftFrequencyFactor * m_basebandSampleRate; + setStatusFrequency(m_deviceCenterFrequency + shift); +} diff --git a/plugins/channelrx/remotesink/remotesinkgui.h b/plugins/channelrx/remotesink/remotesinkgui.h index 10695448c..84332a6fe 100644 --- a/plugins/channelrx/remotesink/remotesinkgui.h +++ b/plugins/channelrx/remotesink/remotesinkgui.h @@ -84,6 +84,7 @@ private: void displayRateAndShift(); bool handleMessage(const Message& message); void makeUIConnections(); + void updateAbsoluteCenterFrequency(); void leaveEvent(QEvent*); void enterEvent(QEvent*); diff --git a/plugins/channelrx/sigmffilesink/sigmffilesink.cpp b/plugins/channelrx/sigmffilesink/sigmffilesink.cpp index 5f1bb3828..ffe624604 100644 --- a/plugins/channelrx/sigmffilesink/sigmffilesink.cpp +++ b/plugins/channelrx/sigmffilesink/sigmffilesink.cpp @@ -170,10 +170,8 @@ bool SigMFFileSink::handleMessage(const Message& cmd) DSPSignalNotification *notif = new DSPSignalNotification(cfg); m_basebandSink->getInputMessageQueue()->push(notif); - if (getMessageQueueToGUI()) - { - DSPSignalNotification *notifToGUI = new DSPSignalNotification(cfg); - getMessageQueueToGUI()->push(notifToGUI); + if (getMessageQueueToGUI()) { + getMessageQueueToGUI()->push(new DSPSignalNotification(cfg)); } return true; diff --git a/plugins/channelrx/sigmffilesink/sigmffilesinkgui.cpp b/plugins/channelrx/sigmffilesink/sigmffilesinkgui.cpp index f7fda943b..f94e3b221 100644 --- a/plugins/channelrx/sigmffilesink/sigmffilesinkgui.cpp +++ b/plugins/channelrx/sigmffilesink/sigmffilesinkgui.cpp @@ -75,7 +75,10 @@ bool SigMFFileSinkGUI::handleMessage(const Message& message) if (DSPSignalNotification::match(message)) { DSPSignalNotification notif = (const DSPSignalNotification&) message; + m_deviceCenterFrequency = notif.getCenterFrequency(); m_basebandSampleRate = notif.getSampleRate(); + ui->deltaFrequency->setValueRange(false, 8, -m_basebandSampleRate/2, m_basebandSampleRate/2); + ui->deltaFrequencyLabel->setToolTip(tr("Range %1 %L2 Hz").arg(QChar(0xB1)).arg(m_basebandSampleRate/2)); displayRate(); if (m_fixedPosition) @@ -165,6 +168,7 @@ SigMFFileSinkGUI::SigMFFileSinkGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISe m_pluginAPI(pluginAPI), m_deviceUISet(deviceUISet), m_channelMarker(this), + m_deviceCenterFrequency(0), m_running(false), m_fixedShiftIndex(0), m_basebandSampleRate(0), @@ -277,6 +281,7 @@ void SigMFFileSinkGUI::displaySettings() setPosFromFrequency(); getRollupContents()->restoreState(m_rollupState); + updateAbsoluteCenterFrequency(); blockApplySettings(false); } @@ -403,6 +408,7 @@ void SigMFFileSinkGUI::on_deltaFrequency_changed(qint64 value) { m_channelMarker.setCenterFrequency(value); m_settings.m_inputFrequencyOffset = m_channelMarker.getCenterFrequency(); + updateAbsoluteCenterFrequency(); setPosFromFrequency(); applySettings(); } @@ -537,6 +543,7 @@ void SigMFFileSinkGUI::setFrequencyFromPos() m_channelMarker.setCenterFrequency(inputFrequencyOffset); ui->deltaFrequency->setValue(m_channelMarker.getCenterFrequency()); m_settings.m_inputFrequencyOffset = m_channelMarker.getCenterFrequency(); + updateAbsoluteCenterFrequency(); } void SigMFFileSinkGUI::setPosFromFrequency() @@ -604,3 +611,8 @@ void SigMFFileSinkGUI::makeUIConnections() QObject::connect(ui->record, &ButtonSwitch::toggled, this, &SigMFFileSinkGUI::on_record_toggled); QObject::connect(ui->showFileDialog, &QPushButton::clicked, this, &SigMFFileSinkGUI::on_showFileDialog_clicked); } + +void SigMFFileSinkGUI::updateAbsoluteCenterFrequency() +{ + setStatusFrequency(m_deviceCenterFrequency + m_settings.m_inputFrequencyOffset); +} diff --git a/plugins/channelrx/sigmffilesink/sigmffilesinkgui.h b/plugins/channelrx/sigmffilesink/sigmffilesinkgui.h index 687dc3bf2..2813e6f44 100644 --- a/plugins/channelrx/sigmffilesink/sigmffilesinkgui.h +++ b/plugins/channelrx/sigmffilesink/sigmffilesinkgui.h @@ -69,6 +69,7 @@ private: ChannelMarker m_channelMarker; RollupState m_rollupState; SigMFFileSinkSettings m_settings; + qint64 m_deviceCenterFrequency; bool m_running; int m_fixedShiftIndex; int m_basebandSampleRate; @@ -97,6 +98,7 @@ private: QString displayScaled(uint64_t value, int precision); bool handleMessage(const Message& message); void makeUIConnections(); + void updateAbsoluteCenterFrequency(); void leaveEvent(QEvent*); void enterEvent(QEvent*); diff --git a/plugins/channelrx/udpsink/udpsink.cpp b/plugins/channelrx/udpsink/udpsink.cpp index 223e9b6e4..4db88391c 100644 --- a/plugins/channelrx/udpsink/udpsink.cpp +++ b/plugins/channelrx/udpsink/udpsink.cpp @@ -141,6 +141,10 @@ bool UDPSink::handleMessage(const Message& cmd) DSPSignalNotification* rep = new DSPSignalNotification(notif); // make a copy qDebug() << "UDPSink::handleMessage: DSPSignalNotification"; m_basebandSink->getInputMessageQueue()->push(rep); + // Forward to GUI if any + if (getMessageQueueToGUI()) { + getMessageQueueToGUI()->push(new DSPSignalNotification(notif)); + } return true; } diff --git a/plugins/channelrx/udpsink/udpsinkgui.cpp b/plugins/channelrx/udpsink/udpsinkgui.cpp index 8010b2d41..c2c9baaf4 100644 --- a/plugins/channelrx/udpsink/udpsinkgui.cpp +++ b/plugins/channelrx/udpsink/udpsinkgui.cpp @@ -20,6 +20,7 @@ #include "plugin/pluginapi.h" #include "dsp/spectrumvis.h" #include "dsp/dspengine.h" +#include "dsp/dspcommands.h" #include "util/simpleserializer.h" #include "util/db.h" #include "gui/basicchannelsettingsdialog.h" @@ -81,6 +82,16 @@ bool UDPSinkGUI::handleMessage(const Message& message ) blockApplySettings(false); return true; } + else if (DSPSignalNotification::match(message)) + { + const DSPSignalNotification& notif = (const DSPSignalNotification&) message; + m_deviceCenterFrequency = notif.getCenterFrequency(); + m_basebandSampleRate = notif.getSampleRate(); + ui->deltaFrequency->setValueRange(false, 8, -m_basebandSampleRate/2, m_basebandSampleRate/2); + ui->deltaFrequencyLabel->setToolTip(tr("Range %1 %L2 Hz").arg(QChar(0xB1)).arg(m_basebandSampleRate/2)); + updateAbsoluteCenterFrequency(); + return true; + } else { return false; @@ -139,7 +150,9 @@ UDPSinkGUI::UDPSinkGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandS ui(new Ui::UDPSinkGUI), m_pluginAPI(pluginAPI), m_deviceUISet(deviceUISet), - m_udpSink(0), + m_udpSink(nullptr), + m_deviceCenterFrequency(0), + m_basebandSampleRate(1), m_channelMarker(this), m_channelPowerAvg(4, 1e-10), m_inPowerAvg(4, 1e-10), @@ -262,6 +275,7 @@ void UDPSinkGUI::displaySettings() displayStreamIndex(); getRollupContents()->restoreState(m_rollupState); + updateAbsoluteCenterFrequency(); blockApplySettings(false); ui->glSpectrum->setSampleRate(m_settings.m_outputSampleRate); @@ -399,6 +413,7 @@ void UDPSinkGUI::on_deltaFrequency_changed(qint64 value) { m_channelMarker.setCenterFrequency(value); m_settings.m_inputFrequencyOffset = m_channelMarker.getCenterFrequency(); + updateAbsoluteCenterFrequency(); applySettings(); } @@ -611,7 +626,6 @@ void UDPSinkGUI::onMenuDialogCalled(const QPoint &p) dialog.move(p); dialog.exec(); - m_settings.m_inputFrequencyOffset = m_channelMarker.getCenterFrequency(); m_settings.m_rgbColor = m_channelMarker.getColor().rgb(); m_settings.m_title = m_channelMarker.getTitle(); m_settings.m_useReverseAPI = dialog.useReverseAPI(); @@ -673,3 +687,8 @@ void UDPSinkGUI::makeUIConnections() QObject::connect(ui->squelchGate, &QDial::valueChanged, this, &UDPSinkGUI::on_squelchGate_valueChanged); QObject::connect(ui->agc, &ButtonSwitch::toggled, this, &UDPSinkGUI::on_agc_toggled); } + +void UDPSinkGUI::updateAbsoluteCenterFrequency() +{ + setStatusFrequency(m_deviceCenterFrequency + m_settings.m_inputFrequencyOffset); +} diff --git a/plugins/channelrx/udpsink/udpsinkgui.h b/plugins/channelrx/udpsink/udpsinkgui.h index 8c58d6dd7..cce4e24be 100644 --- a/plugins/channelrx/udpsink/udpsinkgui.h +++ b/plugins/channelrx/udpsink/udpsinkgui.h @@ -68,6 +68,8 @@ private: DeviceUISet* m_deviceUISet; UDPSink* m_udpSink; UDPSinkSettings m_settings; + qint64 m_deviceCenterFrequency; + int m_basebandSampleRate; ChannelMarker m_channelMarker; RollupState m_rollupState; MovingAverage m_channelPowerAvg; @@ -94,6 +96,7 @@ private: void setSampleFormatIndex(const UDPSinkSettings::SampleFormat& sampleFormat); bool handleMessage(const Message& message); void makeUIConnections(); + void updateAbsoluteCenterFrequency(); void leaveEvent(QEvent*); void enterEvent(QEvent*); diff --git a/plugins/channeltx/filesource/filesource.cpp b/plugins/channeltx/filesource/filesource.cpp index c2035b8e7..2bb39a9b0 100644 --- a/plugins/channeltx/filesource/filesource.cpp +++ b/plugins/channeltx/filesource/filesource.cpp @@ -40,7 +40,6 @@ #include "filesourcebaseband.h" -MESSAGE_CLASS_DEFINITION(FileSource::MsgSampleRateNotification, Message) MESSAGE_CLASS_DEFINITION(FileSource::MsgConfigureFileSource, Message) MESSAGE_CLASS_DEFINITION(FileSource::MsgConfigureFileSourceWork, Message) MESSAGE_CLASS_DEFINITION(FileSource::MsgConfigureFileSourceStreamTiming, Message) @@ -134,8 +133,7 @@ bool FileSource::handleMessage(const Message& cmd) if (m_guiMessageQueue) { qDebug() << "FileSource::handleMessage: DSPSignalNotification: push to GUI"; - MsgSampleRateNotification *msg = MsgSampleRateNotification::create(notif.getSampleRate()); - m_guiMessageQueue->push(msg); + m_guiMessageQueue->push(new DSPSignalNotification(notif)); } return true; diff --git a/plugins/channeltx/filesource/filesource.h b/plugins/channeltx/filesource/filesource.h index 0695b3af4..9b7db6307 100644 --- a/plugins/channeltx/filesource/filesource.h +++ b/plugins/channeltx/filesource/filesource.h @@ -67,26 +67,6 @@ public: { } }; - class MsgSampleRateNotification : public Message { - MESSAGE_CLASS_DECLARATION - - public: - static MsgSampleRateNotification* create(int sampleRate) { - return new MsgSampleRateNotification(sampleRate); - } - - int getSampleRate() const { return m_sampleRate; } - - private: - - MsgSampleRateNotification(int sampleRate) : - Message(), - m_sampleRate(sampleRate) - { } - - int m_sampleRate; - }; - class MsgConfigureFileSourceWork : public Message { MESSAGE_CLASS_DECLARATION diff --git a/plugins/channeltx/filesource/filesourcegui.cpp b/plugins/channeltx/filesource/filesourcegui.cpp index 06e0217d1..4fa0fcc23 100644 --- a/plugins/channeltx/filesource/filesourcegui.cpp +++ b/plugins/channeltx/filesource/filesourcegui.cpp @@ -22,6 +22,7 @@ #include "device/deviceapi.h" #include "device/deviceuiset.h" #include "dsp/hbfilterchainconverter.h" +#include "dsp/dspcommands.h" #include "gui/basicchannelsettingsdialog.h" #include "gui/devicestreamselectiondialog.h" #include "util/db.h" @@ -70,10 +71,12 @@ bool FileSourceGUI::deserialize(const QByteArray& data) bool FileSourceGUI::handleMessage(const Message& message) { - if (FileSource::MsgSampleRateNotification::match(message)) + if (DSPSignalNotification::match(message)) { - FileSource::MsgSampleRateNotification& notif = (FileSource::MsgSampleRateNotification&) message; + DSPSignalNotification& notif = (DSPSignalNotification&) message; + m_deviceCenterFrequency = notif.getCenterFrequency(); m_sampleRate = notif.getSampleRate(); + updateAbsoluteCenterFrequency(); displayRateAndShift(); return true; } @@ -165,6 +168,7 @@ FileSourceGUI::FileSourceGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Bas ui(new Ui::FileSourceGUI), m_pluginAPI(pluginAPI), m_deviceUISet(deviceUISet), + m_deviceCenterFrequency(0), m_sampleRate(0), m_shiftFrequencyFactor(0.0), m_fileSampleRate(0), @@ -315,6 +319,7 @@ void FileSourceGUI::displaySettings() ui->interpolationFactor->setCurrentIndex(m_settings.m_log2Interp); applyInterpolation(); getRollupContents()->restoreState(m_rollupState); + updateAbsoluteCenterFrequency(); blockApplySettings(false); } @@ -497,6 +502,7 @@ void FileSourceGUI::applyPosition() m_shiftFrequencyFactor = HBFilterChainConverter::convertToString(m_settings.m_log2Interp, m_settings.m_filterChainHash, s); ui->filterChainText->setText(s); + updateAbsoluteCenterFrequency(); displayRateAndShift(); applySettings(); } @@ -540,3 +546,9 @@ void FileSourceGUI::makeUIConnections() QObject::connect(ui->play, &ButtonSwitch::toggled, this, &FileSourceGUI::on_play_toggled); QObject::connect(ui->navTime, &QSlider::valueChanged, this, &FileSourceGUI::on_navTime_valueChanged); } + +void FileSourceGUI::updateAbsoluteCenterFrequency() +{ + int shift = m_shiftFrequencyFactor * m_sampleRate; + setStatusFrequency(m_deviceCenterFrequency + shift); +} diff --git a/plugins/channeltx/filesource/filesourcegui.h b/plugins/channeltx/filesource/filesourcegui.h index a270bdbae..7f5c227c7 100644 --- a/plugins/channeltx/filesource/filesourcegui.h +++ b/plugins/channeltx/filesource/filesourcegui.h @@ -64,6 +64,7 @@ private: ChannelMarker m_channelMarker; RollupState m_rollupState; FileSourceSettings m_settings; + qint64 m_deviceCenterFrequency; int m_sampleRate; double m_shiftFrequencyFactor; //!< Channel frequency shift factor int m_fileSampleRate; @@ -94,6 +95,7 @@ private: void displayStreamIndex(); bool handleMessage(const Message& message); void makeUIConnections(); + void updateAbsoluteCenterFrequency(); void leaveEvent(QEvent*); void enterEvent(QEvent*); diff --git a/plugins/channeltx/localsource/localsource.cpp b/plugins/channeltx/localsource/localsource.cpp index 99898a739..7e1bef605 100644 --- a/plugins/channeltx/localsource/localsource.cpp +++ b/plugins/channeltx/localsource/localsource.cpp @@ -38,7 +38,6 @@ #include "localsourcebaseband.h" MESSAGE_CLASS_DEFINITION(LocalSource::MsgConfigureLocalSource, Message) -MESSAGE_CLASS_DEFINITION(LocalSource::MsgBasebandSampleRateNotification, Message) const char* const LocalSource::m_channelIdURI = "sdrangel.channel.localsource"; const char* const LocalSource::m_channelId = "LocalSource"; @@ -122,10 +121,8 @@ bool LocalSource::handleMessage(const Message& cmd) DSPSignalNotification *msg = new DSPSignalNotification(cfg.getSampleRate(), cfg.getCenterFrequency()); m_basebandSource->getInputMessageQueue()->push(msg); - if (m_guiMessageQueue) - { - MsgBasebandSampleRateNotification *msg = MsgBasebandSampleRateNotification::create(cfg.getSampleRate()); - m_guiMessageQueue->push(msg); + if (m_guiMessageQueue) { + m_guiMessageQueue->push(new DSPSignalNotification(cfg)); } return true; diff --git a/plugins/channeltx/localsource/localsource.h b/plugins/channeltx/localsource/localsource.h index 76639d3ea..889ebc125 100644 --- a/plugins/channeltx/localsource/localsource.h +++ b/plugins/channeltx/localsource/localsource.h @@ -60,26 +60,6 @@ public: { } }; - class MsgBasebandSampleRateNotification : public Message { - MESSAGE_CLASS_DECLARATION - - public: - static MsgBasebandSampleRateNotification* create(int sampleRate) { - return new MsgBasebandSampleRateNotification(sampleRate); - } - - int getBasebandSampleRate() const { return m_sampleRate; } - - private: - - MsgBasebandSampleRateNotification(int sampleRate) : - Message(), - m_sampleRate(sampleRate) - { } - - int m_sampleRate; - }; - LocalSource(DeviceAPI *deviceAPI); virtual ~LocalSource(); virtual void destroy() { delete this; } diff --git a/plugins/channeltx/localsource/localsourcegui.cpp b/plugins/channeltx/localsource/localsourcegui.cpp index 00866ccea..d16375d26 100644 --- a/plugins/channeltx/localsource/localsourcegui.cpp +++ b/plugins/channeltx/localsource/localsourcegui.cpp @@ -21,6 +21,7 @@ #include "gui/basicchannelsettingsdialog.h" #include "gui/devicestreamselectiondialog.h" #include "dsp/hbfilterchainconverter.h" +#include "dsp/dspcommands.h" #include "mainwindow.h" #include "localsourcegui.h" @@ -64,10 +65,12 @@ bool LocalSourceGUI::deserialize(const QByteArray& data) bool LocalSourceGUI::handleMessage(const Message& message) { - if (LocalSource::MsgBasebandSampleRateNotification::match(message)) + if (DSPSignalNotification::match(message)) { - LocalSource::MsgBasebandSampleRateNotification& notif = (LocalSource::MsgBasebandSampleRateNotification&) message; - m_basebandSampleRate = notif.getBasebandSampleRate(); + DSPSignalNotification& notif = (DSPSignalNotification&) message; + m_deviceCenterFrequency = notif.getCenterFrequency(); + m_basebandSampleRate = notif.getSampleRate(); + updateAbsoluteCenterFrequency(); displayRateAndShift(); return true; } @@ -93,6 +96,7 @@ LocalSourceGUI::LocalSourceGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, B m_pluginAPI(pluginAPI), m_deviceUISet(deviceUISet), m_basebandSampleRate(0), + m_deviceCenterFrequency(0), m_tickCount(0) { ui->setupUi(getRollupContents()); @@ -168,6 +172,7 @@ void LocalSourceGUI::displaySettings() ui->localDevicePlay->setChecked(m_settings.m_play); applyInterpolation(); getRollupContents()->restoreState(m_rollupState); + updateAbsoluteCenterFrequency(); blockApplySettings(false); } @@ -332,6 +337,7 @@ void LocalSourceGUI::applyPosition() m_shiftFrequencyFactor = HBFilterChainConverter::convertToString(m_settings.m_log2Interp, m_settings.m_filterChainHash, s); ui->filterChainText->setText(s); + updateAbsoluteCenterFrequency(); displayRateAndShift(); applySettings(); } @@ -351,3 +357,9 @@ void LocalSourceGUI::makeUIConnections() QObject::connect(ui->localDevicesRefresh, &QPushButton::clicked, this, &LocalSourceGUI::on_localDevicesRefresh_clicked); QObject::connect(ui->localDevicePlay, &ButtonSwitch::toggled, this, &LocalSourceGUI::on_localDevicePlay_toggled); } + +void LocalSourceGUI::updateAbsoluteCenterFrequency() +{ + int shift = m_shiftFrequencyFactor * m_basebandSampleRate; + setStatusFrequency(m_deviceCenterFrequency + shift); +} diff --git a/plugins/channeltx/localsource/localsourcegui.h b/plugins/channeltx/localsource/localsourcegui.h index 5ae429b53..3dc9b8908 100644 --- a/plugins/channeltx/localsource/localsourcegui.h +++ b/plugins/channeltx/localsource/localsourcegui.h @@ -85,6 +85,7 @@ private: void updateLocalDevices(); bool handleMessage(const Message& message); void makeUIConnections(); + void updateAbsoluteCenterFrequency(); void leaveEvent(QEvent*); void enterEvent(QEvent*); diff --git a/plugins/channeltx/mod802.15.4/ieee_802_15_4_mod.cpp b/plugins/channeltx/mod802.15.4/ieee_802_15_4_mod.cpp index 8138c9078..029063a44 100644 --- a/plugins/channeltx/mod802.15.4/ieee_802_15_4_mod.cpp +++ b/plugins/channeltx/mod802.15.4/ieee_802_15_4_mod.cpp @@ -145,10 +145,8 @@ bool IEEE_802_15_4_Mod::handleMessage(const Message& cmd) m_basebandSource->getInputMessageQueue()->push(rep); // Forward to GUI - if (getMessageQueueToGUI()) - { - DSPSignalNotification *notifToGUI = new DSPSignalNotification(notif); - getMessageQueueToGUI()->push(notifToGUI); + if (getMessageQueueToGUI()) { + getMessageQueueToGUI()->push(new DSPSignalNotification(notif)); } return true; diff --git a/plugins/channeltx/mod802.15.4/ieee_802_15_4_modgui.cpp b/plugins/channeltx/mod802.15.4/ieee_802_15_4_modgui.cpp index b97f63e6f..bf811a207 100644 --- a/plugins/channeltx/mod802.15.4/ieee_802_15_4_modgui.cpp +++ b/plugins/channeltx/mod802.15.4/ieee_802_15_4_modgui.cpp @@ -104,7 +104,11 @@ bool IEEE_802_15_4_ModGUI::handleMessage(const Message& message) if (DSPSignalNotification::match(message)) { DSPSignalNotification& notif = (DSPSignalNotification&) message; + m_deviceCenterFrequency = notif.getCenterFrequency(); m_basebandSampleRate = notif.getSampleRate(); + ui->deltaFrequency->setValueRange(false, 7, -m_basebandSampleRate/2, m_basebandSampleRate/2); + ui->deltaFrequencyLabel->setToolTip(tr("Range %1 %L2 Hz").arg(QChar(0xB1)).arg(m_basebandSampleRate/2)); + updateAbsoluteCenterFrequency(); m_scopeVis->setLiveRate(m_basebandSampleRate); checkSampleRate(); return true; @@ -172,6 +176,7 @@ void IEEE_802_15_4_ModGUI::on_deltaFrequency_changed(qint64 value) { m_channelMarker.setCenterFrequency(value); m_settings.m_inputFrequencyOffset = m_channelMarker.getCenterFrequency(); + updateAbsoluteCenterFrequency(); applySettings(); } @@ -332,7 +337,6 @@ void IEEE_802_15_4_ModGUI::onMenuDialogCalled(const QPoint &p) dialog.move(p); dialog.exec(); - m_settings.m_inputFrequencyOffset = m_channelMarker.getCenterFrequency(); m_settings.m_rgbColor = m_channelMarker.getColor().rgb(); m_settings.m_title = m_channelMarker.getTitle(); m_settings.m_useReverseAPI = dialog.useReverseAPI(); @@ -371,6 +375,7 @@ IEEE_802_15_4_ModGUI::IEEE_802_15_4_ModGUI(PluginAPI* pluginAPI, DeviceUISet *de m_pluginAPI(pluginAPI), m_deviceUISet(deviceUISet), m_channelMarker(this), + m_deviceCenterFrequency(0), m_doApplySettings(true), m_basebandSampleRate(12000000) { @@ -566,6 +571,7 @@ void IEEE_802_15_4_ModGUI::displaySettings() ui->udpPort->setText(QString::number(m_settings.m_udpPort)); getRollupContents()->restoreState(m_rollupState); + updateAbsoluteCenterFrequency(); blockApplySettings(false); } @@ -639,3 +645,8 @@ void IEEE_802_15_4_ModGUI::makeUIConnections() QObject::connect(ui->udpAddress, &QLineEdit::editingFinished, this, &IEEE_802_15_4_ModGUI::on_udpAddress_editingFinished); QObject::connect(ui->udpPort, &QLineEdit::editingFinished, this, &IEEE_802_15_4_ModGUI::on_udpPort_editingFinished); } + +void IEEE_802_15_4_ModGUI::updateAbsoluteCenterFrequency() +{ + setStatusFrequency(m_deviceCenterFrequency + m_settings.m_inputFrequencyOffset); +} diff --git a/plugins/channeltx/mod802.15.4/ieee_802_15_4_modgui.h b/plugins/channeltx/mod802.15.4/ieee_802_15_4_modgui.h index 59df48f6d..a7f9266d6 100644 --- a/plugins/channeltx/mod802.15.4/ieee_802_15_4_modgui.h +++ b/plugins/channeltx/mod802.15.4/ieee_802_15_4_modgui.h @@ -74,6 +74,7 @@ private: ChannelMarker m_channelMarker; RollupState m_rollupState; IEEE_802_15_4_ModSettings m_settings; + qint64 m_deviceCenterFrequency; bool m_doApplySettings; SpectrumVis* m_spectrumVis; ScopeVis* m_scopeVis; @@ -98,6 +99,7 @@ private: QString getDisplayValueWithMultiplier(int value); bool handleMessage(const Message& message); void makeUIConnections(); + void updateAbsoluteCenterFrequency(); void leaveEvent(QEvent*); void enterEvent(QEvent*); diff --git a/plugins/channeltx/modais/aismod.cpp b/plugins/channeltx/modais/aismod.cpp index 22f71d38f..40eccd9a4 100644 --- a/plugins/channeltx/modais/aismod.cpp +++ b/plugins/channeltx/modais/aismod.cpp @@ -164,6 +164,10 @@ bool AISMod::handleMessage(const Message& cmd) DSPSignalNotification* rep = new DSPSignalNotification(notif); // make a copy qDebug() << "AISMod::handleMessage: DSPSignalNotification"; m_basebandSource->getInputMessageQueue()->push(rep); + // Forward to GUI if any + if (getMessageQueueToGUI()) { + getMessageQueueToGUI()->push(new DSPSignalNotification(notif)); + } return true; } else if (MainCore::MsgChannelDemodQuery::match(cmd)) diff --git a/plugins/channeltx/modais/aismodgui.cpp b/plugins/channeltx/modais/aismodgui.cpp index 3c21a40fa..7a1fec6cd 100644 --- a/plugins/channeltx/modais/aismodgui.cpp +++ b/plugins/channeltx/modais/aismodgui.cpp @@ -25,6 +25,7 @@ #include "dsp/spectrumvis.h" #include "dsp/scopevis.h" #include "dsp/glscopesettings.h" +#include "dsp/dspcommands.h" #include "device/deviceuiset.h" #include "plugin/pluginapi.h" #include "util/simpleserializer.h" @@ -96,6 +97,16 @@ bool AISModGUI::handleMessage(const Message& message) ui->message->setText(m_settings.m_data); return true; } + else if (DSPSignalNotification::match(message)) + { + const DSPSignalNotification& notif = (const DSPSignalNotification&) message; + m_deviceCenterFrequency = notif.getCenterFrequency(); + m_basebandSampleRate = notif.getSampleRate(); + ui->deltaFrequency->setValueRange(false, 7, -m_basebandSampleRate/2, m_basebandSampleRate/2); + ui->deltaFrequencyLabel->setToolTip(tr("Range %1 %L2 Hz").arg(QChar(0xB1)).arg(m_basebandSampleRate/2)); + updateAbsoluteCenterFrequency(); + return true; + } else { return false; @@ -127,6 +138,7 @@ void AISModGUI::on_deltaFrequency_changed(qint64 value) { m_channelMarker.setCenterFrequency(value); m_settings.m_inputFrequencyOffset = m_channelMarker.getCenterFrequency(); + updateAbsoluteCenterFrequency(); applySettings(); } @@ -348,7 +360,6 @@ void AISModGUI::onMenuDialogCalled(const QPoint &p) dialog.move(p); dialog.exec(); - m_settings.m_inputFrequencyOffset = m_channelMarker.getCenterFrequency(); m_settings.m_rgbColor = m_channelMarker.getColor().rgb(); m_settings.m_title = m_channelMarker.getTitle(); m_settings.m_useReverseAPI = dialog.useReverseAPI(); @@ -387,6 +398,8 @@ AISModGUI::AISModGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSam m_pluginAPI(pluginAPI), m_deviceUISet(deviceUISet), m_channelMarker(this), + m_deviceCenterFrequency(0), + m_basebandSampleRate(1), m_doApplySettings(true) { ui->setupUi(getRollupContents()); @@ -557,6 +570,7 @@ void AISModGUI::displaySettings() ui->message->setText(m_settings.m_data); getRollupContents()->restoreState(m_rollupState); + updateAbsoluteCenterFrequency(); blockApplySettings(false); } @@ -613,3 +627,8 @@ void AISModGUI::makeUIConnections() QObject::connect(ui->udpAddress, &QLineEdit::editingFinished, this, &AISModGUI::on_udpAddress_editingFinished); QObject::connect(ui->udpPort, &QLineEdit::editingFinished, this, &AISModGUI::on_udpPort_editingFinished); } + +void AISModGUI::updateAbsoluteCenterFrequency() +{ + setStatusFrequency(m_deviceCenterFrequency + m_settings.m_inputFrequencyOffset); +} diff --git a/plugins/channeltx/modais/aismodgui.h b/plugins/channeltx/modais/aismodgui.h index 3ceea99bb..502d5eb0b 100644 --- a/plugins/channeltx/modais/aismodgui.h +++ b/plugins/channeltx/modais/aismodgui.h @@ -68,6 +68,8 @@ private: ChannelMarker m_channelMarker; RollupState m_rollupState; AISModSettings m_settings; + qint64 m_deviceCenterFrequency; + int m_basebandSampleRate; bool m_doApplySettings; SpectrumVis* m_spectrumVis; ScopeVis* m_scopeVis; @@ -87,6 +89,7 @@ private: void displayStreamIndex(); bool handleMessage(const Message& message); void makeUIConnections(); + void updateAbsoluteCenterFrequency(); void leaveEvent(QEvent*); void enterEvent(QEvent*); diff --git a/plugins/channeltx/modam/ammod.cpp b/plugins/channeltx/modam/ammod.cpp index d1f80abfd..e50093b66 100644 --- a/plugins/channeltx/modam/ammod.cpp +++ b/plugins/channeltx/modam/ammod.cpp @@ -196,6 +196,10 @@ bool AMMod::handleMessage(const Message& cmd) DSPSignalNotification* rep = new DSPSignalNotification(notif); // make a copy qDebug() << "AMMod::handleMessage: DSPSignalNotification"; m_basebandSource->getInputMessageQueue()->push(rep); + // Forward to GUI if any + if (getMessageQueueToGUI()) { + getMessageQueueToGUI()->push(new DSPSignalNotification(notif)); + } return true; } diff --git a/plugins/channeltx/modam/ammodgui.cpp b/plugins/channeltx/modam/ammodgui.cpp index f80e21bfb..e73ee98ef 100644 --- a/plugins/channeltx/modam/ammodgui.cpp +++ b/plugins/channeltx/modam/ammodgui.cpp @@ -28,6 +28,7 @@ #include "util/db.h" #include "dsp/dspengine.h" #include "dsp/cwkeyer.h" +#include "dsp/dspcommands.h" #include "gui/crightclickenabler.h" #include "gui/audioselectdialog.h" #include "gui/basicchannelsettingsdialog.h" @@ -105,6 +106,16 @@ bool AMModGUI::handleMessage(const Message& message) ui->cwKeyerGUI->displaySettings(); return true; } + else if (DSPSignalNotification::match(message)) + { + const DSPSignalNotification& notif = (const DSPSignalNotification&) message; + m_deviceCenterFrequency = notif.getCenterFrequency(); + m_basebandSampleRate = notif.getSampleRate(); + ui->deltaFrequency->setValueRange(false, 7, -m_basebandSampleRate/2, m_basebandSampleRate/2); + ui->deltaFrequencyLabel->setToolTip(tr("Range %1 %L2 Hz").arg(QChar(0xB1)).arg(m_basebandSampleRate/2)); + updateAbsoluteCenterFrequency(); + return true; + } else { return false; @@ -135,6 +146,7 @@ void AMModGUI::on_deltaFrequency_changed(qint64 value) { m_channelMarker.setCenterFrequency(value); m_settings.m_inputFrequencyOffset = value; + updateAbsoluteCenterFrequency(); applySettings(); } @@ -288,7 +300,6 @@ void AMModGUI::onMenuDialogCalled(const QPoint &p) dialog.move(p); dialog.exec(); - m_settings.m_inputFrequencyOffset = m_channelMarker.getCenterFrequency(); m_settings.m_rgbColor = m_channelMarker.getColor().rgb(); m_settings.m_title = m_channelMarker.getTitle(); m_settings.m_useReverseAPI = dialog.useReverseAPI(); @@ -327,6 +338,8 @@ AMModGUI::AMModGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSampl m_pluginAPI(pluginAPI), m_deviceUISet(deviceUISet), m_channelMarker(this), + m_deviceCenterFrequency(0), + m_basebandSampleRate(1), m_doApplySettings(true), m_recordLength(0), m_recordSampleRate(48000), @@ -462,6 +475,7 @@ void AMModGUI::displaySettings() displayStreamIndex(); getRollupContents()->restoreState(m_rollupState); + updateAbsoluteCenterFrequency(); blockApplySettings(false); } @@ -601,3 +615,8 @@ void AMModGUI::makeUIConnections() QObject::connect(ui->feedbackEnable, &QToolButton::toggled, this, &AMModGUI::on_feedbackEnable_toggled); QObject::connect(ui->feedbackVolume, &QDial::valueChanged, this, &AMModGUI::on_feedbackVolume_valueChanged); } + +void AMModGUI::updateAbsoluteCenterFrequency() +{ + setStatusFrequency(m_deviceCenterFrequency + m_settings.m_inputFrequencyOffset); +} diff --git a/plugins/channeltx/modam/ammodgui.h b/plugins/channeltx/modam/ammodgui.h index 5577ab9ec..c9ec32652 100644 --- a/plugins/channeltx/modam/ammodgui.h +++ b/plugins/channeltx/modam/ammodgui.h @@ -67,6 +67,8 @@ private: ChannelMarker m_channelMarker; RollupState m_rollupState; AMModSettings m_settings; + qint64 m_deviceCenterFrequency; + int m_basebandSampleRate; bool m_doApplySettings; AMMod* m_amMod; @@ -93,6 +95,7 @@ private: void updateWithStreamTime(); bool handleMessage(const Message& message); void makeUIConnections(); + void updateAbsoluteCenterFrequency(); void leaveEvent(QEvent*); void enterEvent(QEvent*); diff --git a/plugins/channeltx/modatv/atvmod.cpp b/plugins/channeltx/modatv/atvmod.cpp index 5377e20a6..c39f1723c 100644 --- a/plugins/channeltx/modatv/atvmod.cpp +++ b/plugins/channeltx/modatv/atvmod.cpp @@ -161,6 +161,10 @@ bool ATVMod::handleMessage(const Message& cmd) DSPSignalNotification* rep = new DSPSignalNotification(notif); // make a copy qDebug() << "ATVMod::handleMessage: DSPSignalNotification"; m_basebandSource->getInputMessageQueue()->push(rep); + // Forward to GUI if any + if (getMessageQueueToGUI()) { + getMessageQueueToGUI()->push(new DSPSignalNotification(notif)); + } return true; } diff --git a/plugins/channeltx/modatv/atvmodgui.cpp b/plugins/channeltx/modatv/atvmodgui.cpp index 460d68cad..ec5e1ea7a 100644 --- a/plugins/channeltx/modatv/atvmodgui.cpp +++ b/plugins/channeltx/modatv/atvmodgui.cpp @@ -28,6 +28,7 @@ #include "plugin/pluginapi.h" #include "util/simpleserializer.h" #include "dsp/dspengine.h" +#include "dsp/dspcommands.h" #include "util/db.h" #include "gui/basicchannelsettingsdialog.h" #include "gui/devicestreamselectiondialog.h" @@ -54,6 +55,8 @@ ATVModGUI::ATVModGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSam m_pluginAPI(pluginAPI), m_deviceUISet(deviceUISet), m_channelMarker(this), + m_deviceCenterFrequency(0), + m_basebandSampleRate(1), m_doApplySettings(true), m_videoLength(0), m_videoFrameRate(48000), @@ -228,6 +231,16 @@ bool ATVModGUI::handleMessage(const Message& message) ui->videoFileText->setText(cfg.getFileName()); return true; } + else if (DSPSignalNotification::match(message)) + { + const DSPSignalNotification& notif = (const DSPSignalNotification&) message; + m_deviceCenterFrequency = notif.getCenterFrequency(); + m_basebandSampleRate = notif.getSampleRate(); + ui->deltaFrequency->setValueRange(false, 8, -m_basebandSampleRate/2, m_basebandSampleRate/2); + ui->deltaFrequencyLabel->setToolTip(tr("Range %1 %L2 Hz").arg(QChar(0xB1)).arg(m_basebandSampleRate/2)); + updateAbsoluteCenterFrequency(); + return true; + } else { return false; @@ -435,6 +448,7 @@ void ATVModGUI::on_deltaFrequency_changed(qint64 value) { m_channelMarker.setCenterFrequency(value); m_settings.m_inputFrequencyOffset = value; + updateAbsoluteCenterFrequency(); applySettings(); } @@ -691,7 +705,6 @@ void ATVModGUI::onMenuDialogCalled(const QPoint &p) dialog.move(p); dialog.exec(); - m_settings.m_inputFrequencyOffset = m_channelMarker.getCenterFrequency(); m_settings.m_rgbColor = m_channelMarker.getColor().rgb(); m_settings.m_title = m_channelMarker.getTitle(); m_settings.m_useReverseAPI = dialog.useReverseAPI(); @@ -801,6 +814,7 @@ void ATVModGUI::displaySettings() ui->playLoop->setChecked(m_settings.m_videoPlayLoop); getRollupContents()->restoreState(m_rollupState); + updateAbsoluteCenterFrequency(); blockApplySettings(false); } @@ -899,3 +913,8 @@ void ATVModGUI::makeUIConnections() QObject::connect(ui->overlayTextShow, &ButtonSwitch::toggled, this, &ATVModGUI::on_overlayTextShow_toggled); QObject::connect(ui->overlayText, &QLineEdit::textEdited, this, &ATVModGUI::on_overlayText_textEdited); } + +void ATVModGUI::updateAbsoluteCenterFrequency() +{ + setStatusFrequency(m_deviceCenterFrequency + m_settings.m_inputFrequencyOffset); +} diff --git a/plugins/channeltx/modatv/atvmodgui.h b/plugins/channeltx/modatv/atvmodgui.h index 2c6c90604..b6fbd5af3 100644 --- a/plugins/channeltx/modatv/atvmodgui.h +++ b/plugins/channeltx/modatv/atvmodgui.h @@ -66,6 +66,8 @@ private: ChannelMarker m_channelMarker; RollupState m_rollupState; ATVModSettings m_settings; + qint64 m_deviceCenterFrequency; + int m_basebandSampleRate; bool m_doApplySettings; ATVMod* m_atvMod; @@ -99,6 +101,7 @@ private: int getFPSIndex(int fps); bool handleMessage(const Message& message); void makeUIConnections(); + void updateAbsoluteCenterFrequency(); void leaveEvent(QEvent*); void enterEvent(QEvent*); diff --git a/plugins/channeltx/modchirpchat/chirpchatmod.cpp b/plugins/channeltx/modchirpchat/chirpchatmod.cpp index dc849b71b..4ed11d89b 100644 --- a/plugins/channeltx/modchirpchat/chirpchatmod.cpp +++ b/plugins/channeltx/modchirpchat/chirpchatmod.cpp @@ -132,10 +132,8 @@ bool ChirpChatMod::handleMessage(const Message& cmd) m_basebandSource->getInputMessageQueue()->push(rep); // Forward to the GUI - if (getMessageQueueToGUI()) - { - DSPSignalNotification* repToGUI = new DSPSignalNotification(notif); // make a copy - getMessageQueueToGUI()->push(repToGUI); + if (getMessageQueueToGUI()) { + getMessageQueueToGUI()->push(new DSPSignalNotification(notif)); } return true; diff --git a/plugins/channeltx/modchirpchat/chirpchatmodgui.cpp b/plugins/channeltx/modchirpchat/chirpchatmodgui.cpp index 6390a39ec..a7623d984 100644 --- a/plugins/channeltx/modchirpchat/chirpchatmodgui.cpp +++ b/plugins/channeltx/modchirpchat/chirpchatmodgui.cpp @@ -102,6 +102,7 @@ bool ChirpChatModGUI::handleMessage(const Message& message) else if (DSPSignalNotification::match(message)) { DSPSignalNotification& notif = (DSPSignalNotification&) message; + m_deviceCenterFrequency = notif.getCenterFrequency(); int basebandSampleRate = notif.getSampleRate(); qDebug() << "ChirpChatModGUI::handleMessage: DSPSignalNotification: m_basebandSampleRate: " << basebandSampleRate; @@ -111,6 +112,10 @@ bool ChirpChatModGUI::handleMessage(const Message& message) setBandwidths(); } + ui->deltaFrequency->setValueRange(false, 7, -m_basebandSampleRate/2, m_basebandSampleRate/2); + ui->deltaFrequencyLabel->setToolTip(tr("Range %1 %L2 Hz").arg(QChar(0xB1)).arg(m_basebandSampleRate/2)); + updateAbsoluteCenterFrequency(); + return true; } else @@ -143,6 +148,7 @@ void ChirpChatModGUI::on_deltaFrequency_changed(qint64 value) { m_channelMarker.setCenterFrequency(value); m_settings.m_inputFrequencyOffset = m_channelMarker.getCenterFrequency(); + updateAbsoluteCenterFrequency(); applySettings(); } @@ -374,7 +380,6 @@ void ChirpChatModGUI::onMenuDialogCalled(const QPoint &p) dialog.move(p); dialog.exec(); - m_settings.m_inputFrequencyOffset = m_channelMarker.getCenterFrequency(); m_settings.m_rgbColor = m_channelMarker.getColor().rgb(); m_settings.m_title = m_channelMarker.getTitle(); m_settings.m_useReverseAPI = dialog.useReverseAPI(); @@ -413,6 +418,7 @@ ChirpChatModGUI::ChirpChatModGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, m_pluginAPI(pluginAPI), m_deviceUISet(deviceUISet), m_channelMarker(this), + m_deviceCenterFrequency(0), m_basebandSampleRate(125000), m_doApplySettings(true), m_tickCount(0) @@ -530,6 +536,7 @@ void ChirpChatModGUI::displaySettings() ui->udpAddress->setText(m_settings.m_udpAddress); ui->udpPort->setText(QString::number(m_settings.m_udpPort)); getRollupContents()->restoreState(m_rollupState); + updateAbsoluteCenterFrequency(); blockApplySettings(false); } @@ -653,3 +660,8 @@ void ChirpChatModGUI::makeUIConnections() QObject::connect(ui->udpAddress, &QLineEdit::editingFinished, this, &ChirpChatModGUI::on_udpAddress_editingFinished); QObject::connect(ui->udpPort, &QLineEdit::editingFinished, this, &ChirpChatModGUI::on_udpPort_editingFinished); } + +void ChirpChatModGUI::updateAbsoluteCenterFrequency() +{ + setStatusFrequency(m_deviceCenterFrequency + m_settings.m_inputFrequencyOffset); +} diff --git a/plugins/channeltx/modchirpchat/chirpchatmodgui.h b/plugins/channeltx/modchirpchat/chirpchatmodgui.h index 78fd53653..ce012587b 100644 --- a/plugins/channeltx/modchirpchat/chirpchatmodgui.h +++ b/plugins/channeltx/modchirpchat/chirpchatmodgui.h @@ -65,6 +65,7 @@ private: ChannelMarker m_channelMarker; RollupState m_rollupState; ChirpChatModSettings m_settings; + qint64 m_deviceCenterFrequency; int m_basebandSampleRate; bool m_doApplySettings; @@ -86,6 +87,7 @@ private: void setBandwidths(); bool handleMessage(const Message& message); void makeUIConnections(); + void updateAbsoluteCenterFrequency(); void leaveEvent(QEvent*); void enterEvent(QEvent*); diff --git a/plugins/channeltx/moddatv/datvmod.cpp b/plugins/channeltx/moddatv/datvmod.cpp index 2cbe26e06..796ebd9b9 100644 --- a/plugins/channeltx/moddatv/datvmod.cpp +++ b/plugins/channeltx/moddatv/datvmod.cpp @@ -164,6 +164,10 @@ bool DATVMod::handleMessage(const Message& cmd) DSPSignalNotification* rep = new DSPSignalNotification(notif); qDebug() << "DATVMod::handleMessage: DSPSignalNotification"; m_basebandSource->getInputMessageQueue()->push(rep); + // Forward to GUI if any + if (getMessageQueueToGUI()) { + getMessageQueueToGUI()->push(new DSPSignalNotification(notif)); + } return true; } diff --git a/plugins/channeltx/moddatv/datvmodgui.cpp b/plugins/channeltx/moddatv/datvmodgui.cpp index 3d83a96b6..837452b17 100644 --- a/plugins/channeltx/moddatv/datvmodgui.cpp +++ b/plugins/channeltx/moddatv/datvmodgui.cpp @@ -29,6 +29,7 @@ #include "plugin/pluginapi.h" #include "util/simpleserializer.h" #include "dsp/dspengine.h" +#include "dsp/dspcommands.h" #include "util/db.h" #include "gui/basicchannelsettingsdialog.h" #include "gui/devicestreamselectiondialog.h" @@ -55,6 +56,8 @@ DATVModGUI::DATVModGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandS m_pluginAPI(pluginAPI), m_deviceUISet(deviceUISet), m_channelMarker(this), + m_deviceCenterFrequency(0), + m_basebandSampleRate(1), m_doApplySettings(true), m_tickMsgOutstanding(false), m_streamLength(0), @@ -208,6 +211,17 @@ bool DATVModGUI::handleMessage(const Message& message) ui->tsFileText->setText(m_settings.m_tsFileName); return true; } + else if (DSPSignalNotification::match(message)) + { + const DSPSignalNotification& notif = (const DSPSignalNotification&) message; + m_deviceCenterFrequency = notif.getCenterFrequency(); + m_basebandSampleRate = notif.getSampleRate(); + ui->deltaFrequency->setValueRange(false, 8, -m_basebandSampleRate/2, m_basebandSampleRate/2); + ui->deltaFrequencyLabel->setToolTip(tr("Range %1 %L2 Hz").arg(QChar(0xB1)).arg(m_basebandSampleRate/2)); + updateAbsoluteCenterFrequency(); + return true; + + } else { return false; @@ -238,6 +252,7 @@ void DATVModGUI::on_deltaFrequency_changed(qint64 value) { m_channelMarker.setCenterFrequency(value); m_settings.m_inputFrequencyOffset = value; + updateAbsoluteCenterFrequency(); applySettings(); } @@ -493,7 +508,6 @@ void DATVModGUI::onMenuDialogCalled(const QPoint &p) dialog.move(p); dialog.exec(); - m_settings.m_inputFrequencyOffset = m_channelMarker.getCenterFrequency(); m_settings.m_rgbColor = m_channelMarker.getColor().rgb(); m_settings.m_title = m_channelMarker.getTitle(); m_settings.m_useReverseAPI = dialog.useReverseAPI(); @@ -586,6 +600,7 @@ void DATVModGUI::displaySettings() ui->udpPort->setValue(m_settings.m_udpPort); getRollupContents()->restoreState(m_rollupState); + updateAbsoluteCenterFrequency(); blockApplySettings(false); } @@ -684,3 +699,8 @@ void DATVModGUI::makeUIConnections() QObject::connect(ui->udpAddress, &QLineEdit::editingFinished, this, &DATVModGUI::on_udpAddress_editingFinished); QObject::connect(ui->udpPort, QOverload::of(&QSpinBox::valueChanged), this, &DATVModGUI::on_udpPort_valueChanged); } + +void DATVModGUI::updateAbsoluteCenterFrequency() +{ + setStatusFrequency(m_deviceCenterFrequency + m_settings.m_inputFrequencyOffset); +} diff --git a/plugins/channeltx/moddatv/datvmodgui.h b/plugins/channeltx/moddatv/datvmodgui.h index ea24d775f..f0aba1bb8 100644 --- a/plugins/channeltx/moddatv/datvmodgui.h +++ b/plugins/channeltx/moddatv/datvmodgui.h @@ -67,6 +67,8 @@ private: ChannelMarker m_channelMarker; RollupState m_rollupState; DATVModSettings m_settings; + qint64 m_deviceCenterFrequency; + int m_basebandSampleRate; bool m_doApplySettings; bool m_tickMsgOutstanding; @@ -95,6 +97,7 @@ private: void setChannelMarkerBandwidth(); bool handleMessage(const Message& message); void makeUIConnections(); + void updateAbsoluteCenterFrequency(); void updateFEC(); diff --git a/plugins/channeltx/modfreedv/freedvmod.cpp b/plugins/channeltx/modfreedv/freedvmod.cpp index dd3d4a004..bee8aa146 100644 --- a/plugins/channeltx/modfreedv/freedvmod.cpp +++ b/plugins/channeltx/modfreedv/freedvmod.cpp @@ -196,6 +196,10 @@ bool FreeDVMod::handleMessage(const Message& cmd) DSPSignalNotification* rep = new DSPSignalNotification(notif); // make a copy qDebug() << "FreeDVMod::handleMessage: DSPSignalNotification"; m_basebandSource->getInputMessageQueue()->push(rep); + // Forward to GUI if any + if (getMessageQueueToGUI()) { + getMessageQueueToGUI()->push(new DSPSignalNotification(notif)); + } return true; } diff --git a/plugins/channeltx/modfreedv/freedvmodgui.cpp b/plugins/channeltx/modfreedv/freedvmodgui.cpp index be987aa4b..55fa8dcdf 100644 --- a/plugins/channeltx/modfreedv/freedvmodgui.cpp +++ b/plugins/channeltx/modfreedv/freedvmodgui.cpp @@ -99,6 +99,16 @@ bool FreeDVModGUI::handleMessage(const Message& message) applyBandwidths(5 - ui->spanLog2->value()); // will update spectrum details with new sample rate return true; } + else if (DSPSignalNotification::match(message)) + { + const DSPSignalNotification& notif = (const DSPSignalNotification&) message; + m_deviceCenterFrequency = notif.getCenterFrequency(); + m_basebandSampleRate = notif.getSampleRate(); + ui->deltaFrequency->setValueRange(false, 7, -m_basebandSampleRate/2, m_basebandSampleRate/2); + ui->deltaFrequencyLabel->setToolTip(tr("Range %1 %L2 Hz").arg(QChar(0xB1)).arg(m_basebandSampleRate/2)); + updateAbsoluteCenterFrequency(); + return true; + } else if (FreeDVMod::MsgConfigureFreeDVMod::match(message)) { const FreeDVMod::MsgConfigureFreeDVMod& cfg = (FreeDVMod::MsgConfigureFreeDVMod&) message; @@ -154,6 +164,7 @@ void FreeDVModGUI::on_deltaFrequency_changed(qint64 value) { m_channelMarker.setCenterFrequency(value); m_settings.m_inputFrequencyOffset = m_channelMarker.getCenterFrequency(); + updateAbsoluteCenterFrequency(); applySettings(); } @@ -302,7 +313,6 @@ void FreeDVModGUI::onMenuDialogCalled(const QPoint &p) dialog.move(p); dialog.exec(); - m_settings.m_inputFrequencyOffset = m_channelMarker.getCenterFrequency(); m_settings.m_rgbColor = m_channelMarker.getColor().rgb(); m_settings.m_title = m_channelMarker.getTitle(); m_settings.m_useReverseAPI = dialog.useReverseAPI(); @@ -341,6 +351,8 @@ FreeDVModGUI::FreeDVModGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Baseb m_pluginAPI(pluginAPI), m_deviceUISet(deviceUISet), m_channelMarker(this), + m_deviceCenterFrequency(0), + m_basebandSampleRate(1), m_doApplySettings(true), m_spectrumRate(6000), m_recordLength(0), @@ -506,6 +518,7 @@ void FreeDVModGUI::displaySettings() ui->morseKeyer->setChecked(m_settings.m_modAFInput == FreeDVModSettings::FreeDVModInputAF::FreeDVModInputCWTone); getRollupContents()->restoreState(m_rollupState); + updateAbsoluteCenterFrequency(); blockApplySettings(false); } @@ -617,3 +630,8 @@ void FreeDVModGUI::makeUIConnections() QObject::connect(ui->navTimeSlider, &QSlider::valueChanged, this, &FreeDVModGUI::on_navTimeSlider_valueChanged); QObject::connect(ui->showFileDialog, &QPushButton::clicked, this, &FreeDVModGUI::on_showFileDialog_clicked); } + +void FreeDVModGUI::updateAbsoluteCenterFrequency() +{ + setStatusFrequency(m_deviceCenterFrequency + m_settings.m_inputFrequencyOffset); +} diff --git a/plugins/channeltx/modfreedv/freedvmodgui.h b/plugins/channeltx/modfreedv/freedvmodgui.h index 30494d4c4..ad79495de 100644 --- a/plugins/channeltx/modfreedv/freedvmodgui.h +++ b/plugins/channeltx/modfreedv/freedvmodgui.h @@ -68,6 +68,8 @@ private: ChannelMarker m_channelMarker; RollupState m_rollupState; FreeDVModSettings m_settings; + qint64 m_deviceCenterFrequency; + int m_basebandSampleRate; bool m_doApplySettings; int m_spectrumRate; @@ -98,6 +100,7 @@ private: void channelMarkerUpdate(); bool handleMessage(const Message& message); void makeUIConnections(); + void updateAbsoluteCenterFrequency(); void leaveEvent(QEvent*); void enterEvent(QEvent*); diff --git a/plugins/channeltx/modnfm/nfmmod.cpp b/plugins/channeltx/modnfm/nfmmod.cpp index 70ddfbe57..0f5457afc 100644 --- a/plugins/channeltx/modnfm/nfmmod.cpp +++ b/plugins/channeltx/modnfm/nfmmod.cpp @@ -228,6 +228,10 @@ bool NFMMod::handleMessage(const Message& cmd) DSPSignalNotification* rep = new DSPSignalNotification(notif); // make a copy qDebug() << "NFMMod::handleMessage: DSPSignalNotification"; m_basebandSource->getInputMessageQueue()->push(rep); + // Forward to GUI if any + if (getMessageQueueToGUI()) { + getMessageQueueToGUI()->push(new DSPSignalNotification(notif)); + } return true; } diff --git a/plugins/channeltx/modnfm/nfmmodgui.cpp b/plugins/channeltx/modnfm/nfmmodgui.cpp index 9aacf2b30..2fe399f89 100644 --- a/plugins/channeltx/modnfm/nfmmodgui.cpp +++ b/plugins/channeltx/modnfm/nfmmodgui.cpp @@ -28,6 +28,7 @@ #include "util/db.h" #include "dsp/dspengine.h" #include "dsp/cwkeyer.h" +#include "dsp/dspcommands.h" #include "gui/crightclickenabler.h" #include "gui/audioselectdialog.h" #include "gui/basicchannelsettingsdialog.h" @@ -106,6 +107,16 @@ bool NFMModGUI::handleMessage(const Message& message) ui->cwKeyerGUI->displaySettings(); return true; } + else if (DSPSignalNotification::match(message)) + { + const DSPSignalNotification& notif = (const DSPSignalNotification&) message; + m_deviceCenterFrequency = notif.getCenterFrequency(); + m_basebandSampleRate = notif.getSampleRate(); + ui->deltaFrequency->setValueRange(false, 7, -m_basebandSampleRate/2, m_basebandSampleRate/2); + ui->deltaFrequencyLabel->setToolTip(tr("Range %1 %L2 Hz").arg(QChar(0xB1)).arg(m_basebandSampleRate/2)); + updateAbsoluteCenterFrequency(); + return true; + } else { return false; @@ -136,6 +147,7 @@ void NFMModGUI::on_deltaFrequency_changed(qint64 value) { m_channelMarker.setCenterFrequency(value); m_settings.m_inputFrequencyOffset = m_channelMarker.getCenterFrequency(); + updateAbsoluteCenterFrequency(); applySettings(); } @@ -360,7 +372,6 @@ void NFMModGUI::onMenuDialogCalled(const QPoint &p) dialog.move(p); dialog.exec(); - m_settings.m_inputFrequencyOffset = m_channelMarker.getCenterFrequency(); m_settings.m_rgbColor = m_channelMarker.getColor().rgb(); m_settings.m_title = m_channelMarker.getTitle(); m_settings.m_useReverseAPI = dialog.useReverseAPI(); @@ -399,6 +410,8 @@ NFMModGUI::NFMModGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSam m_pluginAPI(pluginAPI), m_deviceUISet(deviceUISet), m_channelMarker(this), + m_deviceCenterFrequency(0), + m_basebandSampleRate(1), m_doApplySettings(true), m_recordLength(0), m_recordSampleRate(48000), @@ -561,6 +574,7 @@ void NFMModGUI::displaySettings() ui->feedbackVolumeText->setText(QString("%1").arg(m_settings.m_feedbackVolumeFactor, 0, 'f', 2)); getRollupContents()->restoreState(m_rollupState); + updateAbsoluteCenterFrequency(); blockApplySettings(false); } @@ -707,3 +721,8 @@ void NFMModGUI::makeUIConnections() QObject::connect(ui->feedbackEnable, &QToolButton::toggled, this, &NFMModGUI::on_feedbackEnable_toggled); QObject::connect(ui->feedbackVolume, &QDial::valueChanged, this, &NFMModGUI::on_feedbackVolume_valueChanged); } + +void NFMModGUI::updateAbsoluteCenterFrequency() +{ + setStatusFrequency(m_deviceCenterFrequency + m_settings.m_inputFrequencyOffset); +} diff --git a/plugins/channeltx/modnfm/nfmmodgui.h b/plugins/channeltx/modnfm/nfmmodgui.h index 10af4b8c0..2343874b4 100644 --- a/plugins/channeltx/modnfm/nfmmodgui.h +++ b/plugins/channeltx/modnfm/nfmmodgui.h @@ -67,6 +67,8 @@ private: ChannelMarker m_channelMarker; RollupState m_rollupState; NFMModSettings m_settings; + qint64 m_deviceCenterFrequency; + int m_basebandSampleRate; bool m_doApplySettings; NFMMod* m_nfmMod; @@ -95,6 +97,7 @@ private: void updateWithStreamTime(); bool handleMessage(const Message& message); void makeUIConnections(); + void updateAbsoluteCenterFrequency(); void leaveEvent(QEvent*); void enterEvent(QEvent*); diff --git a/plugins/channeltx/modpacket/packetmod.cpp b/plugins/channeltx/modpacket/packetmod.cpp index 73c1e2dc3..c08f28d3c 100644 --- a/plugins/channeltx/modpacket/packetmod.cpp +++ b/plugins/channeltx/modpacket/packetmod.cpp @@ -147,6 +147,10 @@ bool PacketMod::handleMessage(const Message& cmd) DSPSignalNotification* rep = new DSPSignalNotification(notif); // make a copy qDebug() << "PacketMod::handleMessage: DSPSignalNotification"; m_basebandSource->getInputMessageQueue()->push(rep); + // Forward to GUI if any + if (getMessageQueueToGUI()) { + getMessageQueueToGUI()->push(new DSPSignalNotification(notif)); + } return true; } diff --git a/plugins/channeltx/modpacket/packetmodgui.cpp b/plugins/channeltx/modpacket/packetmodgui.cpp index 2c309a80f..a647320c4 100644 --- a/plugins/channeltx/modpacket/packetmodgui.cpp +++ b/plugins/channeltx/modpacket/packetmodgui.cpp @@ -23,11 +23,12 @@ #include #include "dsp/spectrumvis.h" +#include "dsp/dspengine.h" +#include "dsp/dspcommands.h" #include "device/deviceuiset.h" #include "plugin/pluginapi.h" #include "util/simpleserializer.h" #include "util/db.h" -#include "dsp/dspengine.h" #include "gui/glspectrum.h" #include "gui/crightclickenabler.h" #include "gui/basicchannelsettingsdialog.h" @@ -95,6 +96,16 @@ bool PacketModGUI::handleMessage(const Message& message) ui->transmittedText->appendPlainText(str); return true; } + else if (DSPSignalNotification::match(message)) + { + const DSPSignalNotification& notif = (const DSPSignalNotification&) message; + m_deviceCenterFrequency = notif.getCenterFrequency(); + m_basebandSampleRate = notif.getSampleRate(); + ui->deltaFrequency->setValueRange(false, 7, -m_basebandSampleRate/2, m_basebandSampleRate/2); + ui->deltaFrequencyLabel->setToolTip(tr("Range %1 %L2 Hz").arg(QChar(0xB1)).arg(m_basebandSampleRate/2)); + updateAbsoluteCenterFrequency(); + return true; + } else { return false; @@ -125,6 +136,7 @@ void PacketModGUI::on_deltaFrequency_changed(qint64 value) { m_channelMarker.setCenterFrequency(value); m_settings.m_inputFrequencyOffset = m_channelMarker.getCenterFrequency(); + updateAbsoluteCenterFrequency(); applySettings(); } @@ -385,7 +397,6 @@ void PacketModGUI::onMenuDialogCalled(const QPoint &p) dialog.move(p); dialog.exec(); - m_settings.m_inputFrequencyOffset = m_channelMarker.getCenterFrequency(); m_settings.m_rgbColor = m_channelMarker.getColor().rgb(); m_settings.m_title = m_channelMarker.getTitle(); m_settings.m_useReverseAPI = dialog.useReverseAPI(); @@ -424,6 +435,8 @@ PacketModGUI::PacketModGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Baseb m_pluginAPI(pluginAPI), m_deviceUISet(deviceUISet), m_channelMarker(this), + m_deviceCenterFrequency(0), + m_basebandSampleRate(1), m_doApplySettings(true) { ui->setupUi(getRollupContents()); @@ -577,6 +590,7 @@ void PacketModGUI::displaySettings() ui->packet->setText(m_settings.m_data); getRollupContents()->restoreState(m_rollupState); + updateAbsoluteCenterFrequency(); blockApplySettings(false); } @@ -628,3 +642,8 @@ void PacketModGUI::makeUIConnections() QObject::connect(ui->udpAddress, &QLineEdit::editingFinished, this, &PacketModGUI::on_udpAddress_editingFinished); QObject::connect(ui->udpPort, &QLineEdit::editingFinished, this, &PacketModGUI::on_udpPort_editingFinished); } + +void PacketModGUI::updateAbsoluteCenterFrequency() +{ + setStatusFrequency(m_deviceCenterFrequency + m_settings.m_inputFrequencyOffset); +} diff --git a/plugins/channeltx/modpacket/packetmodgui.h b/plugins/channeltx/modpacket/packetmodgui.h index dbc2c1779..4c5fba36d 100644 --- a/plugins/channeltx/modpacket/packetmodgui.h +++ b/plugins/channeltx/modpacket/packetmodgui.h @@ -67,6 +67,8 @@ private: ChannelMarker m_channelMarker; RollupState m_rollupState; PacketModSettings m_settings; + qint64 m_deviceCenterFrequency; + int m_basebandSampleRate; bool m_doApplySettings; SpectrumVis* m_spectrumVis; @@ -85,6 +87,7 @@ private: void displayStreamIndex(); bool handleMessage(const Message& message); void makeUIConnections(); + void updateAbsoluteCenterFrequency(); void leaveEvent(QEvent*); void enterEvent(QEvent*); diff --git a/plugins/channeltx/modssb/ssbmod.cpp b/plugins/channeltx/modssb/ssbmod.cpp index b833c0562..44ad84076 100644 --- a/plugins/channeltx/modssb/ssbmod.cpp +++ b/plugins/channeltx/modssb/ssbmod.cpp @@ -193,6 +193,10 @@ bool SSBMod::handleMessage(const Message& cmd) DSPSignalNotification* rep = new DSPSignalNotification(notif); // make a copy qDebug() << "SSBMod::handleMessage: DSPSignalNotification"; m_basebandSource->getInputMessageQueue()->push(rep); + // Forward to GUI if any + if (getMessageQueueToGUI()) { + getMessageQueueToGUI()->push(new DSPSignalNotification(notif)); + } return true; } diff --git a/plugins/channeltx/modssb/ssbmodgui.cpp b/plugins/channeltx/modssb/ssbmodgui.cpp index eb1dc3cb7..6150d3928 100644 --- a/plugins/channeltx/modssb/ssbmodgui.cpp +++ b/plugins/channeltx/modssb/ssbmodgui.cpp @@ -99,6 +99,16 @@ bool SSBModGUI::handleMessage(const Message& message) applyBandwidths(5 - ui->spanLog2->value()); // will update spectrum details with new sample rate return true; } + else if (DSPSignalNotification::match(message)) + { + const DSPSignalNotification& notif = (const DSPSignalNotification&) message; + m_deviceCenterFrequency = notif.getCenterFrequency(); + m_basebandSampleRate = notif.getSampleRate(); + ui->deltaFrequency->setValueRange(false, 7, -m_basebandSampleRate/2, m_basebandSampleRate/2); + ui->deltaFrequencyLabel->setToolTip(tr("Range %1 %L2 Hz").arg(QChar(0xB1)).arg(m_basebandSampleRate/2)); + updateAbsoluteCenterFrequency(); + return true; + } else if (SSBMod::MsgConfigureSSBMod::match(message)) { SSBModSettings mod_settings; // different USB/LSB convention between modulator and GUI @@ -160,6 +170,7 @@ void SSBModGUI::on_deltaFrequency_changed(qint64 value) { m_channelMarker.setCenterFrequency(value); m_settings.m_inputFrequencyOffset = m_channelMarker.getCenterFrequency(); + updateAbsoluteCenterFrequency(); applySettings(); } @@ -366,7 +377,6 @@ void SSBModGUI::onMenuDialogCalled(const QPoint &p) dialog.move(p); dialog.exec(); - m_settings.m_inputFrequencyOffset = m_channelMarker.getCenterFrequency(); m_settings.m_rgbColor = m_channelMarker.getColor().rgb(); m_settings.m_title = m_channelMarker.getTitle(); m_settings.m_useReverseAPI = dialog.useReverseAPI(); @@ -405,6 +415,8 @@ SSBModGUI::SSBModGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSam m_pluginAPI(pluginAPI), m_deviceUISet(deviceUISet), m_channelMarker(this), + m_deviceCenterFrequency(0), + m_basebandSampleRate(1), m_doApplySettings(true), m_spectrumRate(6000), m_recordLength(0), @@ -720,6 +732,7 @@ void SSBModGUI::displaySettings() ui->feedbackVolumeText->setText(QString("%1").arg(m_settings.m_feedbackVolumeFactor, 0, 'f', 2)); getRollupContents()->restoreState(m_rollupState); + updateAbsoluteCenterFrequency(); blockApplySettings(false); } @@ -866,3 +879,8 @@ void SSBModGUI::makeUIConnections() QObject::connect(ui->feedbackEnable, &QToolButton::toggled, this, &SSBModGUI::on_feedbackEnable_toggled); QObject::connect(ui->feedbackVolume, &QDial::valueChanged, this, &SSBModGUI::on_feedbackVolume_valueChanged); } + +void SSBModGUI::updateAbsoluteCenterFrequency() +{ + setStatusFrequency(m_deviceCenterFrequency + m_settings.m_inputFrequencyOffset); +} diff --git a/plugins/channeltx/modssb/ssbmodgui.h b/plugins/channeltx/modssb/ssbmodgui.h index 8ac6de0bd..74e95c05a 100644 --- a/plugins/channeltx/modssb/ssbmodgui.h +++ b/plugins/channeltx/modssb/ssbmodgui.h @@ -68,6 +68,8 @@ private: ChannelMarker m_channelMarker; RollupState m_rollupState; SSBModSettings m_settings; + qint64 m_deviceCenterFrequency; + int m_basebandSampleRate; bool m_doApplySettings; int m_spectrumRate; @@ -101,6 +103,7 @@ private: void channelMarkerUpdate(); bool handleMessage(const Message& message); void makeUIConnections(); + void updateAbsoluteCenterFrequency(); void leaveEvent(QEvent*); void enterEvent(QEvent*); diff --git a/plugins/channeltx/modwfm/wfmmod.cpp b/plugins/channeltx/modwfm/wfmmod.cpp index 2aceef3e2..ae83b93a3 100644 --- a/plugins/channeltx/modwfm/wfmmod.cpp +++ b/plugins/channeltx/modwfm/wfmmod.cpp @@ -189,6 +189,10 @@ bool WFMMod::handleMessage(const Message& cmd) DSPSignalNotification* rep = new DSPSignalNotification(notif); // make a copy qDebug() << "WFMMod::handleMessage: DSPSignalNotification"; m_basebandSource->getInputMessageQueue()->push(rep); + // Forward to GUI if any + if (getMessageQueueToGUI()) { + getMessageQueueToGUI()->push(new DSPSignalNotification(notif)); + } return true; } diff --git a/plugins/channeltx/modwfm/wfmmodgui.cpp b/plugins/channeltx/modwfm/wfmmodgui.cpp index 0ecca32d1..991aaec65 100644 --- a/plugins/channeltx/modwfm/wfmmodgui.cpp +++ b/plugins/channeltx/modwfm/wfmmodgui.cpp @@ -28,6 +28,7 @@ #include "util/db.h" #include "dsp/dspengine.h" #include "dsp/cwkeyer.h" +#include "dsp/dspcommands.h" #include "gui/crightclickenabler.h" #include "gui/audioselectdialog.h" #include "gui/basicchannelsettingsdialog.h" @@ -105,6 +106,16 @@ bool WFMModGUI::handleMessage(const Message& message) ui->cwKeyerGUI->displaySettings(); return true; } + else if (DSPSignalNotification::match(message)) + { + const DSPSignalNotification& notif = (const DSPSignalNotification&) message; + m_deviceCenterFrequency = notif.getCenterFrequency(); + m_basebandSampleRate = notif.getSampleRate(); + ui->deltaFrequency->setValueRange(false, 8, -m_basebandSampleRate/2, m_basebandSampleRate/2); + ui->deltaFrequencyLabel->setToolTip(tr("Range %1 %L2 Hz").arg(QChar(0xB1)).arg(m_basebandSampleRate/2)); + updateAbsoluteCenterFrequency(); + return true; + } else { return false; @@ -135,6 +146,7 @@ void WFMModGUI::on_deltaFrequency_changed(qint64 value) { m_channelMarker.setCenterFrequency(value); m_settings.m_inputFrequencyOffset = m_channelMarker.getCenterFrequency(); + updateAbsoluteCenterFrequency(); applySettings(); } @@ -295,7 +307,6 @@ void WFMModGUI::onMenuDialogCalled(const QPoint &p) dialog.move(p); dialog.exec(); - m_settings.m_inputFrequencyOffset = m_channelMarker.getCenterFrequency(); m_settings.m_rgbColor = m_channelMarker.getColor().rgb(); m_settings.m_title = m_channelMarker.getTitle(); m_settings.m_useReverseAPI = dialog.useReverseAPI(); @@ -334,6 +345,8 @@ WFMModGUI::WFMModGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSam m_pluginAPI(pluginAPI), m_deviceUISet(deviceUISet), m_channelMarker(this), + m_deviceCenterFrequency(0), + m_basebandSampleRate(1), m_doApplySettings(true), m_recordLength(0), m_recordSampleRate(48000), @@ -480,6 +493,7 @@ void WFMModGUI::displaySettings() ui->feedbackVolumeText->setText(QString("%1").arg(m_settings.m_feedbackVolumeFactor, 0, 'f', 2)); getRollupContents()->restoreState(m_rollupState); + updateAbsoluteCenterFrequency(); blockApplySettings(false); } @@ -620,3 +634,8 @@ void WFMModGUI::makeUIConnections() QObject::connect(ui->feedbackEnable, &QToolButton::toggled, this, &WFMModGUI::on_feedbackEnable_toggled); QObject::connect(ui->feedbackVolume, &QDial::valueChanged, this, &WFMModGUI::on_feedbackVolume_valueChanged); } + +void WFMModGUI::updateAbsoluteCenterFrequency() +{ + setStatusFrequency(m_deviceCenterFrequency + m_settings.m_inputFrequencyOffset); +} diff --git a/plugins/channeltx/modwfm/wfmmodgui.h b/plugins/channeltx/modwfm/wfmmodgui.h index 928173855..3c14d722e 100644 --- a/plugins/channeltx/modwfm/wfmmodgui.h +++ b/plugins/channeltx/modwfm/wfmmodgui.h @@ -65,6 +65,8 @@ private: ChannelMarker m_channelMarker; RollupState m_rollupState; WFMModSettings m_settings; + qint64 m_deviceCenterFrequency; + int m_basebandSampleRate; bool m_doApplySettings; WFMMod* m_wfmMod; @@ -91,6 +93,7 @@ private: void updateWithStreamTime(); bool handleMessage(const Message& message); void makeUIConnections(); + void updateAbsoluteCenterFrequency(); void leaveEvent(QEvent*); void enterEvent(QEvent*); diff --git a/plugins/channeltx/remotesource/remotesource.cpp b/plugins/channeltx/remotesource/remotesource.cpp index 3512b7c35..89af2e0db 100644 --- a/plugins/channeltx/remotesource/remotesource.cpp +++ b/plugins/channeltx/remotesource/remotesource.cpp @@ -42,7 +42,6 @@ MESSAGE_CLASS_DEFINITION(RemoteSource::MsgConfigureRemoteSource, Message) MESSAGE_CLASS_DEFINITION(RemoteSource::MsgQueryStreamData, Message) MESSAGE_CLASS_DEFINITION(RemoteSource::MsgReportStreamData, Message) -MESSAGE_CLASS_DEFINITION(RemoteSource::MsgBasebandSampleRateNotification, Message) const char* const RemoteSource::m_channelIdURI = "sdrangel.channeltx.remotesource"; const char* const RemoteSource::m_channelId ="RemoteSource"; @@ -124,10 +123,8 @@ bool RemoteSource::handleMessage(const Message& cmd) calculateFrequencyOffset(m_settings.m_log2Interp, m_settings.m_filterChainHash); // This is when device sample rate changes m_centerFrequency = notif.getCenterFrequency(); - if (m_guiMessageQueue) - { - MsgBasebandSampleRateNotification *msg = MsgBasebandSampleRateNotification::create(notif.getSampleRate()); - m_guiMessageQueue->push(msg); + if (m_guiMessageQueue) { + m_guiMessageQueue->push(new DSPSignalNotification(notif)); } return true; diff --git a/plugins/channeltx/remotesource/remotesource.h b/plugins/channeltx/remotesource/remotesource.h index b86a7ca21..ec9f27a11 100644 --- a/plugins/channeltx/remotesource/remotesource.h +++ b/plugins/channeltx/remotesource/remotesource.h @@ -153,27 +153,6 @@ public: { } }; - - class MsgBasebandSampleRateNotification : public Message { - MESSAGE_CLASS_DECLARATION - - public: - static MsgBasebandSampleRateNotification* create(int sampleRate) { - return new MsgBasebandSampleRateNotification(sampleRate); - } - - int getBasebandSampleRate() const { return m_sampleRate; } - - private: - - MsgBasebandSampleRateNotification(int sampleRate) : - Message(), - m_sampleRate(sampleRate) - { } - - int m_sampleRate; - }; - RemoteSource(DeviceAPI *deviceAPI); virtual ~RemoteSource(); diff --git a/plugins/channeltx/remotesource/remotesourcegui.cpp b/plugins/channeltx/remotesource/remotesourcegui.cpp index a8e5a0461..aecba396e 100644 --- a/plugins/channeltx/remotesource/remotesourcegui.cpp +++ b/plugins/channeltx/remotesource/remotesourcegui.cpp @@ -20,6 +20,7 @@ #include "device/deviceapi.h" #include "device/deviceuiset.h" #include "dsp/hbfilterchainconverter.h" +#include "dsp/dspcommands.h" #include "gui/basicchannelsettingsdialog.h" #include "gui/devicestreamselectiondialog.h" #include "mainwindow.h" @@ -66,10 +67,12 @@ bool RemoteSourceGUI::deserialize(const QByteArray& data) bool RemoteSourceGUI::handleMessage(const Message& message) { - if (RemoteSource::MsgBasebandSampleRateNotification::match(message)) + if (DSPSignalNotification::match(message)) { - RemoteSource::MsgBasebandSampleRateNotification& notif = (RemoteSource::MsgBasebandSampleRateNotification&) message; - m_basebandSampleRate = notif.getBasebandSampleRate(); + DSPSignalNotification& notif = (DSPSignalNotification&) message; + m_deviceCenterFrequency = notif.getCenterFrequency(); + m_basebandSampleRate = notif.getSampleRate(); + updateAbsoluteCenterFrequency(); displayRateAndShift(); return true; } @@ -156,6 +159,7 @@ RemoteSourceGUI::RemoteSourceGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, ui(new Ui::RemoteSourceGUI), m_pluginAPI(pluginAPI), m_deviceUISet(deviceUISet), + m_deviceCenterFrequency(0), m_remoteSampleRate(48000), m_basebandSampleRate(48000), m_shiftFrequencyFactor(0.0), @@ -245,6 +249,7 @@ void RemoteSourceGUI::displaySettings() ui->dataAddress->setText(m_settings.m_dataAddress); ui->dataPort->setText(tr("%1").arg(m_settings.m_dataPort)); getRollupContents()->restoreState(m_rollupState); + updateAbsoluteCenterFrequency(); blockApplySettings(false); } @@ -432,6 +437,7 @@ void RemoteSourceGUI::applyPosition() m_shiftFrequencyFactor = HBFilterChainConverter::convertToString(m_settings.m_log2Interp, m_settings.m_filterChainHash, s); ui->filterChainText->setText(s); + updateAbsoluteCenterFrequency(); displayRateAndShift(); applySettings(); } @@ -496,3 +502,10 @@ void RemoteSourceGUI::makeUIConnections() QObject::connect(ui->dataApplyButton, &QPushButton::clicked, this, &RemoteSourceGUI::on_dataApplyButton_clicked); QObject::connect(ui->eventCountsReset, &QPushButton::clicked, this, &RemoteSourceGUI::on_eventCountsReset_clicked); } + +void RemoteSourceGUI::updateAbsoluteCenterFrequency() +{ + int shift = m_shiftFrequencyFactor * m_basebandSampleRate; + setStatusFrequency(m_deviceCenterFrequency + shift); +} + diff --git a/plugins/channeltx/remotesource/remotesourcegui.h b/plugins/channeltx/remotesource/remotesourcegui.h index 630204fb7..6a095e0ee 100644 --- a/plugins/channeltx/remotesource/remotesourcegui.h +++ b/plugins/channeltx/remotesource/remotesourcegui.h @@ -66,6 +66,7 @@ private: ChannelMarker m_channelMarker; RollupState m_rollupState; RemoteSourceSettings m_settings; + qint64 m_deviceCenterFrequency; int m_remoteSampleRate; int m_basebandSampleRate; bool m_doApplySettings; @@ -95,6 +96,7 @@ private: void displayStreamIndex(); bool handleMessage(const Message& message); void makeUIConnections(); + void updateAbsoluteCenterFrequency(); void leaveEvent(QEvent*); void enterEvent(QEvent*); diff --git a/plugins/channeltx/udpsource/udpsource.cpp b/plugins/channeltx/udpsource/udpsource.cpp index f455801c4..68f9e9b91 100644 --- a/plugins/channeltx/udpsource/udpsource.cpp +++ b/plugins/channeltx/udpsource/udpsource.cpp @@ -145,6 +145,10 @@ bool UDPSource::handleMessage(const Message& cmd) DSPSignalNotification* rep = new DSPSignalNotification(notif); // make a copy qDebug() << "UDPSource::handleMessage: DSPSignalNotification"; m_basebandSource->getInputMessageQueue()->push(rep); + // Forward to GUI if any + if (getMessageQueueToGUI()) { + getMessageQueueToGUI()->push(new DSPSignalNotification(notif)); + } return true; } diff --git a/plugins/channeltx/udpsource/udpsourcegui.cpp b/plugins/channeltx/udpsource/udpsourcegui.cpp index 500c51e50..8e41b5402 100644 --- a/plugins/channeltx/udpsource/udpsourcegui.cpp +++ b/plugins/channeltx/udpsource/udpsourcegui.cpp @@ -20,6 +20,7 @@ #include "device/deviceuiset.h" #include "dsp/spectrumvis.h" #include "dsp/dspengine.h" +#include "dsp/dspcommands.h" #include "util/simpleserializer.h" #include "util/db.h" #include "gui/basicchannelsettingsdialog.h" @@ -78,6 +79,16 @@ bool UDPSourceGUI::handleMessage(const Message& message) blockApplySettings(false); return true; } + else if (DSPSignalNotification::match(message)) + { + const DSPSignalNotification& notif = (const DSPSignalNotification&) message; + m_deviceCenterFrequency = notif.getCenterFrequency(); + m_basebandSampleRate = notif.getSampleRate(); + ui->deltaFrequency->setValueRange(false, 8, -m_basebandSampleRate/2, m_basebandSampleRate/2); + ui->deltaFrequencyLabel->setToolTip(tr("Range %1 %L2 Hz").arg(QChar(0xB1)).arg(m_basebandSampleRate/2)); + updateAbsoluteCenterFrequency(); + return true; + } else { return false; @@ -104,6 +115,8 @@ UDPSourceGUI::UDPSourceGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Baseb m_deviceUISet(deviceUISet), m_tickCount(0), m_channelMarker(this), + m_deviceCenterFrequency(0), + m_basebandSampleRate(1), m_rfBandwidthChanged(false), m_doApplySettings(true) { @@ -240,6 +253,7 @@ void UDPSourceGUI::displaySettings() ui->applyBtn->setStyleSheet("QPushButton { background:rgb(79,79,79); }"); getRollupContents()->restoreState(m_rollupState); + updateAbsoluteCenterFrequency(); blockApplySettings(false); } @@ -263,6 +277,7 @@ void UDPSourceGUI::on_deltaFrequency_changed(qint64 value) { m_settings.m_inputFrequencyOffset = value; m_channelMarker.setCenterFrequency(value); + updateAbsoluteCenterFrequency(); applySettings(); } @@ -493,7 +508,6 @@ void UDPSourceGUI::onMenuDialogCalled(const QPoint &p) dialog.move(p); dialog.exec(); - m_settings.m_inputFrequencyOffset = m_channelMarker.getCenterFrequency(); m_settings.m_rgbColor = m_channelMarker.getColor().rgb(); m_settings.m_useReverseAPI = dialog.useReverseAPI(); m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); @@ -663,3 +677,8 @@ void UDPSourceGUI::makeUIConnections() QObject::connect(ui->autoRWBalance, &ButtonSwitch::toggled, this, &UDPSourceGUI::on_autoRWBalance_toggled); QObject::connect(ui->stereoInput, &QToolButton::toggled, this, &UDPSourceGUI::on_stereoInput_toggled); } + +void UDPSourceGUI::updateAbsoluteCenterFrequency() +{ + setStatusFrequency(m_deviceCenterFrequency + m_settings.m_inputFrequencyOffset); +} diff --git a/plugins/channeltx/udpsource/udpsourcegui.h b/plugins/channeltx/udpsource/udpsourcegui.h index a009ee03a..f9cfdecf0 100644 --- a/plugins/channeltx/udpsource/udpsourcegui.h +++ b/plugins/channeltx/udpsource/udpsourcegui.h @@ -75,6 +75,8 @@ private: // settings UDPSourceSettings m_settings; + qint64 m_deviceCenterFrequency; + int m_basebandSampleRate; bool m_rfBandwidthChanged; bool m_doApplySettings; MessageQueue m_inputMessageQueue; @@ -90,6 +92,7 @@ private: void setSampleFormatIndex(const UDPSourceSettings::SampleFormat& sampleFormat); bool handleMessage(const Message& message); void makeUIConnections(); + void updateAbsoluteCenterFrequency(); void leaveEvent(QEvent*); void enterEvent(QEvent*); diff --git a/sdrgui/channel/channelgui.cpp b/sdrgui/channel/channelgui.cpp index 78673e2b1..383ec3828 100644 --- a/sdrgui/channel/channelgui.cpp +++ b/sdrgui/channel/channelgui.cpp @@ -90,6 +90,17 @@ ChannelGUI::ChannelGUI(QWidget *parent) : m_closeButton->setIcon(closeIcon); m_closeButton->setToolTip("Close channel"); + m_statusFrequency = new QLabel(); + // QFont font = m_statusFrequency->font(); + // font.setPointSize(8); + // m_statusFrequency->setFont(font); + m_statusFrequency->setAlignment(Qt::AlignRight |Qt::AlignVCenter); + m_statusFrequency->setFixedHeight(20); + m_statusFrequency->setFixedWidth(90); + m_statusFrequency->setSizePolicy(QSizePolicy::Fixed,QSizePolicy::Fixed); + m_statusFrequency->setText(tr("%L1").arg(0)); + m_statusFrequency->setToolTip("Channel absolute frequency (Hz)"); + m_statusLabel = new QLabel(); // m_statusLabel->setText("OK"); // for future use m_statusLabel->setFixedHeight(20); @@ -122,6 +133,7 @@ ChannelGUI::ChannelGUI(QWidget *parent) : m_bottomLayout = new QHBoxLayout(); m_bottomLayout->setContentsMargins(0, 0, 0, 0); + m_bottomLayout->addWidget(m_statusFrequency ); m_bottomLayout->addWidget(m_statusLabel); m_sizeGripBottomRight = new QSizeGrip(this); m_sizeGripBottomRight->setStyleSheet("QSizeGrip { background-color: rgb(128, 128, 128); width: 10px; height: 10px; }"); @@ -162,6 +174,7 @@ ChannelGUI::~ChannelGUI() delete m_topLayout; delete m_layouts; delete m_statusLabel; + delete m_statusFrequency; delete m_closeButton; delete m_hideButton; delete m_shrinkButton; @@ -297,6 +310,16 @@ void ChannelGUI::setDeviceSetIndex(int index) updateIndexLabel(); } +void ChannelGUI::setStatusFrequency(qint64 frequency) +{ + m_statusFrequency->setText(tr("%L1").arg(frequency)); +} + +void ChannelGUI::setStatusText(const QString& text) +{ + m_statusLabel->setText(text); +} + void ChannelGUI::updateIndexLabel() { m_indexLabel->setText(tr("%1%2:%3").arg(getDeviceTypeTag()).arg(m_deviceSetIndex).arg(m_channelIndex)); @@ -304,7 +327,7 @@ void ChannelGUI::updateIndexLabel() bool ChannelGUI::isOnMovingPad() { - return m_indexLabel->underMouse() || m_titleLabel->underMouse() || m_statusLabel->underMouse(); + return m_indexLabel->underMouse() || m_titleLabel->underMouse() || m_statusFrequency->underMouse() || m_statusLabel->underMouse(); } QString ChannelGUI::getDeviceTypeTag() diff --git a/sdrgui/channel/channelgui.h b/sdrgui/channel/channelgui.h index c305b0aa5..326384e64 100644 --- a/sdrgui/channel/channelgui.h +++ b/sdrgui/channel/channelgui.h @@ -79,6 +79,8 @@ public: int getIndex() const { return m_channelIndex; } void setDeviceSetIndex(int index); int getDeviceSetIndex() const { return m_channelIndex; } + void setStatusFrequency(qint64 frequency); + void setStatusText(const QString& text); protected: void closeEvent(QCloseEvent *event); @@ -111,6 +113,7 @@ private: QPushButton *m_shrinkButton; QPushButton *m_hideButton; QPushButton *m_closeButton; + QLabel *m_statusFrequency; QLabel *m_statusLabel; QVBoxLayout *m_layouts; QHBoxLayout *m_topLayout; From c797060a7dc09d17eedfc44122d8bf7b53e1361b Mon Sep 17 00:00:00 2001 From: f4exb Date: Wed, 13 Apr 2022 18:43:37 +0200 Subject: [PATCH 013/115] Massive UI revamping (v7): device set presets --- sdrbase/maincore.h | 1 + sdrbase/settings/mainsettings.h | 1 + sdrgui/CMakeLists.txt | 3 + sdrgui/device/devicegui.cpp | 7 + sdrgui/device/devicegui.h | 2 + sdrgui/device/deviceuiset.h | 1 + sdrgui/gui/devicesetpresetsdialog.cpp | 468 ++++++++++++++++++++++++++ sdrgui/gui/devicesetpresetsdialog.h | 80 +++++ sdrgui/gui/devicesetpresetsdialog.ui | 264 +++++++++++++++ sdrgui/gui/featurepresetsdialog.ui | 358 ++++++++++---------- sdrgui/mainwindow.cpp | 103 +++--- sdrgui/mainwindow.h | 2 +- 12 files changed, 1066 insertions(+), 224 deletions(-) create mode 100644 sdrgui/gui/devicesetpresetsdialog.cpp create mode 100644 sdrgui/gui/devicesetpresetsdialog.h create mode 100644 sdrgui/gui/devicesetpresetsdialog.ui diff --git a/sdrbase/maincore.h b/sdrbase/maincore.h index 0c1222dd0..77a5ab322 100644 --- a/sdrbase/maincore.h +++ b/sdrbase/maincore.h @@ -744,6 +744,7 @@ public: friend class MainWindow; friend class WebAPIAdapter; friend class CommandsDialog; + friend class DeviceSetPresetsDialog; signals: void deviceSetAdded(int index, DeviceAPI *device); diff --git a/sdrbase/settings/mainsettings.h b/sdrbase/settings/mainsettings.h index cf9a7fa85..9c94b0392 100644 --- a/sdrbase/settings/mainsettings.h +++ b/sdrbase/settings/mainsettings.h @@ -46,6 +46,7 @@ public: void clearPresets(); const Preset& getWorkingPresetConst() const { return m_workingPreset; } Preset* getWorkingPreset() { return &m_workingPreset; } + QList *getPresets() { return &m_presets; } void addCommand(Command *command); void deleteCommand(const Command* command); diff --git a/sdrgui/CMakeLists.txt b/sdrgui/CMakeLists.txt index 57a90f961..f42591168 100644 --- a/sdrgui/CMakeLists.txt +++ b/sdrgui/CMakeLists.txt @@ -29,6 +29,7 @@ set(sdrgui_SOURCES gui/cwkeyergui.cpp gui/datetimedelegate.cpp gui/decimaldelegate.cpp + gui/devicesetpresetsdialog.cpp gui/devicestreamselectiondialog.cpp gui/deviceuserargsdialog.cpp gui/dmsspinbox.cpp @@ -130,6 +131,7 @@ set(sdrgui_HEADERS gui/cwkeyergui.h gui/datetimedelegate.h gui/decimaldelegate.h + gui/devicesetpresetsdialog.h gui/devicestreamselectiondialog.h gui/deviceuserargsdialog.h gui/dmsspinbox.h @@ -220,6 +222,7 @@ set(sdrgui_FORMS gui/commandoutputdialog.ui gui/configurationsdialog.ui gui/cwkeyergui.ui + gui/devicesetpresetsdialog.ui gui/devicestreamselectiondialog.ui gui/deviceuserargsdialog.ui gui/editcommanddialog.ui diff --git a/sdrgui/device/devicegui.cpp b/sdrgui/device/devicegui.cpp index 6778c168c..c4c68a84e 100644 --- a/sdrgui/device/devicegui.cpp +++ b/sdrgui/device/devicegui.cpp @@ -169,6 +169,7 @@ DeviceGUI::DeviceGUI(QWidget *parent) : connect(m_changeDeviceButton, SIGNAL(clicked()), this, SLOT(openChangeDeviceDialog())); connect(m_reloadDeviceButton, SIGNAL(clicked()), this, SLOT(deviceReload())); connect(m_addChannelsButton, SIGNAL(clicked()), this, SLOT(openAddChannelsDialog())); + connect(m_deviceSetPresetsButton, SIGNAL(clicked()), this, SLOT(deviceSetPresetsDialog())); connect(m_helpButton, SIGNAL(clicked()), this, SLOT(showHelp())); connect(m_moveButton, SIGNAL(clicked()), this, SLOT(openMoveToWorkspaceDialog())); connect(m_shrinkButton, SIGNAL(clicked()), this, SLOT(shrinkWindow())); @@ -305,6 +306,12 @@ void DeviceGUI::shrinkWindow() adjustSize(); } +void DeviceGUI::deviceSetPresetsDialog() +{ + QPoint p = mapFromGlobal(QCursor::pos()); + emit deviceSetPresetsDialogRequested(p, this); +} + void DeviceGUI::setTitle(const QString& title) { m_titleLabel->setText(title); diff --git a/sdrgui/device/devicegui.h b/sdrgui/device/devicegui.h index 61c36f8d9..f8e99e894 100644 --- a/sdrgui/device/devicegui.h +++ b/sdrgui/device/devicegui.h @@ -131,6 +131,7 @@ private slots: void openMoveToWorkspaceDialog(); void showSpectrumHandler(); void showAllChannelsHandler(); + void deviceSetPresetsDialog(); signals: void forceClose(); @@ -142,6 +143,7 @@ signals: void showSpectrum(int deviceSetIndex); void showAllChannels(int deviceSetIndex); void addChannelEmitted(int channelIndex); + void deviceSetPresetsDialogRequested(QPoint, DeviceGUI*); }; #endif // INCLUDE_DEVICEGUI_H diff --git a/sdrgui/device/deviceuiset.h b/sdrgui/device/deviceuiset.h index 3daf59524..f0e940fda 100644 --- a/sdrgui/device/deviceuiset.h +++ b/sdrgui/device/deviceuiset.h @@ -72,6 +72,7 @@ public: ~DeviceUISet(); void setIndex(int deviceSetIndex); + int getIndex() const { return m_deviceSetIndex; } GLSpectrum *getSpectrum() { return m_spectrum; } //!< Direct spectrum getter void setSpectrumScalingFactor(float scalef); void addChannelMarker(ChannelMarker* channelMarker); //!< Add channel marker to spectrum diff --git a/sdrgui/gui/devicesetpresetsdialog.cpp b/sdrgui/gui/devicesetpresetsdialog.cpp new file mode 100644 index 000000000..3bbd2077d --- /dev/null +++ b/sdrgui/gui/devicesetpresetsdialog.cpp @@ -0,0 +1,468 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2022 F4EXB // +// written by Edouard Griffiths // +// // +// This program 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 as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program 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 V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#include +#include + +#include "settings/preset.h" +#include "gui/presetitem.h" +#include "gui/addpresetdialog.h" +#include "device/deviceuiset.h" +#include "maincore.h" + +#include "devicesetpresetsdialog.h" +#include "ui_devicesetpresetsdialog.h" + +DeviceSetPresetsDialog::DeviceSetPresetsDialog(QWidget* parent) : + QDialog(parent), + ui(new Ui::DeviceSetPresetsDialog), + m_deviceSetPresets(nullptr), + m_deviceUISet(nullptr), + m_pluginAPI(nullptr), + m_currentWorkspace(nullptr), + m_workspaces(nullptr), + m_presetLoaded(false) +{ + ui->setupUi(this); +} + +DeviceSetPresetsDialog::~DeviceSetPresetsDialog() +{ + delete ui; +} + +void DeviceSetPresetsDialog::populateTree(int deviceType) +{ + if (!m_deviceSetPresets) { + return; + } + + QList::const_iterator it = m_deviceSetPresets->begin(); + QList treeItems; + ui->presetTree->clear(); + + for (int i = 0; it != m_deviceSetPresets->end(); ++it, i++) + { + if (((*it)->isSourcePreset() && (deviceType == 0)) || + ((*it)->isSinkPreset() && (deviceType == 1)) || + ((*it)->isMIMOPreset() && (deviceType == 2))) + { + QTreeWidgetItem *treeItem = addPresetToTree(*it); + treeItems.push_back(treeItem); + } + } + + if (treeItems.size() > 0) { + ui->presetTree->setCurrentItem(treeItems.at(treeItems.size()/2)); + } + + updatePresetControls(); +} + +QTreeWidgetItem* DeviceSetPresetsDialog::addPresetToTree(const Preset* preset) +{ + QTreeWidgetItem* group = 0; + + for(int i = 0; i < ui->presetTree->topLevelItemCount(); i++) + { + if(ui->presetTree->topLevelItem(i)->text(0) == preset->getGroup()) + { + group = ui->presetTree->topLevelItem(i); + break; + } + } + + if(group == 0) + { + QStringList sl; + sl.append(preset->getGroup()); + group = new QTreeWidgetItem(ui->presetTree, sl, PGroup); + group->setFirstColumnSpanned(true); + group->setExpanded(true); + ui->presetTree->sortByColumn(0, Qt::AscendingOrder); + } + + QStringList sl; + sl.append(QString("%1").arg(preset->getCenterFrequency() / 1e6f, 0, 'f', 3)); // frequency column + sl.append(QString("%1").arg(preset->isSourcePreset() ? 'R' : preset->isSinkPreset() ? 'T' : preset->isMIMOPreset() ? 'M' : 'X')); // mode column + sl.append(preset->getDescription()); // description column + PresetItem* item = new PresetItem(group, sl, preset->getCenterFrequency(), PItem); + item->setTextAlignment(0, Qt::AlignRight); + item->setData(0, Qt::UserRole, QVariant::fromValue(preset)); + ui->presetTree->resizeColumnToContents(0); // Resize frequency column to minimum + ui->presetTree->resizeColumnToContents(1); // Resize mode column to minimum + + updatePresetControls(); + return item; +} + +void DeviceSetPresetsDialog::updatePresetControls() +{ + ui->presetTree->resizeColumnToContents(0); + + if (ui->presetTree->currentItem() != 0) + { + ui->presetDelete->setEnabled(true); + ui->presetLoad->setEnabled(true); + } + else + { + ui->presetDelete->setEnabled(false); + ui->presetLoad->setEnabled(false); + } +} + +void DeviceSetPresetsDialog::on_presetSave_clicked() +{ + QStringList groups; + QString group; + QString description = ""; + + for(int i = 0; i < ui->presetTree->topLevelItemCount(); i++) { + groups.append(ui->presetTree->topLevelItem(i)->text(0)); + } + + QTreeWidgetItem* item = ui->presetTree->currentItem(); + + if (item) + { + if (item->type() == PGroup) + { + group = item->text(0); + } + else if (item->type() == PItem) + { + group = item->parent()->text(0); + description = item->text(0); + } + } + + AddPresetDialog dlg(groups, group, this); + + if (description.length() > 0) { + dlg.setDescription(description); + } + + if (dlg.exec() == QDialog::Accepted) + { + Preset* preset = MainCore::instance()->m_settings.newPreset(dlg.group(), dlg.description()); + m_deviceUISet->saveDeviceSetSettings(preset); + ui->presetTree->setCurrentItem(addPresetToTree(preset)); + } + + MainCore::instance()->m_settings.sortPresets(); +} + +void DeviceSetPresetsDialog::on_presetUpdate_clicked() +{ + QTreeWidgetItem* item = ui->presetTree->currentItem(); + const Preset* changedPreset = 0; + + if(item != 0) + { + if(item->type() == PItem) + { + const Preset* preset = qvariant_cast(item->data(0, Qt::UserRole)); + + if (preset != 0) + { + Preset* preset_mod = const_cast(preset); + m_deviceUISet->saveDeviceSetSettings(preset_mod); + changedPreset = preset; + } + } + } + + MainCore::instance()->m_settings.sortPresets(); + ui->presetTree->clear(); + + for (int i = 0; i < MainCore::instance()->m_settings.getPresetCount(); ++i) + { + QTreeWidgetItem *item_x = addPresetToTree(MainCore::instance()->m_settings.getPreset(i)); + const Preset* preset_x = qvariant_cast(item_x->data(0, Qt::UserRole)); + + if (changedPreset && (preset_x == changedPreset)) { // set cursor on changed preset + ui->presetTree->setCurrentItem(item_x); + } + } +} + +void DeviceSetPresetsDialog::on_presetEdit_clicked() +{ + QTreeWidgetItem* item = ui->presetTree->currentItem(); + QStringList groups; + bool change = false; + const Preset *changedPreset = 0; + QString newGroupName; + + for(int i = 0; i < ui->presetTree->topLevelItemCount(); i++) { + groups.append(ui->presetTree->topLevelItem(i)->text(0)); + } + + if (item) + { + if (item->type() == PItem) + { + const Preset* preset = qvariant_cast(item->data(0, Qt::UserRole)); + AddPresetDialog dlg(groups, preset->getGroup(), this); + dlg.setDescription(preset->getDescription()); + + if (dlg.exec() == QDialog::Accepted) + { + Preset* preset_mod = const_cast(preset); + preset_mod->setGroup(dlg.group()); + preset_mod->setDescription(dlg.description()); + change = true; + changedPreset = preset; + } + } + else if (item->type() == PGroup) + { + AddPresetDialog dlg(groups, item->text(0), this); + dlg.showGroupOnly(); + dlg.setDialogTitle("Edit preset group"); + + if (dlg.exec() == QDialog::Accepted) + { + MainCore::instance()->m_settings.renamePresetGroup(item->text(0), dlg.group()); + newGroupName = dlg.group(); + change = true; + } + } + } + + if (change) + { + MainCore::instance()->m_settings.sortPresets(); + ui->presetTree->clear(); + + for (int i = 0; i < MainCore::instance()->m_settings.getPresetCount(); ++i) + { + QTreeWidgetItem *item_x = addPresetToTree(MainCore::instance()->m_settings.getPreset(i)); + const Preset* preset_x = qvariant_cast(item_x->data(0, Qt::UserRole)); + + if (changedPreset && (preset_x == changedPreset)) { // set cursor on changed preset + ui->presetTree->setCurrentItem(item_x); + } + } + + if (!changedPreset) // on group name change set cursor on the group that has been changed + { + for(int i = 0; i < ui->presetTree->topLevelItemCount(); i++) + { + QTreeWidgetItem* item = ui->presetTree->topLevelItem(i); + + if (item->text(0) == newGroupName) { + ui->presetTree->setCurrentItem(item); + } + } + } + } +} + +void DeviceSetPresetsDialog::on_presetExport_clicked() +{ + QTreeWidgetItem* item = ui->presetTree->currentItem(); + + if (item) + { + if (item->type() == PItem) + { + const Preset* preset = qvariant_cast(item->data(0, Qt::UserRole)); + QString base64Str = preset->serialize().toBase64(); + QString fileName = QFileDialog::getSaveFileName( + this, + tr("Open preset export file"), + ".", + tr("Preset export files (*.prex)"), + 0, + QFileDialog::DontUseNativeDialog + ); + + if (fileName != "") + { + QFileInfo fileInfo(fileName); + + if (fileInfo.suffix() != "prex") { + fileName += ".prex"; + } + + QFile exportFile(fileName); + + if (exportFile.open(QIODevice::WriteOnly | QIODevice::Text)) + { + QTextStream outstream(&exportFile); + outstream << base64Str; + exportFile.close(); + } + else + { + QMessageBox::information(this, tr("Message"), tr("Cannot open file for writing")); + } + } + } + } +} + +void DeviceSetPresetsDialog::on_presetImport_clicked() +{ + QTreeWidgetItem* item = ui->presetTree->currentItem(); + + if (item) + { + QString group; + + if (item->type() == PGroup) { + group = item->text(0); + } else if (item->type() == PItem) { + group = item->parent()->text(0); + } else { + return; + } + + QString fileName = QFileDialog::getOpenFileName( + this, + tr("Open preset export file"), + ".", + tr("Preset export files (*.prex)"), + 0, + QFileDialog::DontUseNativeDialog + ); + + if (fileName != "") + { + QFile exportFile(fileName); + + if (exportFile.open(QIODevice::ReadOnly | QIODevice::Text)) + { + QByteArray base64Str; + QTextStream instream(&exportFile); + instream >> base64Str; + exportFile.close(); + + Preset* preset = MainCore::instance()->m_settings.newPreset("", ""); + preset->deserialize(QByteArray::fromBase64(base64Str)); + preset->setGroup(group); // override with current group + + ui->presetTree->setCurrentItem(addPresetToTree(preset)); + } + else + { + QMessageBox::information(this, tr("Message"), tr("Cannot open file for reading")); + } + } + } +} + +void DeviceSetPresetsDialog::on_presetLoad_clicked() +{ + qDebug() << "DeviceSetPresetsDialog::on_presetLoad_clicked"; + + QTreeWidgetItem* item = ui->presetTree->currentItem(); + + if (!item) + { + qDebug("DeviceSetPresetsDialog::on_presetLoad_clicked: item null"); + updatePresetControls(); + return; + } + + const Preset* preset = qvariant_cast(item->data(0, Qt::UserRole)); + + if (!preset) + { + qDebug("DeviceSetPresetsDialog::on_presetLoad_clicked: preset null"); + return; + } + + loadDeviceSetPresetSettings(preset); +} + +void DeviceSetPresetsDialog::on_presetDelete_clicked() +{ + QTreeWidgetItem* item = ui->presetTree->currentItem(); + + if (!item) + { + updatePresetControls(); + return; + } + else + { + if (item->type() == PItem) + { + const Preset* preset = qvariant_cast(item->data(0, Qt::UserRole)); + + if (preset) + { + if (QMessageBox::question( + this, + tr("Delete Preset"), + tr("Do you want to delete preset '%1'?").arg(preset->getDescription()), + QMessageBox::No | QMessageBox::Yes, QMessageBox::No) == QMessageBox::Yes + ) + { + delete item; + MainCore::instance()->m_settings.deletePreset(preset); + } + } + } + else if (item->type() == PGroup) + { + if (QMessageBox::question( + this, + tr("Delete preset group"), + tr("Do you want to delete preset group '%1'?").arg(item->text(0)), + QMessageBox::No | QMessageBox::Yes, QMessageBox::No) == QMessageBox::Yes + ) + { + MainCore::instance()->m_settings.deletePresetGroup(item->text(0)); + + ui->presetTree->clear(); + + for (int i = 0; i < MainCore::instance()->m_settings.getPresetCount(); ++i) { + addPresetToTree(MainCore::instance()->m_settings.getPreset(i)); + } + } + } + } +} + +void DeviceSetPresetsDialog::on_presetTree_currentItemChanged(QTreeWidgetItem *current, QTreeWidgetItem *previous) +{ + (void) current; + (void) previous; + updatePresetControls(); +} + +void DeviceSetPresetsDialog::on_presetTree_itemActivated(QTreeWidgetItem *item, int column) +{ + (void) item; + (void) column; + on_presetLoad_clicked(); +} + +void DeviceSetPresetsDialog::loadDeviceSetPresetSettings(const Preset* preset) +{ + qDebug("DeviceSetPresetsDialog::loadPresetSettings: preset [%s | %s]", + qPrintable(preset->getGroup()), + qPrintable(preset->getDescription())); + + m_deviceUISet->loadDeviceSetSettings(preset, m_pluginAPI, m_workspaces, m_currentWorkspace); + m_presetLoaded = true; +} diff --git a/sdrgui/gui/devicesetpresetsdialog.h b/sdrgui/gui/devicesetpresetsdialog.h new file mode 100644 index 000000000..c61ff1ba8 --- /dev/null +++ b/sdrgui/gui/devicesetpresetsdialog.h @@ -0,0 +1,80 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2022 F4EXB // +// written by Edouard Griffiths // +// // +// This program 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 as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program 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 V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef SDRGUI_GUI_DEVICESETPRESETDIALOG_H_ +#define SDRGUI_GUI_DEVICESETPRESETDIALOG_H_ + +#include +#include + +#include "export.h" + +class Preset; +class DeviceUISet; +class PluginAPI; +class Workspace; + +namespace Ui { + class DeviceSetPresetsDialog; +} + +class SDRGUI_API DeviceSetPresetsDialog : public QDialog { + Q_OBJECT +public: + explicit DeviceSetPresetsDialog(QWidget* parent = nullptr); + ~DeviceSetPresetsDialog(); + + void setPresets(QList* presets) { m_deviceSetPresets = presets; } + void setDeviceUISet(DeviceUISet *deviceUISet) { m_deviceUISet = deviceUISet; } + void setPluginAPI(PluginAPI *pluginAPI) { m_pluginAPI = pluginAPI; } + void setCurrentWorkspace(Workspace *workspace) { m_currentWorkspace = workspace; } + void setWorkspaces(QList *workspaces) { m_workspaces = workspaces; } + void populateTree(int deviceType); + bool wasPresetLoaded() const { return m_presetLoaded; } + +private: + enum { + PGroup, + PItem + }; + + Ui::DeviceSetPresetsDialog* ui; + QList *m_deviceSetPresets; + DeviceUISet *m_deviceUISet; + PluginAPI *m_pluginAPI; + Workspace *m_currentWorkspace; + QList *m_workspaces; + bool m_presetLoaded; + + QTreeWidgetItem* addPresetToTree(const Preset* preset); + void updatePresetControls(); + void loadDeviceSetPresetSettings(const Preset* preset); + +private slots: + void on_presetSave_clicked(); + void on_presetUpdate_clicked(); + void on_presetEdit_clicked(); + void on_presetExport_clicked(); + void on_presetImport_clicked(); + void on_presetLoad_clicked(); + void on_presetDelete_clicked(); + void on_presetTree_currentItemChanged(QTreeWidgetItem *current, QTreeWidgetItem *previous); + void on_presetTree_itemActivated(QTreeWidgetItem *item, int column); +}; + +#endif // SDRGUI_GUI_DEVICESETPRESETDIALOG_H_ diff --git a/sdrgui/gui/devicesetpresetsdialog.ui b/sdrgui/gui/devicesetpresetsdialog.ui new file mode 100644 index 000000000..4a28cea13 --- /dev/null +++ b/sdrgui/gui/devicesetpresetsdialog.ui @@ -0,0 +1,264 @@ + + + DeviceSetPresetsDialog + + + + 0 + 0 + 396 + 396 + + + + + Liberation Sans + 9 + + + + Device Set Presets + + + + + + 10 + + + true + + + 5 + + + + Freq (MHz) + + + Center frequency in MHz + + + + + M + + + Mode: R: Rx or source, T: Tx or sink + + + + + Description + + + + + + + + + + Save current settings as new preset + + + ... + + + + :/create.png:/create.png + + + + 16 + 16 + + + + + + + + Update selected preset with current settings + + + ... + + + + :/recycle.png:/recycle.png + + + + 16 + 16 + + + + + + + + Edit preset details + + + + + + + :/edit.png:/edit.png + + + + + + + Export current preset to file + + + + + + + :/export.png:/export.png + + + + + + + Import preset from file into current group + + + + + + + :/import.png:/import.png + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Delete selected preset + + + ... + + + + :/bin.png:/bin.png + + + + 16 + 16 + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + Load selected preset + + + ... + + + + :/load.png:/load.png + + + + 16 + 16 + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Close + + + + + + + + + + + buttonBox + accepted() + DeviceSetPresetsDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + DeviceSetPresetsDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/sdrgui/gui/featurepresetsdialog.ui b/sdrgui/gui/featurepresetsdialog.ui index 1ee9c22f2..12d5c8f42 100644 --- a/sdrgui/gui/featurepresetsdialog.ui +++ b/sdrgui/gui/featurepresetsdialog.ui @@ -6,10 +6,16 @@ 0 0 - 392 - 414 + 376 + 399 + + + 0 + 0 + + Liberation Sans @@ -19,187 +25,175 @@ Feature presets - - - - 40 - 380 - 341 - 32 - - - - Qt::Horizontal - - - QDialogButtonBox::Close - - - - - - 0 - 10 - 392 - 310 - - - - - - - 5 - - - true - - - 1 - - - 5 - - - - Description + + + + + + 0 + 0 + + + + + + + 5 + + + true + + + 1 + + + 5 + + + + Description + + + + + + + + + + + + + Save current settings as new preset - - - - - - - - - 0 - 330 - 392 - 34 - - - - - - - Save current settings as new preset - - - ... - - - - :/create.png:/create.png - - - - 16 - 16 - - - - - - - - Update selected preset with current settings - - - ... - - - - :/recycle.png:/recycle.png - - - - 16 - 16 - - - - - - - - Edit preset details - - - - - - - :/edit.png:/edit.png - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Delete selected preset - - - ... - - - - :/bin.png:/bin.png - - - - 16 - 16 - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Load selected preset - - - ... - - - - :/load.png:/load.png - - - - 16 - 16 - - - - - - + + ... + + + + :/create.png:/create.png + + + + 16 + 16 + + + + + + + + Update selected preset with current settings + + + ... + + + + :/recycle.png:/recycle.png + + + + 16 + 16 + + + + + + + + Edit preset details + + + + + + + :/edit.png:/edit.png + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Delete selected preset + + + ... + + + + :/bin.png:/bin.png + + + + 16 + 16 + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Load selected preset + + + ... + + + + :/load.png:/load.png + + + + 16 + 16 + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Close + + + + diff --git a/sdrgui/mainwindow.cpp b/sdrgui/mainwindow.cpp index 5570df766..467590ca7 100644 --- a/sdrgui/mainwindow.cpp +++ b/sdrgui/mainwindow.cpp @@ -66,6 +66,7 @@ #include "gui/ambedevicesdialog.h" #include "gui/workspace.h" #include "gui/featurepresetsdialog.h" +#include "gui/devicesetpresetsdialog.h" #include "gui/commandsdialog.h" #include "gui/configurationsdialog.h" #include "dsp/dspengine.h" @@ -542,6 +543,12 @@ void MainWindow::sampleSourceCreate( this, [=](){ this->removeDeviceSet(deviceGUI->getIndex()); } ); + QObject::connect( + deviceGUI, + &DeviceGUI::deviceSetPresetsDialogRequested, + this, + &MainWindow::openDeviceSetPresetsDialog + ); deviceAPI->getSampleSource()->setMessageQueueToGUI(deviceGUI->getInputMessageQueue()); deviceUISet->m_deviceGUI = deviceGUI; @@ -753,6 +760,13 @@ void MainWindow::sampleSinkCreate( this, [=](){ this->removeDeviceSet(deviceGUI->getIndex()); } ); + QObject::connect( + deviceGUI, + &DeviceGUI::deviceSetPresetsDialogRequested, + this, + &MainWindow::openDeviceSetPresetsDialog + ); + deviceAPI->getSampleSink()->setMessageQueueToGUI(deviceGUI->getInputMessageQueue()); deviceUISet->m_deviceGUI = deviceGUI; const PluginInterface::SamplingDevice *selectedDevice = DeviceEnumerator::instance()->getRxSamplingDevice(selectedDeviceIndex); @@ -937,6 +951,13 @@ void MainWindow::sampleMIMOCreate( this, [=](){ this->removeDeviceSet(deviceGUI->getIndex()); } ); + QObject::connect( + deviceGUI, + &DeviceGUI::deviceSetPresetsDialogRequested, + this, + &MainWindow::openDeviceSetPresetsDialog + ); + deviceAPI->getSampleMIMO()->setMessageQueueToGUI(deviceGUI->getInputMessageQueue()); deviceUISet->m_deviceGUI = deviceGUI; const PluginInterface::SamplingDevice *selectedDevice = DeviceEnumerator::instance()->getRxSamplingDevice(selectedDeviceIndex); @@ -1591,44 +1612,6 @@ void MainWindow::updatePresetControls() // } } -QTreeWidgetItem* MainWindow::addPresetToTree(const Preset* preset) -{ - // QTreeWidgetItem* group = 0; - - // for(int i = 0; i < ui->presetTree->topLevelItemCount(); i++) - // { - // if(ui->presetTree->topLevelItem(i)->text(0) == preset->getGroup()) - // { - // group = ui->presetTree->topLevelItem(i); - // break; - // } - // } - - // if(group == 0) - // { - // QStringList sl; - // sl.append(preset->getGroup()); - // group = new QTreeWidgetItem(ui->presetTree, sl, PGroup); - // group->setFirstColumnSpanned(true); - // group->setExpanded(true); - // ui->presetTree->sortByColumn(0, Qt::AscendingOrder); - // } - - // QStringList sl; - // sl.append(QString("%1").arg(preset->getCenterFrequency() / 1e6f, 0, 'f', 3)); // frequency column - // sl.append(QString("%1").arg(preset->isSourcePreset() ? 'R' : preset->isSinkPreset() ? 'T' : preset->isMIMOPreset() ? 'M' : 'X')); // mode column - // sl.append(preset->getDescription()); // description column - // PresetItem* item = new PresetItem(group, sl, preset->getCenterFrequency(), PItem); - // item->setTextAlignment(0, Qt::AlignRight); - // item->setData(0, Qt::UserRole, QVariant::fromValue(preset)); - // ui->presetTree->resizeColumnToContents(0); // Resize frequency column to minimum - // ui->presetTree->resizeColumnToContents(1); // Resize mode column to minimum - - // updatePresetControls(); - // return item; - return nullptr; -} - void MainWindow::applySettings() { // loadDeviceSetPresetSettings(m_mainCore->m_settings.getWorkingPreset(), 0); @@ -1664,7 +1647,15 @@ bool MainWindow::handleMessage(const Message& cmd) if (MainCore::MsgLoadPreset::match(cmd)) { MainCore::MsgLoadPreset& notif = (MainCore::MsgLoadPreset&) cmd; - loadDeviceSetPresetSettings(notif.getPreset(), notif.getDeviceSetIndex()); + int deviceSetIndex = notif.getDeviceSetIndex(); + const Preset *preset = notif.getPreset(); + + if (deviceSetIndex < (int) m_deviceUIs.size()) + { + DeviceUISet *deviceUISet = m_deviceUIs[deviceSetIndex]; + deviceUISet->loadDeviceSetSettings(preset, m_pluginManager->getPluginAPI(), &m_workspaces, nullptr); + } + return true; } else if (MainCore::MsgSavePreset::match(cmd)) @@ -2303,14 +2294,14 @@ void MainWindow::on_presetTree_currentItemChanged(QTreeWidgetItem *current, QTre { (void) current; (void) previous; - updatePresetControls(); + // updatePresetControls(); } void MainWindow::on_presetTree_itemActivated(QTreeWidgetItem *item, int column) { (void) item; (void) column; - on_presetLoad_clicked(); + // on_presetLoad_clicked(); } void MainWindow::on_action_Quick_Start_triggered() @@ -2787,6 +2778,36 @@ void MainWindow::openFeaturePresetsDialog(QPoint p, Workspace *workspace) } } +void MainWindow::openDeviceSetPresetsDialog(QPoint p, DeviceGUI *deviceGUI) +{ + Workspace *workspace = m_workspaces[deviceGUI->getWorkspaceIndex()]; + DeviceUISet *deviceUISet = m_deviceUIs[deviceGUI->getIndex()]; + + DeviceSetPresetsDialog dialog; + dialog.setDeviceUISet(deviceUISet); + dialog.setPresets(m_mainCore->m_settings.getPresets()); + dialog.setPluginAPI(m_pluginManager->getPluginAPI()); + dialog.setCurrentWorkspace(workspace); + dialog.setWorkspaces(&m_workspaces); + dialog.populateTree((int) deviceGUI->getDeviceType()); + dialog.move(p); + dialog.exec(); + + // if (dialog.wasPresetLoaded()) + // { + // for (int i = 0; i < m_featureUIs[0]->getNumberOfFeatures(); i++) + // { + // FeatureGUI *gui = m_featureUIs[0]->getFeatureGuiAt(i); + // QObject::connect( + // gui, + // &FeatureGUI::moveToWorkspace, + // this, + // [=](int wsIndexDest){ this->featureMove(gui, wsIndexDest); } + // ); + // } + // } +} + void MainWindow::deleteFeature(int featureSetIndex, int featureIndex) { if ((featureSetIndex >= 0) && (featureSetIndex < (int) m_featureUIs.size())) diff --git a/sdrgui/mainwindow.h b/sdrgui/mainwindow.h index 174d5f366..3cff9ce7c 100644 --- a/sdrgui/mainwindow.h +++ b/sdrgui/mainwindow.h @@ -138,7 +138,6 @@ private: void createStatusBar(); void closeEvent(QCloseEvent*); void updatePresetControls(); - QTreeWidgetItem* addPresetToTree(const Preset* preset); void applySettings(); void removeDeviceSet(int deviceSetIndex); @@ -215,6 +214,7 @@ private slots: void mainSpectrumMove(MainSpectrumGUI *gui, int wsIndexDestnation); void mainSpectrumShow(MainSpectrumGUI *gui); void showAllChannels(int deviceSetIndex); + void openDeviceSetPresetsDialog(QPoint p, DeviceGUI *deviceGUI); void on_action_Quick_Start_triggered(); void on_action_Main_Window_triggered(); void on_action_Loaded_Plugins_triggered(); From 459cf2bd527c9f922e0d2e851294e6457035c9eb Mon Sep 17 00:00:00 2001 From: f4exb Date: Wed, 13 Apr 2022 18:52:21 +0200 Subject: [PATCH 014/115] MSVC build: build for v7 branch also --- .github/workflows/sdrangel.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/sdrangel.yml b/.github/workflows/sdrangel.yml index ac183aa87..28b653fe9 100644 --- a/.github/workflows/sdrangel.yml +++ b/.github/workflows/sdrangel.yml @@ -6,6 +6,7 @@ on: push: branches: - master + - v7 tags: - 'v*' pull_request: From 426bf4d45cc0144b224d5208b63de27450ca6fdd Mon Sep 17 00:00:00 2001 From: f4exb Date: Wed, 13 Apr 2022 21:45:56 +0200 Subject: [PATCH 015/115] Massive UI revamping (v7): try to fix Ubuntu prod build error --- sdrgui/gui/devicesetpresetsdialog.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sdrgui/gui/devicesetpresetsdialog.cpp b/sdrgui/gui/devicesetpresetsdialog.cpp index 3bbd2077d..7e066519a 100644 --- a/sdrgui/gui/devicesetpresetsdialog.cpp +++ b/sdrgui/gui/devicesetpresetsdialog.cpp @@ -18,6 +18,8 @@ #include #include +#include +#include #include "settings/preset.h" #include "gui/presetitem.h" @@ -371,7 +373,7 @@ void DeviceSetPresetsDialog::on_presetImport_clicked() void DeviceSetPresetsDialog::on_presetLoad_clicked() { - qDebug() << "DeviceSetPresetsDialog::on_presetLoad_clicked"; + qDebug("DeviceSetPresetsDialog::on_presetLoad_clicked"); QTreeWidgetItem* item = ui->presetTree->currentItem(); From df4e769e18d8cb88423a90e9fe2c31d230cbf6d7 Mon Sep 17 00:00:00 2001 From: f4exb Date: Thu, 14 Apr 2022 00:10:06 +0200 Subject: [PATCH 016/115] AirspyHF: fixed issue initializing device when open fails (no sample rates) --- .../samplesource/airspyhf/airspyhfinput.cpp | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/plugins/samplesource/airspyhf/airspyhfinput.cpp b/plugins/samplesource/airspyhf/airspyhfinput.cpp index 6704350be..70f509899 100644 --- a/plugins/samplesource/airspyhf/airspyhfinput.cpp +++ b/plugins/samplesource/airspyhf/airspyhfinput.cpp @@ -48,7 +48,7 @@ const qint64 AirspyHFInput::loHighLimitFreqVHF = 260000000L; AirspyHFInput::AirspyHFInput(DeviceAPI *deviceAPI) : m_deviceAPI(deviceAPI), m_settings(), - m_dev(0), + m_dev(nullptr), m_airspyHFWorker(nullptr), m_deviceDescription("AirspyHF"), m_running(false) @@ -89,7 +89,7 @@ void AirspyHFInput::destroy() bool AirspyHFInput::openDevice() { - if (m_dev != 0) + if (m_dev) { closeDevice(); } @@ -105,7 +105,7 @@ bool AirspyHFInput::openDevice() if ((m_dev = open_airspyhf_from_serial(m_deviceAPI->getSamplingDeviceSerial())) == 0) { qCritical("AirspyHFInput::openDevice: could not open Airspy HF with serial %s", qPrintable(m_deviceAPI->getSamplingDeviceSerial())); - m_dev = 0; + m_dev = nullptr; return false; } else @@ -226,11 +226,11 @@ void AirspyHFInput::stopWorker() void AirspyHFInput::closeDevice() { - if (m_dev != 0) + if (m_dev) { airspyhf_stop(m_dev); airspyhf_close(m_dev); - m_dev = 0; + m_dev = nullptr; } m_deviceDescription.clear(); @@ -445,7 +445,7 @@ bool AirspyHFInput::applySettings(const AirspyHFSettings& settings, bool force) sampleRateIndex = m_sampleRates.size() - 1; } - if ((m_dev != 0) && (sampleRateIndex >= 0)) + if (m_dev && (sampleRateIndex >= 0)) { rc = (airspyhf_error) airspyhf_set_samplerate(m_dev, sampleRateIndex); @@ -486,7 +486,7 @@ bool AirspyHFInput::applySettings(const AirspyHFSettings& settings, bool force) { reverseAPIKeys.append("LOppmTenths"); - if (m_dev != 0) + if (m_dev) { rc = (airspyhf_error) airspyhf_set_calibration(m_dev, settings.m_LOppmTenths * 100); @@ -520,7 +520,7 @@ bool AirspyHFInput::applySettings(const AirspyHFSettings& settings, bool force) deviceCenterFrequency = deviceCenterFrequency < 0 ? 0 : deviceCenterFrequency; qint64 f_img = deviceCenterFrequency; - if ((m_dev != 0) && (sampleRateIndex >= 0)) + if (m_dev && (sampleRateIndex >= 0)) { quint32 devSampleRate = m_sampleRates[sampleRateIndex]; setDeviceCenterFrequency(deviceCenterFrequency, settings); @@ -539,7 +539,7 @@ bool AirspyHFInput::applySettings(const AirspyHFSettings& settings, bool force) { reverseAPIKeys.append("useAGC"); - if (m_dev != 0) + if (m_dev) { rc = (airspyhf_error) airspyhf_set_hf_agc(m_dev, settings.m_useAGC ? 1 : 0); @@ -555,7 +555,7 @@ bool AirspyHFInput::applySettings(const AirspyHFSettings& settings, bool force) { reverseAPIKeys.append("agcHigh"); - if (m_dev != 0) + if (m_dev) { rc = (airspyhf_error) airspyhf_set_hf_agc_threshold(m_dev, settings.m_agcHigh ? 1 : 0); @@ -571,7 +571,7 @@ bool AirspyHFInput::applySettings(const AirspyHFSettings& settings, bool force) { reverseAPIKeys.append("useDSP"); - if (m_dev != 0) + if (m_dev) { rc = (airspyhf_error) airspyhf_set_lib_dsp(m_dev, settings.m_useDSP ? 1 : 0); @@ -587,7 +587,7 @@ bool AirspyHFInput::applySettings(const AirspyHFSettings& settings, bool force) { reverseAPIKeys.append("useLNA"); - if (m_dev != 0) + if (m_dev) { rc = (airspyhf_error) airspyhf_set_hf_lna(m_dev, settings.m_useLNA ? 1 : 0); @@ -603,7 +603,7 @@ bool AirspyHFInput::applySettings(const AirspyHFSettings& settings, bool force) { reverseAPIKeys.append("attenuatorSteps"); - if (m_dev != 0) + if (m_dev) { rc = (airspyhf_error) airspyhf_set_hf_att(m_dev, settings.m_attenuatorSteps); @@ -615,7 +615,7 @@ bool AirspyHFInput::applySettings(const AirspyHFSettings& settings, bool force) } } - if (forwardChange && (sampleRateIndex >= 0)) + if (forwardChange && (m_sampleRates.size() != 0) && (sampleRateIndex >= 0)) { int sampleRate = m_sampleRates[sampleRateIndex]/(1< Date: Thu, 14 Apr 2022 00:11:16 +0200 Subject: [PATCH 017/115] Massive UI revamping (v7): fixed initialization sequence in VOR Demod SC --- plugins/channelrx/demodvorsc/vordemodscgui.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/channelrx/demodvorsc/vordemodscgui.cpp b/plugins/channelrx/demodvorsc/vordemodscgui.cpp index 9966497f8..dbabc1268 100644 --- a/plugins/channelrx/demodvorsc/vordemodscgui.cpp +++ b/plugins/channelrx/demodvorsc/vordemodscgui.cpp @@ -282,8 +282,8 @@ VORDemodSCGUI::VORDemodSCGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Bas ui(new Ui::VORDemodSCGUI), m_pluginAPI(pluginAPI), m_deviceUISet(deviceUISet), - m_deviceCenterFrequency(0), m_channelMarker(this), + m_deviceCenterFrequency(0), m_doApplySettings(true), m_squelchOpen(false), m_tickCount(0) From 3604bf80919ce30885275f955e673aa909516d2c Mon Sep 17 00:00:00 2001 From: f4exb Date: Thu, 14 Apr 2022 03:07:33 +0200 Subject: [PATCH 018/115] Massive UI revamping (v7): fixed spectrum move and device workspace index save in preset --- .../samplesource/hackrfinput/hackrfinput.cpp | 10 +- sdrgui/device/deviceuiset.cpp | 1 + sdrgui/mainwindow.cpp | 104 ++++++++++++------ sdrgui/mainwindow.h | 6 +- 4 files changed, 78 insertions(+), 43 deletions(-) diff --git a/plugins/samplesource/hackrfinput/hackrfinput.cpp b/plugins/samplesource/hackrfinput/hackrfinput.cpp index 9f7b4bcd9..4eb5a4ead 100644 --- a/plugins/samplesource/hackrfinput/hackrfinput.cpp +++ b/plugins/samplesource/hackrfinput/hackrfinput.cpp @@ -87,7 +87,7 @@ void HackRFInput::destroy() bool HackRFInput::openDevice() { - if (m_dev != 0) + if (m_dev) { closeDevice(); } @@ -109,7 +109,7 @@ bool HackRFInput::openDevice() return false; } - if (buddySharedParams->m_dev == 0) // device is not opened by buddy + if (buddySharedParams->m_dev == nullptr) // device is not opened by buddy { qCritical("HackRFInput::openDevice: could not get HackRF handle from buddy"); return false; @@ -123,12 +123,14 @@ bool HackRFInput::openDevice() if ((m_dev = DeviceHackRF::open_hackrf(qPrintable(m_deviceAPI->getSamplingDeviceSerial()))) == 0) { qCritical("HackRFInput::openDevice: could not open HackRF %s", qPrintable(m_deviceAPI->getSamplingDeviceSerial())); + m_dev = nullptr; return false; } m_sharedParams.m_dev = m_dev; } + qDebug("HackRFInput::openDevice: success"); return true; } @@ -172,7 +174,7 @@ void HackRFInput::closeDevice() { qDebug("HackRFInput::closeDevice: closing device since Tx side is not open"); - if(m_dev != 0) // close BladeRF + if (m_dev) // close HackRF { hackrf_close(m_dev); //hackrf_exit(); // TODO: this may not work if several HackRF Devices are running concurrently. It should be handled globally in the application @@ -180,7 +182,7 @@ void HackRFInput::closeDevice() } m_sharedParams.m_dev = 0; - m_dev = 0; + m_dev = nullptr; } void HackRFInput::stop() diff --git a/sdrgui/device/deviceuiset.cpp b/sdrgui/device/deviceuiset.cpp index da3373072..7dd0e5ced 100644 --- a/sdrgui/device/deviceuiset.cpp +++ b/sdrgui/device/deviceuiset.cpp @@ -214,6 +214,7 @@ void DeviceUISet::saveDeviceSetSettings(Preset* preset) const (int) m_deviceAPI->getSamplingDeviceSequence(), (int) m_deviceAPI->getDeviceItemIndex() }); + preset->setDeviceWorkspaceIndex(m_deviceGUI->getWorkspaceIndex()); preset->clearChannels(); if (m_deviceSourceEngine) // source device diff --git a/sdrgui/mainwindow.cpp b/sdrgui/mainwindow.cpp index 467590ca7..66aaaa4bd 100644 --- a/sdrgui/mainwindow.cpp +++ b/sdrgui/mainwindow.cpp @@ -357,7 +357,7 @@ MainWindow::~MainWindow() qDebug() << "MainWindow::~MainWindow: end"; } -void MainWindow::sampleSourceAdd(Workspace *workspace, int deviceIndex) +void MainWindow::sampleSourceAdd(Workspace *deviceWorkspace, Workspace *spectrumWorkspace, int deviceIndex) { DSPDeviceSourceEngine *dspDeviceSourceEngine = m_dspEngine->addDeviceSourceEngine(); dspDeviceSourceEngine->start(); @@ -393,25 +393,26 @@ void MainWindow::sampleSourceAdd(Workspace *workspace, int deviceIndex) } sampleSourceCreate(deviceSetIndex, deviceIndex, deviceAPI, m_deviceUIs.back()); - m_deviceUIs.back()->m_deviceGUI->setWorkspaceIndex(workspace->getIndex()); - m_deviceUIs.back()->m_mainSpectrumGUI->setWorkspaceIndex(workspace->getIndex()); + m_deviceUIs.back()->m_deviceGUI->setWorkspaceIndex(deviceWorkspace->getIndex()); + m_deviceUIs.back()->m_mainSpectrumGUI->setWorkspaceIndex(spectrumWorkspace->getIndex()); + MainSpectrumGUI *mainSpectrumGUI = m_deviceUIs.back()->m_mainSpectrumGUI; QObject::connect( - m_deviceUIs.back()->m_mainSpectrumGUI, + mainSpectrumGUI, &MainSpectrumGUI::moveToWorkspace, this, - [=](int wsIndexDest){ this->mainSpectrumMove(m_deviceUIs.back()->m_mainSpectrumGUI, wsIndexDest); } + [=](int wsIndexDest){ this->mainSpectrumMove(mainSpectrumGUI, wsIndexDest); } ); QObject::connect( m_deviceUIs.back()->m_deviceGUI, &DeviceGUI::addChannelEmitted, this, - [=](int channelIndex){ this->channelAddClicked(workspace, deviceSetIndex, channelIndex); } + [=](int channelIndex){ this->channelAddClicked(deviceWorkspace, deviceSetIndex, channelIndex); } ); - workspace->addToMdiArea(m_deviceUIs.back()->m_deviceGUI); - workspace->addToMdiArea(m_deviceUIs.back()->m_mainSpectrumGUI); + deviceWorkspace->addToMdiArea(m_deviceUIs.back()->m_deviceGUI); + spectrumWorkspace->addToMdiArea(m_deviceUIs.back()->m_mainSpectrumGUI); emit m_mainCore->deviceSetAdded(deviceSetIndex, deviceAPI); } @@ -574,7 +575,7 @@ void MainWindow::sampleSourceCreate( mainSpectrumGUI->setTitle(samplingDevice->displayedName.split(" ")[0]); } -void MainWindow::sampleSinkAdd(Workspace *workspace, int deviceIndex) +void MainWindow::sampleSinkAdd(Workspace *deviceWorkspace, Workspace *spectrumWorkspace, int deviceIndex) { DSPDeviceSinkEngine *dspDeviceSinkEngine = m_dspEngine->addDeviceSinkEngine(); dspDeviceSinkEngine->start(); @@ -610,25 +611,26 @@ void MainWindow::sampleSinkAdd(Workspace *workspace, int deviceIndex) } sampleSinkCreate(deviceSetIndex, deviceIndex, deviceAPI, m_deviceUIs.back()); - m_deviceUIs.back()->m_deviceGUI->setWorkspaceIndex(workspace->getIndex()); - m_deviceUIs.back()->m_mainSpectrumGUI->setWorkspaceIndex(workspace->getIndex()); + m_deviceUIs.back()->m_deviceGUI->setWorkspaceIndex(deviceWorkspace->getIndex()); + m_deviceUIs.back()->m_mainSpectrumGUI->setWorkspaceIndex(spectrumWorkspace->getIndex()); + MainSpectrumGUI *mainSpectrumGUI = m_deviceUIs.back()->m_mainSpectrumGUI; QObject::connect( - m_deviceUIs.back()->m_mainSpectrumGUI, + mainSpectrumGUI, &MainSpectrumGUI::moveToWorkspace, this, - [=](int wsIndexDest){ this->mainSpectrumMove(m_deviceUIs.back()->m_mainSpectrumGUI, wsIndexDest); } + [=](int wsIndexDest){ this->mainSpectrumMove(mainSpectrumGUI, wsIndexDest); } ); QObject::connect( m_deviceUIs.back()->m_deviceGUI, &DeviceGUI::addChannelEmitted, this, - [=](int channelIndex){ this->channelAddClicked(workspace, deviceSetIndex, channelIndex); } + [=](int channelIndex){ this->channelAddClicked(deviceWorkspace, deviceSetIndex, channelIndex); } ); - workspace->addToMdiArea(m_deviceUIs.back()->m_deviceGUI); - workspace->addToMdiArea(m_deviceUIs.back()->m_mainSpectrumGUI); + deviceWorkspace->addToMdiArea(m_deviceUIs.back()->m_deviceGUI); + spectrumWorkspace->addToMdiArea(m_deviceUIs.back()->m_mainSpectrumGUI); emit m_mainCore->deviceSetAdded(deviceSetIndex, deviceAPI); } @@ -791,7 +793,7 @@ void MainWindow::sampleSinkCreate( spectrumGUI->setTitle(samplingDevice->displayedName.split(" ")[0]); } -void MainWindow::sampleMIMOAdd(Workspace *workspace, int deviceIndex) +void MainWindow::sampleMIMOAdd(Workspace *deviceWorkspace, Workspace *spectrumWorkspace, int deviceIndex) { DSPDeviceMIMOEngine *dspDeviceMIMOEngine = m_dspEngine->addDeviceMIMOEngine(); dspDeviceMIMOEngine->start(); @@ -835,25 +837,26 @@ void MainWindow::sampleMIMOAdd(Workspace *workspace, int deviceIndex) } sampleMIMOCreate(deviceSetIndex, deviceIndex, deviceAPI, m_deviceUIs.back()); - m_deviceUIs.back()->m_deviceGUI->setWorkspaceIndex(workspace->getIndex()); - m_deviceUIs.back()->m_mainSpectrumGUI->setWorkspaceIndex(workspace->getIndex()); + m_deviceUIs.back()->m_deviceGUI->setWorkspaceIndex(deviceWorkspace->getIndex()); + m_deviceUIs.back()->m_mainSpectrumGUI->setWorkspaceIndex(spectrumWorkspace->getIndex()); + MainSpectrumGUI *mainSpectrumGUI = m_deviceUIs.back()->m_mainSpectrumGUI; QObject::connect( - m_deviceUIs.back()->m_mainSpectrumGUI, + mainSpectrumGUI, &MainSpectrumGUI::moveToWorkspace, this, - [=](int wsIndexDest){ this->mainSpectrumMove(m_deviceUIs.back()->m_mainSpectrumGUI, wsIndexDest); } + [=](int wsIndexDest){ this->mainSpectrumMove(mainSpectrumGUI, wsIndexDest); } ); QObject::connect( m_deviceUIs.back()->m_deviceGUI, &DeviceGUI::addChannelEmitted, this, - [=](int channelIndex){ this->channelAddClicked(workspace, deviceSetIndex, channelIndex); } + [=](int channelIndex){ this->channelAddClicked(deviceWorkspace, deviceSetIndex, channelIndex); } ); - workspace->addToMdiArea(m_deviceUIs.back()->m_deviceGUI); - workspace->addToMdiArea(m_deviceUIs.back()->m_mainSpectrumGUI); + deviceWorkspace->addToMdiArea(m_deviceUIs.back()->m_deviceGUI); + spectrumWorkspace->addToMdiArea(m_deviceUIs.back()->m_mainSpectrumGUI); emit m_mainCore->deviceSetAdded(deviceSetIndex, deviceAPI); } @@ -1351,10 +1354,17 @@ void MainWindow::loadConfiguration(const Configuration *configuration, bool from deviceSetPreset.getSelectedDevice().m_deviceSequence, deviceSetPreset.getSelectedDevice().m_deviceItemIndex ); - int workspaceIndex = deviceSetPreset.getDeviceWorkspaceIndex() < m_workspaces.size() ? + qDebug("MainWindow::loadConfiguration: add source %s in workspace %d spectrum in %d", + qPrintable(deviceSetPreset.getSelectedDevice().m_deviceId), + deviceSetPreset.getDeviceWorkspaceIndex(), + deviceSetPreset.getSpectrumWorkspaceIndex()); + int deviceWorkspaceIndex = deviceSetPreset.getDeviceWorkspaceIndex() < m_workspaces.size() ? deviceSetPreset.getDeviceWorkspaceIndex() : 0; - sampleSourceAdd(m_workspaces[workspaceIndex], bestDeviceIndex); + int spectrumWorkspaceIndex = deviceSetPreset.getSpectrumWorkspaceIndex() < m_workspaces.size() ? + deviceSetPreset.getSpectrumWorkspaceIndex() : + deviceWorkspaceIndex; + sampleSourceAdd(m_workspaces[deviceWorkspaceIndex], m_workspaces[spectrumWorkspaceIndex], bestDeviceIndex); } else if (deviceSetPreset.isSinkPreset()) { @@ -1364,10 +1374,17 @@ void MainWindow::loadConfiguration(const Configuration *configuration, bool from deviceSetPreset.getSelectedDevice().m_deviceSequence, deviceSetPreset.getSelectedDevice().m_deviceItemIndex ); - int workspaceIndex = deviceSetPreset.getDeviceWorkspaceIndex() < m_workspaces.size() ? + qDebug("MainWindow::loadConfiguration: add sink %s in workspace %d spectrum in %d", + qPrintable(deviceSetPreset.getSelectedDevice().m_deviceId), + deviceSetPreset.getDeviceWorkspaceIndex(), + deviceSetPreset.getSpectrumWorkspaceIndex()); + int deviceWorkspaceIndex = deviceSetPreset.getDeviceWorkspaceIndex() < m_workspaces.size() ? deviceSetPreset.getDeviceWorkspaceIndex() : 0; - sampleSinkAdd(m_workspaces[workspaceIndex], bestDeviceIndex); + int spectrumWorkspaceIndex = deviceSetPreset.getSpectrumWorkspaceIndex() < m_workspaces.size() ? + deviceSetPreset.getSpectrumWorkspaceIndex() : + deviceWorkspaceIndex; + sampleSinkAdd(m_workspaces[deviceWorkspaceIndex], m_workspaces[spectrumWorkspaceIndex], bestDeviceIndex); } else if (deviceSetPreset.isMIMOPreset()) { int bestDeviceIndex = DeviceEnumerator::instance()->getBestMIMOSamplingDeviceIndex( @@ -1375,10 +1392,17 @@ void MainWindow::loadConfiguration(const Configuration *configuration, bool from deviceSetPreset.getSelectedDevice().m_deviceSerial, deviceSetPreset.getSelectedDevice().m_deviceSequence ); - int workspaceIndex = deviceSetPreset.getDeviceWorkspaceIndex() < m_workspaces.size() ? + qDebug("MainWindow::loadConfiguration: add MIMO %s in workspace %d spectrum in %d", + qPrintable(deviceSetPreset.getSelectedDevice().m_deviceId), + deviceSetPreset.getDeviceWorkspaceIndex(), + deviceSetPreset.getSpectrumWorkspaceIndex()); + int deviceWorkspaceIndex = deviceSetPreset.getDeviceWorkspaceIndex() < m_workspaces.size() ? deviceSetPreset.getDeviceWorkspaceIndex() : 0; - sampleMIMOAdd(m_workspaces[workspaceIndex], bestDeviceIndex); + int spectrumWorkspaceIndex = deviceSetPreset.getSpectrumWorkspaceIndex() < m_workspaces.size() ? + deviceSetPreset.getSpectrumWorkspaceIndex() : + deviceWorkspaceIndex; + sampleMIMOAdd(m_workspaces[deviceWorkspaceIndex], m_workspaces[spectrumWorkspaceIndex], bestDeviceIndex); } m_deviceUIs.back()->m_deviceGUI->restoreGeometry(deviceSetPreset.getDeviceGeometry()); @@ -1445,6 +1469,10 @@ void MainWindow::saveConfiguration(Configuration *configuration) deviceSetPresets.back().setSpectrumWorkspaceIndex(deviceUISet->m_mainSpectrumGUI->getWorkspaceIndex()); deviceSetPresets.back().setDeviceGeometry(deviceUISet->m_deviceGUI->saveGeometry()); deviceSetPresets.back().setDeviceWorkspaceIndex(deviceUISet->m_deviceGUI->getWorkspaceIndex()); + qDebug("MainWindow::saveConfiguration: %s device in workspace %d spectrun in %d", + qPrintable(deviceUISet->m_deviceAPI->getSamplingDeviceId()), + deviceUISet->m_deviceGUI->getWorkspaceIndex(), + deviceUISet->m_mainSpectrumGUI->getWorkspaceIndex()); } m_featureUIs[0]->saveFeatureSetSettings(&configuration->getFeatureSetPreset()); @@ -1733,11 +1761,11 @@ bool MainWindow::handleMessage(const Message& cmd) // in Server flavor. Set nullptr for workspace if index is out of bonds if (direction == 1) { // Single stream Tx - sampleSinkAdd(nullptr, -1); // create with file output device by default + sampleSinkAdd(nullptr, nullptr, -1); // create with file output device by default } else if (direction == 0) { // Single stream Rx - sampleSourceAdd(nullptr, -1); // create with file input device by default + sampleSourceAdd(nullptr, nullptr, -1); // create with file input device by default } else if (direction == 2) { // MIMO - sampleMIMOAdd(nullptr, -1); // create with testMI MIMO device y default + sampleMIMOAdd(nullptr, nullptr, -1); // create with testMI MIMO device y default } return true; @@ -1886,21 +1914,21 @@ void MainWindow::addWorkspace() m_workspaces.back(), &Workspace::addRxDevice, this, - &MainWindow::sampleSourceAdd + [=](Workspace *inWorkspace, int deviceIndex) { this->sampleSourceAdd(inWorkspace, inWorkspace, deviceIndex); } ); QObject::connect( m_workspaces.back(), &Workspace::addTxDevice, this, - &MainWindow::sampleSinkAdd + [=](Workspace *inWorkspace, int deviceIndex) { this->sampleSourceAdd(inWorkspace, inWorkspace, deviceIndex); } ); QObject::connect( m_workspaces.back(), &Workspace::addMIMODevice, this, - &MainWindow::sampleMIMOAdd + [=](Workspace *inWorkspace, int deviceIndex) { this->sampleSourceAdd(inWorkspace, inWorkspace, deviceIndex); } ); QObject::connect( @@ -2700,6 +2728,8 @@ void MainWindow::featureMove(FeatureGUI *gui, int wsIndexDestnation) void MainWindow::deviceMove(DeviceGUI *gui, int wsIndexDestnation) { int wsIndexOrigin = gui->getWorkspaceIndex(); + qDebug("MainWindow::deviceMove: %s from %d to %d", + qPrintable(gui->getTitle()), wsIndexOrigin, wsIndexDestnation); if (wsIndexOrigin == wsIndexDestnation) { return; @@ -2726,6 +2756,8 @@ void MainWindow::channelMove(ChannelGUI *gui, int wsIndexDestnation) void MainWindow::mainSpectrumMove(MainSpectrumGUI *gui, int wsIndexDestnation) { int wsIndexOrigin = gui->getWorkspaceIndex(); + qDebug("MainWindow::mainSpectrumMove: %s from %d to %d", + qPrintable(gui->getTitle()), wsIndexOrigin, wsIndexDestnation); if (wsIndexOrigin == wsIndexDestnation) { return; diff --git a/sdrgui/mainwindow.h b/sdrgui/mainwindow.h index 3cff9ce7c..bbd510ab3 100644 --- a/sdrgui/mainwindow.h +++ b/sdrgui/mainwindow.h @@ -181,9 +181,9 @@ private slots: void removeEmptyWorkspaces(); void loadConfiguration(const Configuration *configuration, bool fromDialog = false); void saveConfiguration(Configuration *configuration); - void sampleSourceAdd(Workspace *workspace, int deviceIndex); - void sampleSinkAdd(Workspace *workspace, int deviceIndex); - void sampleMIMOAdd(Workspace *workspace, int deviceIndex); + void sampleSourceAdd(Workspace *deviceWorkspace, Workspace *spectrumWorkspace, int deviceIndex); + void sampleSinkAdd(Workspace *workspace, Workspace *spectrumWorkspace, int deviceIndex); + void sampleMIMOAdd(Workspace *workspace, Workspace *spectrumWorkspace, int deviceIndex); void sampleDeviceChangeHandler(DeviceGUI *deviceGUI, int newDeviceIndex); void on_action_View_Fullscreen_toggled(bool checked); From ec7a10e66205399c0f5b1e5cb714480b0e04f788 Mon Sep 17 00:00:00 2001 From: f4exb Date: Thu, 14 Apr 2022 03:49:32 +0200 Subject: [PATCH 019/115] Massive UI revamping (v7): removed --mimo option and added --scrach option. Fixes #1201 --- sdrbase/mainparser.cpp | 22 +++++++--------------- sdrbase/mainparser.h | 5 +++-- sdrgui/mainwindow.cpp | 22 +++++++++++++++------- sdrsrv/mainserver.cpp | 2 +- 4 files changed, 26 insertions(+), 25 deletions(-) diff --git a/sdrbase/mainparser.cpp b/sdrbase/mainparser.cpp index ef3cf48b2..4f1a4bab6 100644 --- a/sdrbase/mainparser.cpp +++ b/sdrbase/mainparser.cpp @@ -34,11 +34,13 @@ MainParser::MainParser() : m_fftwfWisdomOption(QStringList() << "w" << "fftwf-wisdom", "FFTW Wisdom file.", "file", - "") + ""), + m_scratchOption("scratch", "Start from scratch (no current config).") { + m_serverAddress = ""; // Bind to any address m_serverPort = 8091; - m_mimoSupport = false; + m_scratch = false; m_fftwfWindowFileName = ""; m_parser.setApplicationDescription("Software Defined Radio application"); @@ -48,6 +50,7 @@ MainParser::MainParser() : m_parser.addOption(m_serverAddressOption); m_parser.addOption(m_serverPortOption); m_parser.addOption(m_fftwfWisdomOption); + m_parser.addOption(m_scratchOption); } MainParser::~MainParser() @@ -94,18 +97,7 @@ void MainParser::parse(const QCoreApplication& app) m_fftwfWindowFileName = m_parser.value(m_fftwfWisdomOption); - // MIMO - from version + // Scratch mode - QStringList versionParts = app.applicationVersion().split("."); - - if (versionParts.size() > 0) - { - bool ok; - int maj = versionParts.at(0).toInt(&ok); - m_mimoSupport = ok && (maj > 4); - } - else - { - m_mimoSupport = false; - } + m_scratch = m_parser.isSet(m_scratchOption); } diff --git a/sdrbase/mainparser.h b/sdrbase/mainparser.h index 100752798..a04541909 100644 --- a/sdrbase/mainparser.h +++ b/sdrbase/mainparser.h @@ -34,19 +34,20 @@ public: const QString& getServerAddress() const { return m_serverAddress; } uint16_t getServerPort() const { return m_serverPort; } - bool getMIMOSupport() const { return m_mimoSupport; } + bool getScratch() const { return m_scratch; } const QString& getFFTWFWisdomFileName() const { return m_fftwfWindowFileName; } private: QString m_serverAddress; uint16_t m_serverPort; QString m_fftwfWindowFileName; - bool m_mimoSupport; //!< obtained from major version + bool m_scratch; QCommandLineParser m_parser; QCommandLineOption m_serverAddressOption; QCommandLineOption m_serverPortOption; QCommandLineOption m_fftwfWisdomOption; + QCommandLineOption m_scratchOption; }; diff --git a/sdrgui/mainwindow.cpp b/sdrgui/mainwindow.cpp index 66aaaa4bd..a0bd76e24 100644 --- a/sdrgui/mainwindow.cpp +++ b/sdrgui/mainwindow.cpp @@ -261,13 +261,21 @@ MainWindow::MainWindow(qtwebapp::LoggerWithFile *logger, const MainParser& parse // m_deviceUIs.back()->m_deviceAPI->setBuddyLeader(true); // the first device is always the leader tabChannelsIndexChanged(); // force channel selection list update - splash->showStatusMessage("load current configuration...", Qt::white); - qDebug() << "MainWindow::MainWindow: load current configuration..."; - - // loadDeviceSetPresetSettings(m_mainCore->m_settings.getWorkingPreset(), 0); m_apiAdapter = new WebAPIAdapter(); - // loadFeatureSetPresetSettings(m_mainCore->m_settings.getWorkingFeatureSetPreset(), 0); - loadConfiguration(m_mainCore->m_settings.getWorkingConfiguration()); + + if (!parser.getScratch()) + { + splash->showStatusMessage("load current configuration...", Qt::white); + qDebug() << "MainWindow::MainWindow: load current configuration..."; + + // loadDeviceSetPresetSettings(m_mainCore->m_settings.getWorkingPreset(), 0); + // loadFeatureSetPresetSettings(m_mainCore->m_settings.getWorkingFeatureSetPreset(), 0); + loadConfiguration(m_mainCore->m_settings.getWorkingConfiguration()); + } + else + { + qDebug() << "MainWindow::MainWindow: scratch mode: do not load current configuration"; + } // splash->showStatusMessage("update preset controls...", Qt::white); // qDebug() << "MainWindow::MainWindow: update preset controls..."; @@ -304,7 +312,7 @@ MainWindow::MainWindow(qtwebapp::LoggerWithFile *logger, const MainParser& parse m_commandKeyReceiver->setRelease(true); this->installEventFilter(m_commandKeyReceiver); - m_dspEngine->setMIMOSupport(parser.getMIMOSupport()); + m_dspEngine->setMIMOSupport(true); // if (!parser.getMIMOSupport()) { // ui->menu_Devices->removeAction(ui->action_sampleMIMOAdd); diff --git a/sdrsrv/mainserver.cpp b/sdrsrv/mainserver.cpp index 9547b88c1..5380041f0 100644 --- a/sdrsrv/mainserver.cpp +++ b/sdrsrv/mainserver.cpp @@ -76,7 +76,7 @@ MainServer::MainServer(qtwebapp::LoggerWithFile *logger, const MainParser& parse m_apiServer = new WebAPIServer(parser.getServerAddress(), parser.getServerPort(), m_requestMapper); m_apiServer->start(); - m_dspEngine->setMIMOSupport(parser.getMIMOSupport()); + m_dspEngine->setMIMOSupport(true); qDebug() << "MainServer::MainServer: end"; } From 8a3d2bd497aa1b323db1f0510c13ee2e61adc734 Mon Sep 17 00:00:00 2001 From: f4exb Date: Thu, 14 Apr 2022 12:08:18 +0200 Subject: [PATCH 020/115] Massive UI revamping (v7): Removed forceClose --- sdrgui/channel/channelgui.cpp | 10 ++++++---- sdrgui/channel/channelgui.h | 6 +++--- sdrgui/device/devicegui.cpp | 4 +--- sdrgui/device/devicegui.h | 1 - sdrgui/mainspectrum/mainspectrumgui.cpp | 1 - sdrgui/mainspectrum/mainspectrumgui.h | 1 - sdrgui/mainwindow.cpp | 6 +++--- 7 files changed, 13 insertions(+), 16 deletions(-) diff --git a/sdrgui/channel/channelgui.cpp b/sdrgui/channel/channelgui.cpp index 383ec3828..d7ae3ab46 100644 --- a/sdrgui/channel/channelgui.cpp +++ b/sdrgui/channel/channelgui.cpp @@ -28,6 +28,7 @@ #include "mainwindow.h" #include "gui/workspaceselectiondialog.h" +#include "gui/rollupcontents.h" #include "channelgui.h" @@ -129,7 +130,8 @@ ChannelGUI::ChannelGUI(QWidget *parent) : m_topLayout->addWidget(m_sizeGripTopRight, 0, Qt::AlignTop | Qt::AlignRight); m_centerLayout = new QHBoxLayout(); - m_centerLayout->addWidget(&m_rollupContents); + m_rollupContents = new RollupContents(); // Do not delete! Done in child's destructor with "delete ui" + m_centerLayout->addWidget(m_rollupContents); m_bottomLayout = new QHBoxLayout(); m_bottomLayout->setContentsMargins(0, 0, 0, 0); @@ -157,7 +159,7 @@ ChannelGUI::ChannelGUI(QWidget *parent) : connect(m_closeButton, SIGNAL(clicked()), this, SLOT(close())); connect( - &m_rollupContents, + m_rollupContents, &RollupContents::widgetRolled, this, &ChannelGUI::onWidgetRolled @@ -258,13 +260,13 @@ void ChannelGUI::onWidgetRolled(QWidget *widget, bool show) { // qDebug("ChannelGUI::onWidgetRolled: show: %d %d", m_rollupContents.height(), widget->height()); int dh = m_heightsMap.contains(widget) ? m_heightsMap[widget] - widget->height() : widget->minimumHeight(); - resize(width(), 52 + m_rollupContents.height() + dh); + resize(width(), 52 + 3 + m_rollupContents->height() + dh); } else { // qDebug("ChannelGUI::onWidgetRolled: hide: %d %d", m_rollupContents.height(), widget->height()); m_heightsMap[widget] = widget->height(); - resize(width(), 52 + m_rollupContents.height()); + resize(width(), 52 + 3 + m_rollupContents->height()); } } diff --git a/sdrgui/channel/channelgui.h b/sdrgui/channel/channelgui.h index 326384e64..cc0116fa4 100644 --- a/sdrgui/channel/channelgui.h +++ b/sdrgui/channel/channelgui.h @@ -21,7 +21,6 @@ #include #include -#include "gui/rollupcontents.h" #include "export.h" class QCloseEvent; @@ -31,6 +30,7 @@ class QPushButton; class QVBoxLayout; class QHBoxLayout; class QSizeGrip; +class RollupContents; class SDRGUI_API ChannelGUI : public QMdiSubWindow { @@ -69,7 +69,7 @@ public: virtual MessageQueue* getInputMessageQueue() = 0; - RollupContents *getRollupContents() { return &m_rollupContents; } + RollupContents *getRollupContents() { return m_rollupContents; } void setTitle(const QString& title); void setTitleColor(const QColor& c); void setDeviceType(DeviceType type); @@ -93,7 +93,7 @@ protected: int m_deviceSetIndex; int m_channelIndex; QString m_helpURL; - RollupContents m_rollupContents; + RollupContents* m_rollupContents; ContextMenuType m_contextMenuType; protected slots: diff --git a/sdrgui/device/devicegui.cpp b/sdrgui/device/devicegui.cpp index c4c68a84e..34eba9840 100644 --- a/sdrgui/device/devicegui.cpp +++ b/sdrgui/device/devicegui.cpp @@ -145,7 +145,7 @@ DeviceGUI::DeviceGUI(QWidget *parent) : m_topLayout->addWidget(m_sizeGripTopRight, 0, Qt::AlignTop | Qt::AlignRight); m_centerLayout = new QHBoxLayout(); - m_contents = new QWidget(); + m_contents = new QWidget(); // Do not delete! Done in child's destructor with "delete ui" m_centerLayout->addWidget(m_contents); m_bottomLayout = new QHBoxLayout(); @@ -175,7 +175,6 @@ DeviceGUI::DeviceGUI(QWidget *parent) : connect(m_shrinkButton, SIGNAL(clicked()), this, SLOT(shrinkWindow())); connect(this, SIGNAL(forceShrink()), this, SLOT(shrinkWindow())); connect(m_closeButton, SIGNAL(clicked()), this, SLOT(close())); - connect(this, SIGNAL(forceClose()), this, SLOT(close())); connect(m_showSpectrumButton, SIGNAL(clicked()), this, SLOT(showSpectrumHandler())); connect(m_showAllChannelsButton, SIGNAL(clicked()), this, SLOT(showAllChannelsHandler())); @@ -209,7 +208,6 @@ DeviceGUI::~DeviceGUI() delete m_reloadDeviceButton; delete m_changeDeviceButton; delete m_indexLabel; - delete m_contents; qDebug("DeviceGUI::~DeviceGUI: end"); } diff --git a/sdrgui/device/devicegui.h b/sdrgui/device/devicegui.h index f8e99e894..595a0a112 100644 --- a/sdrgui/device/devicegui.h +++ b/sdrgui/device/devicegui.h @@ -134,7 +134,6 @@ private slots: void deviceSetPresetsDialog(); signals: - void forceClose(); void closing(); void moveToWorkspace(int workspaceIndex); void forceShrink(); diff --git a/sdrgui/mainspectrum/mainspectrumgui.cpp b/sdrgui/mainspectrum/mainspectrumgui.cpp index 04349036b..ef98a195a 100644 --- a/sdrgui/mainspectrum/mainspectrumgui.cpp +++ b/sdrgui/mainspectrum/mainspectrumgui.cpp @@ -135,7 +135,6 @@ MainSpectrumGUI::MainSpectrumGUI(GLSpectrum *spectrum, GLSpectrumGUI *spectrumGU connect(m_shrinkButton, SIGNAL(clicked()), this, SLOT(shrinkWindow())); connect(this, SIGNAL(forceShrink()), this, SLOT(shrinkWindow())); connect(m_hideButton, SIGNAL(clicked()), this, SLOT(hide())); - connect(this, SIGNAL(forceClose()), this, SLOT(close())); shrinkWindow(); } diff --git a/sdrgui/mainspectrum/mainspectrumgui.h b/sdrgui/mainspectrum/mainspectrumgui.h index 4a03ba983..29892af29 100644 --- a/sdrgui/mainspectrum/mainspectrumgui.h +++ b/sdrgui/mainspectrum/mainspectrumgui.h @@ -100,7 +100,6 @@ private slots: void shrinkWindow(); signals: - void forceClose(); void closing(); void moveToWorkspace(int workspaceIndex); void forceShrink(); diff --git a/sdrgui/mainwindow.cpp b/sdrgui/mainwindow.cpp index a0bd76e24..47be1a7d8 100644 --- a/sdrgui/mainwindow.cpp +++ b/sdrgui/mainwindow.cpp @@ -509,7 +509,7 @@ void MainWindow::sampleSourceCreate( // delete previous GUI if it exists if (deviceUISet->m_deviceGUI) { - emit deviceUISet->m_deviceGUI->forceClose(); + emit deviceUISet->m_deviceGUI->destroy(); } // constructs new GUI and input object @@ -727,7 +727,7 @@ void MainWindow::sampleSinkCreate( // delete previous plugin GUI if it exists if (deviceUISet->m_deviceGUI) { - emit deviceUISet->m_deviceGUI->forceClose(); + emit deviceUISet->m_deviceGUI->destroy(); } // constructs new GUI and output object @@ -919,7 +919,7 @@ void MainWindow::sampleMIMOCreate( // delete previous plugin GUI if it exists if (deviceUISet->m_deviceGUI) { - emit deviceUISet->m_deviceGUI->forceClose(); + emit deviceUISet->m_deviceGUI->destroy(); } // constructs new GUI and output object From 98a3a76ca111652a7e6714e9231e6c42bb212a0b Mon Sep 17 00:00:00 2001 From: f4exb Date: Thu, 14 Apr 2022 13:14:44 +0200 Subject: [PATCH 021/115] Massive UI revamping (v7): DSDDemod and DATVDemod: Prefer memory leak to core dump as TVScreen destructor is buggy --- plugins/channelrx/demoddatv/datvdemodgui.cpp | 1 + plugins/channelrx/demoddsd/dsddemodgui.cpp | 1 + 2 files changed, 2 insertions(+) diff --git a/plugins/channelrx/demoddatv/datvdemodgui.cpp b/plugins/channelrx/demoddatv/datvdemodgui.cpp index 5f5d08454..2d745594a 100644 --- a/plugins/channelrx/demoddatv/datvdemodgui.cpp +++ b/plugins/channelrx/demoddatv/datvdemodgui.cpp @@ -302,6 +302,7 @@ DATVDemodGUI::DATVDemodGUI(PluginAPI* objPluginAPI, DeviceUISet *deviceUISet, Ba DATVDemodGUI::~DATVDemodGUI() { + ui->screenTV->setParent(nullptr); // Prefer memory leak to core dump... ~TVScreen() is buggy delete ui; } diff --git a/plugins/channelrx/demoddsd/dsddemodgui.cpp b/plugins/channelrx/demoddsd/dsddemodgui.cpp index 5d1213cf2..5bd437c50 100644 --- a/plugins/channelrx/demoddsd/dsddemodgui.cpp +++ b/plugins/channelrx/demoddsd/dsddemodgui.cpp @@ -404,6 +404,7 @@ DSDDemodGUI::DSDDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Baseban DSDDemodGUI::~DSDDemodGUI() { delete m_scopeVisXY; + ui->screenTV->setParent(nullptr); // Prefer memory leak to core dump... ~TVScreen() is buggy delete ui; } From 054d9890409a423b1667b07b0d9f6f6b3878f258 Mon Sep 17 00:00:00 2001 From: f4exb Date: Thu, 14 Apr 2022 13:16:01 +0200 Subject: [PATCH 022/115] Massive UI revamping (v7): GLShaderTVArray: updated make proper initialization list --- sdrgui/gui/glshadertvarray.cpp | 29 +++++++++++++++-------------- sdrgui/gui/glshadertvarray.h | 8 +++----- 2 files changed, 18 insertions(+), 19 deletions(-) diff --git a/sdrgui/gui/glshadertvarray.cpp b/sdrgui/gui/glshadertvarray.cpp index 5a8f4602c..0ed685258 100644 --- a/sdrgui/gui/glshadertvarray.cpp +++ b/sdrgui/gui/glshadertvarray.cpp @@ -37,24 +37,25 @@ const QString GLShaderTVArray::m_strFragmentShaderSourceColored = QString( " gl_FragColor = texture2D(uTexture, texCoordVar);\n" "}\n"); -GLShaderTVArray::GLShaderTVArray(bool blnColor) : m_blnColor(blnColor) +GLShaderTVArray::GLShaderTVArray(bool blnColor) : + m_objProgram(nullptr), + m_matrixLoc(0), + m_textureLoc(0), + m_objImage(nullptr), + m_objTexture(nullptr), + m_intCols(0), + m_intRows(0), + m_objCurrentRow(nullptr), + m_blnInitialized(false), + m_blnColor(blnColor), + m_blnAlphaBlend(false), + m_blnAlphaReset(false) { - m_blnAlphaBlend = false; - m_blnAlphaReset = false; - m_objProgram = nullptr; - m_objImage = nullptr; - m_objTexture = nullptr; - m_intCols = 0; - m_intRows = 0; - m_blnInitialized = false; - m_objCurrentRow = nullptr; - - m_textureLoc = 0; - m_matrixLoc = 0; } GLShaderTVArray::~GLShaderTVArray() { + qDebug("GLShaderTVArray::~GLShaderTVArray"); cleanup(); } @@ -128,7 +129,7 @@ void GLShaderTVArray::initializeGL(int intCols, int intRows) } -QRgb * GLShaderTVArray::GetRowBuffer(int intRow) +QRgb *GLShaderTVArray::GetRowBuffer(int intRow) { if (!m_blnInitialized) { return nullptr; diff --git a/sdrgui/gui/glshadertvarray.h b/sdrgui/gui/glshadertvarray.h index 488acba3c..4a2ce35f5 100644 --- a/sdrgui/gui/glshadertvarray.h +++ b/sdrgui/gui/glshadertvarray.h @@ -55,9 +55,7 @@ public: bool SelectRow(int intLine); bool SetDataColor(int intCol,QRgb objColor); - protected: - QOpenGLShaderProgram *m_objProgram; int m_matrixLoc; int m_textureLoc; @@ -65,13 +63,13 @@ protected: static const QString m_strVertexShaderSourceArray; static const QString m_strFragmentShaderSourceColored; - QImage *m_objImage=NULL; - QOpenGLTexture *m_objTexture=NULL; + QImage *m_objImage; + QOpenGLTexture *m_objTexture; int m_intCols; int m_intRows; - QRgb * m_objCurrentRow; + QRgb *m_objCurrentRow; bool m_blnInitialized; bool m_blnColor; From eb720c3f7522b6770a4817442f51a54a97704759 Mon Sep 17 00:00:00 2001 From: f4exb Date: Thu, 14 Apr 2022 13:16:54 +0200 Subject: [PATCH 023/115] Massive UI revamping (v7): Removed unneeded references to ScopeVisXY --- plugins/channelrx/demodais/aisdemodgui.h | 1 - plugins/channelrx/demodradiosonde/radiosondedemodgui.h | 1 - plugins/channelrx/radioclock/radioclockgui.h | 1 - plugins/channeltx/mod802.15.4/ieee_802_15_4_modgui.h | 1 - 4 files changed, 4 deletions(-) diff --git a/plugins/channelrx/demodais/aisdemodgui.h b/plugins/channelrx/demodais/aisdemodgui.h index fb977d1f0..a77d95f25 100644 --- a/plugins/channelrx/demodais/aisdemodgui.h +++ b/plugins/channelrx/demodais/aisdemodgui.h @@ -37,7 +37,6 @@ class PluginAPI; class DeviceUISet; class BasebandSampleSink; class ScopeVis; -class ScopeVisXY; class AISDemod; class AISDemodGUI; diff --git a/plugins/channelrx/demodradiosonde/radiosondedemodgui.h b/plugins/channelrx/demodradiosonde/radiosondedemodgui.h index 4f44d1fe8..0f64739b0 100644 --- a/plugins/channelrx/demodradiosonde/radiosondedemodgui.h +++ b/plugins/channelrx/demodradiosonde/radiosondedemodgui.h @@ -34,7 +34,6 @@ class PluginAPI; class DeviceUISet; class BasebandSampleSink; class ScopeVis; -class ScopeVisXY; class RadiosondeDemod; class RadiosondeDemodGUI; class RS41Frame; diff --git a/plugins/channelrx/radioclock/radioclockgui.h b/plugins/channelrx/radioclock/radioclockgui.h index c5691d9bc..74259e465 100644 --- a/plugins/channelrx/radioclock/radioclockgui.h +++ b/plugins/channelrx/radioclock/radioclockgui.h @@ -31,7 +31,6 @@ class PluginAPI; class DeviceUISet; class BasebandSampleSink; class ScopeVis; -class ScopeVisXY; class RadioClock; class RadioClockGUI; diff --git a/plugins/channeltx/mod802.15.4/ieee_802_15_4_modgui.h b/plugins/channeltx/mod802.15.4/ieee_802_15_4_modgui.h index a7f9266d6..2071794f8 100644 --- a/plugins/channeltx/mod802.15.4/ieee_802_15_4_modgui.h +++ b/plugins/channeltx/mod802.15.4/ieee_802_15_4_modgui.h @@ -33,7 +33,6 @@ class DeviceUISet; class BasebandSampleSource; class SpectrumVis; class ScopeVis; -class ScopeVisXY; namespace Ui { class IEEE_802_15_4_ModGUI; From 0ec3ee5a5712f83479fdde9642e58bae7a7d5185 Mon Sep 17 00:00:00 2001 From: f4exb Date: Thu, 14 Apr 2022 20:41:32 +0200 Subject: [PATCH 024/115] Massive UI revamping (v7): device change: Prefer memory leak to core dump by not deleting old GUI. Unresolved seqfault --- .../hackrfinput/hackrfinputgui.cpp | 2 + plugins/samplesource/rtlsdr/rtlsdrgui.cpp | 2 + sdrgui/mainwindow.cpp | 38 ++++++++----------- sdrgui/mainwindow.h | 3 -- 4 files changed, 19 insertions(+), 26 deletions(-) diff --git a/plugins/samplesource/hackrfinput/hackrfinputgui.cpp b/plugins/samplesource/hackrfinput/hackrfinputgui.cpp index 358c8b1b4..7f2c2a8d1 100644 --- a/plugins/samplesource/hackrfinput/hackrfinputgui.cpp +++ b/plugins/samplesource/hackrfinput/hackrfinputgui.cpp @@ -78,7 +78,9 @@ HackRFInputGui::HackRFInputGui(DeviceUISet *deviceUISet, QWidget* parent) : HackRFInputGui::~HackRFInputGui() { + qDebug("HackRFInputGui::~HackRFInputGui"); delete ui; + qDebug("HackRFInputGui::~HackRFInputGui: end"); } void HackRFInputGui::destroy() diff --git a/plugins/samplesource/rtlsdr/rtlsdrgui.cpp b/plugins/samplesource/rtlsdr/rtlsdrgui.cpp index 71a2f3b9d..9d10e9b83 100644 --- a/plugins/samplesource/rtlsdr/rtlsdrgui.cpp +++ b/plugins/samplesource/rtlsdr/rtlsdrgui.cpp @@ -78,7 +78,9 @@ RTLSDRGui::RTLSDRGui(DeviceUISet *deviceUISet, QWidget* parent) : RTLSDRGui::~RTLSDRGui() { + qDebug("RTLSDRGui::~RTLSDRGui"); delete ui; + qDebug("RTLSDRGui::~RTLSDRGui: end"); } void RTLSDRGui::destroy() diff --git a/sdrgui/mainwindow.cpp b/sdrgui/mainwindow.cpp index 47be1a7d8..1f11dbd68 100644 --- a/sdrgui/mainwindow.cpp +++ b/sdrgui/mainwindow.cpp @@ -400,7 +400,7 @@ void MainWindow::sampleSourceAdd(Workspace *deviceWorkspace, Workspace *spectrum deviceIndex = DeviceEnumerator::instance()->getFileInputDeviceIndex(); } - sampleSourceCreate(deviceSetIndex, deviceIndex, deviceAPI, m_deviceUIs.back()); + sampleSourceCreate(deviceSetIndex, deviceIndex, m_deviceUIs.back()); m_deviceUIs.back()->m_deviceGUI->setWorkspaceIndex(deviceWorkspace->getIndex()); m_deviceUIs.back()->m_mainSpectrumGUI->setWorkspaceIndex(spectrumWorkspace->getIndex()); MainSpectrumGUI *mainSpectrumGUI = m_deviceUIs.back()->m_mainSpectrumGUI; @@ -427,10 +427,10 @@ void MainWindow::sampleSourceAdd(Workspace *deviceWorkspace, Workspace *spectrum void MainWindow::sampleSourceCreate( int deviceSetIndex, int deviceIndex, - DeviceAPI *deviceAPI, DeviceUISet *deviceUISet ) { + DeviceAPI *deviceAPI = deviceUISet->m_deviceAPI; int selectedDeviceIndex = deviceIndex; DeviceEnumerator::instance()->changeRxSelection(deviceSetIndex, deviceIndex); const PluginInterface::SamplingDevice *samplingDevice = DeviceEnumerator::instance()->getRxSamplingDevice(deviceIndex); @@ -507,10 +507,7 @@ void MainWindow::sampleSourceCreate( deviceUISet->m_deviceAPI->setBuddyLeader(true); } - // delete previous GUI if it exists - if (deviceUISet->m_deviceGUI) { - emit deviceUISet->m_deviceGUI->destroy(); - } + // DeviceGUI *oldDeviceGUI = deviceUISet->m_deviceGUI; // store old GUI pointer for later // constructs new GUI and input object DeviceSampleSource *source = deviceAPI->getPluginInterface()->createSampleSourcePluginInstance( @@ -618,7 +615,7 @@ void MainWindow::sampleSinkAdd(Workspace *deviceWorkspace, Workspace *spectrumWo deviceIndex = DeviceEnumerator::instance()->getFileOutputDeviceIndex(); // create a file output by default } - sampleSinkCreate(deviceSetIndex, deviceIndex, deviceAPI, m_deviceUIs.back()); + sampleSinkCreate(deviceSetIndex, deviceIndex, m_deviceUIs.back()); m_deviceUIs.back()->m_deviceGUI->setWorkspaceIndex(deviceWorkspace->getIndex()); m_deviceUIs.back()->m_mainSpectrumGUI->setWorkspaceIndex(spectrumWorkspace->getIndex()); MainSpectrumGUI *mainSpectrumGUI = m_deviceUIs.back()->m_mainSpectrumGUI; @@ -645,10 +642,10 @@ void MainWindow::sampleSinkAdd(Workspace *deviceWorkspace, Workspace *spectrumWo void MainWindow::sampleSinkCreate( int deviceSetIndex, int deviceIndex, - DeviceAPI *deviceAPI, DeviceUISet *deviceUISet ) { + DeviceAPI *deviceAPI = deviceUISet->m_deviceAPI; int selectedDeviceIndex = deviceIndex; DeviceEnumerator::instance()->changeTxSelection(deviceSetIndex, deviceIndex); const PluginInterface::SamplingDevice *samplingDevice = DeviceEnumerator::instance()->getTxSamplingDevice(deviceIndex); @@ -725,10 +722,7 @@ void MainWindow::sampleSinkCreate( deviceAPI->setBuddyLeader(true); } - // delete previous plugin GUI if it exists - if (deviceUISet->m_deviceGUI) { - emit deviceUISet->m_deviceGUI->destroy(); - } + // DeviceGUI *oldDeviceGUI = deviceUISet->m_deviceGUI; // store old GUI pointer for later // constructs new GUI and output object DeviceSampleSink *sink = deviceAPI->getPluginInterface()->createSampleSinkPluginInstance( @@ -844,7 +838,7 @@ void MainWindow::sampleMIMOAdd(Workspace *deviceWorkspace, Workspace *spectrumWo deviceIndex = DeviceEnumerator::instance()->getTestMIMODeviceIndex(); // create a test MIMO by default } - sampleMIMOCreate(deviceSetIndex, deviceIndex, deviceAPI, m_deviceUIs.back()); + sampleMIMOCreate(deviceSetIndex, deviceIndex, m_deviceUIs.back()); m_deviceUIs.back()->m_deviceGUI->setWorkspaceIndex(deviceWorkspace->getIndex()); m_deviceUIs.back()->m_mainSpectrumGUI->setWorkspaceIndex(spectrumWorkspace->getIndex()); MainSpectrumGUI *mainSpectrumGUI = m_deviceUIs.back()->m_mainSpectrumGUI; @@ -871,10 +865,10 @@ void MainWindow::sampleMIMOAdd(Workspace *deviceWorkspace, Workspace *spectrumWo void MainWindow::sampleMIMOCreate( int deviceSetIndex, int deviceIndex, - DeviceAPI *deviceAPI, DeviceUISet *deviceUISet ) { + DeviceAPI *deviceAPI = deviceUISet->m_deviceAPI; int selectedDeviceIndex = deviceIndex; DeviceEnumerator::instance()->changeMIMOSelection(deviceSetIndex, deviceIndex); const PluginInterface::SamplingDevice *samplingDevice = DeviceEnumerator::instance()->getMIMOSamplingDevice(deviceIndex); @@ -917,10 +911,7 @@ void MainWindow::sampleMIMOCreate( deviceAPI->setHardwareUserArguments(userArgs); } - // delete previous plugin GUI if it exists - if (deviceUISet->m_deviceGUI) { - emit deviceUISet->m_deviceGUI->destroy(); - } + // DeviceGUI *oldDeviceGUI = deviceUISet->m_deviceGUI; // store old GUI pointer for later // constructs new GUI and output object DeviceSampleMIMO *mimo = deviceAPI->getPluginInterface()->createSampleMIMOPluginInstance( @@ -995,6 +986,7 @@ void MainWindow::sampleMIMOCreate( void MainWindow::removeDeviceSet(int deviceSetIndex) { + qDebug("MainWindow::removeDeviceSet: index: %d", deviceSetIndex); if (deviceSetIndex >= (int) m_deviceUIs.size()) { return; } @@ -2513,19 +2505,19 @@ void MainWindow::sampleSourceChange(int deviceSetIndex, int newDeviceIndex, Work { qDebug("MainWindow::sampleSourceChange: deviceSet %d workspace: %d", deviceSetIndex, workspace->getIndex()); DeviceUISet *deviceUISet = m_deviceUIs[deviceSetIndex]; - DeviceGUI *gui = deviceUISet->m_deviceGUI; - workspace->removeFromMdiArea(gui); + workspace->removeFromMdiArea(deviceUISet->m_deviceGUI); // deviceUI->m_deviceAPI->saveSamplingDeviceSettings(m_mainCore->m_settings.getWorkingPreset()); // save old API settings deviceUISet->m_deviceAPI->stopDeviceEngine(); // deletes old UI and input object deviceUISet->m_deviceAPI->getSampleSource()->setMessageQueueToGUI(nullptr); // have source stop sending messages to the GUI + //deviceUISet->m_deviceGUI->destroy(); deviceUISet->m_deviceAPI->resetSamplingDeviceId(); deviceUISet->m_deviceAPI->getPluginInterface()->deleteSampleSourcePluginInstanceInput(deviceUISet->m_deviceAPI->getSampleSource()); deviceUISet->m_deviceAPI->clearBuddiesLists(); // clear old API buddies lists - sampleSourceCreate(deviceSetIndex, newDeviceIndex, deviceUISet->m_deviceAPI, deviceUISet); + sampleSourceCreate(deviceSetIndex, newDeviceIndex, deviceUISet); deviceUISet->m_deviceGUI->setWorkspaceIndex(workspace->getIndex()); workspace->addToMdiArea(deviceUISet->m_deviceGUI); @@ -2554,7 +2546,7 @@ void MainWindow::sampleSinkChange(int deviceSetIndex, int newDeviceIndex, Worksp deviceUISet->m_deviceAPI->getPluginInterface()->deleteSampleSinkPluginInstanceOutput(deviceUISet->m_deviceAPI->getSampleSink()); deviceUISet->m_deviceAPI->clearBuddiesLists(); // clear old API buddies lists - sampleSourceCreate(deviceSetIndex, newDeviceIndex, deviceUISet->m_deviceAPI, deviceUISet); + sampleSinkCreate(deviceSetIndex, newDeviceIndex, deviceUISet); deviceUISet->m_deviceGUI->setWorkspaceIndex(workspace->getIndex()); workspace->addToMdiArea(deviceUISet->m_deviceGUI); @@ -2582,7 +2574,7 @@ void MainWindow::sampleMIMOChange(int deviceSetIndex, int newDeviceIndex, Worksp deviceUISet->m_deviceAPI->resetSamplingDeviceId(); deviceUISet->m_deviceAPI->getPluginInterface()->deleteSampleMIMOPluginInstanceMIMO(deviceUISet->m_deviceAPI->getSampleMIMO()); - sampleSourceCreate(deviceSetIndex, newDeviceIndex, deviceUISet->m_deviceAPI, deviceUISet); + sampleMIMOCreate(deviceSetIndex, newDeviceIndex, deviceUISet); deviceUISet->m_deviceGUI->setWorkspaceIndex(workspace->getIndex()); workspace->addToMdiArea(deviceUISet->m_deviceGUI); diff --git a/sdrgui/mainwindow.h b/sdrgui/mainwindow.h index bbd510ab3..772b47399 100644 --- a/sdrgui/mainwindow.h +++ b/sdrgui/mainwindow.h @@ -153,19 +153,16 @@ private: void sampleSourceCreate( int deviceSetIndex, int deviceIndex, - DeviceAPI *deviceAPI, DeviceUISet *deviceUISet ); void sampleSinkCreate( int deviceSetIndex, int deviceIndex, - DeviceAPI *deviceAPI, DeviceUISet *deviceUISet ); void sampleMIMOCreate( int deviceSetIndex, int deviceIndex, - DeviceAPI *deviceAPI, DeviceUISet *deviceUISet ); void deleteFeature(int featureSetIndex, int featureIndex); From 296c71ce5af838b9e3971c662ef17ca0dbf0fddb Mon Sep 17 00:00:00 2001 From: f4exb Date: Fri, 15 Apr 2022 06:10:45 +0200 Subject: [PATCH 025/115] Massive UI revamping (v7): save/restore all geometries in device set presets --- sdrgui/device/deviceuiset.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/sdrgui/device/deviceuiset.cpp b/sdrgui/device/deviceuiset.cpp index 7dd0e5ced..e04f387ed 100644 --- a/sdrgui/device/deviceuiset.cpp +++ b/sdrgui/device/deviceuiset.cpp @@ -186,7 +186,11 @@ void DeviceUISet::loadDeviceSetSettings( Workspace *currentWorkspace ) { + qDebug("DeviceUISet::loadDeviceSetSettings: preset: [%s, %s]", + qPrintable(preset->getGroup()), qPrintable(preset->getDescription())); m_spectrumGUI->deserialize(preset->getSpectrumConfig()); + m_mainSpectrumGUI->restoreGeometry(preset->getSpectrumGeometry()); + m_deviceGUI->restoreGeometry(preset->getDeviceGeometry()); m_deviceAPI->loadSamplingDeviceSettings(preset); if (!preset->getShowSpectrum()) { @@ -204,9 +208,12 @@ void DeviceUISet::loadDeviceSetSettings( void DeviceUISet::saveDeviceSetSettings(Preset* preset) const { + qDebug("DeviceUISet::saveDeviceSetSettings: preset: [%s, %s]", + qPrintable(preset->getGroup()), qPrintable(preset->getDescription())); preset->setSpectrumConfig(m_spectrumGUI->serialize()); preset->setSpectrumWorkspaceIndex(m_mainSpectrumGUI->getWorkspaceIndex()); preset->setSpectrumGeometry(m_mainSpectrumGUI->saveGeometry()); + preset->setDeviceGeometry(m_deviceGUI->saveGeometry()); preset->setShowSpectrum(m_spectrumGUI->isVisible()); preset->setSelectedDevice(Preset::SelectedDevice{ m_deviceAPI->getSamplingDeviceId(), From a7ca65028635bd8ba1aea2bf08f36e466269fb1f Mon Sep 17 00:00:00 2001 From: f4exb Date: Fri, 15 Apr 2022 10:04:24 +0200 Subject: [PATCH 026/115] Massive UI revamping (v7): MainWindow: first round of cleanup --- sdrbase/settings/mainsettings.cpp | 2 - sdrgui/mainwindow.cpp | 588 +----------------------------- sdrgui/mainwindow.h | 46 +-- 3 files changed, 31 insertions(+), 605 deletions(-) diff --git a/sdrbase/settings/mainsettings.cpp b/sdrbase/settings/mainsettings.cpp index 560b7ecd1..85d2dbdd7 100644 --- a/sdrbase/settings/mainsettings.cpp +++ b/sdrbase/settings/mainsettings.cpp @@ -143,8 +143,6 @@ void MainSettings::save() const QSettings s; s.setValue("preferences", qCompress(m_preferences.serialize()).toBase64()); - // s.setValue("current", qCompress(m_workingPreset.serialize()).toBase64()); - // s.setValue("current-featureset", qCompress(m_workingFeatureSetPreset.serialize()).toBase64()); s.setValue("current-configuration", qCompress(m_workingConfiguration.serialize()).toBase64()); if (m_audioDeviceManager) { diff --git a/sdrgui/mainwindow.cpp b/sdrgui/mainwindow.cpp index 1f11dbd68..89e298a3d 100644 --- a/sdrgui/mainwindow.cpp +++ b/sdrgui/mainwindow.cpp @@ -138,7 +138,6 @@ MainWindow::MainWindow(qtwebapp::LoggerWithFile *logger, const MainParser& parse splash->showStatusMessage("starting...", Qt::white); splash->showStatusMessage("starting...", Qt::white); - // ui->setupUi(this); setWindowIcon(QIcon(":/sdrangel_icon.png")); createMenuBar(); createStatusBar(); @@ -150,52 +149,6 @@ MainWindow::MainWindow(qtwebapp::LoggerWithFile *logger, const MainParser& parse setCorner(Qt::TopRightCorner, Qt::RightDockWidgetArea); setCorner(Qt::BottomRightCorner, Qt::RightDockWidgetArea); - // work around broken Qt dock widget ordering - // removeDockWidget(ui->inputViewDock); - // removeDockWidget(ui->spectraControlDock); - // removeDockWidget(ui->presetDock); - // removeDockWidget(ui->commandsDock); - // removeDockWidget(ui->channelDock); - // removeDockWidget(ui->featureDock); - // addDockWidget(Qt::LeftDockWidgetArea, ui->inputViewDock); - // addDockWidget(Qt::LeftDockWidgetArea, ui->spectraControlDock); - // addDockWidget(Qt::LeftDockWidgetArea, ui->presetDock); - // addDockWidget(Qt::LeftDockWidgetArea, ui->commandsDock); - // tabifyDockWidget(ui->presetDock, ui->commandsDock); - // addDockWidget(Qt::RightDockWidgetArea, ui->channelDock); - // addDockWidget(Qt::RightDockWidgetArea, ui->featureDock); - - // ui->inputViewDock->show(); - // ui->spectraControlDock->show(); - // ui->presetDock->show(); - // ui->commandsDock->show(); - // ui->channelDock->show(); - // ui->featureDock->show(); - - // m_spectrumToggleViewAction = new QAction(tr("Spectrum display")); - // m_spectrumToggleViewAction->setCheckable(true); - // m_spectrumToggleViewAction->setChecked(true); - // connect(m_spectrumToggleViewAction, SIGNAL(toggled(bool)), this, SLOT(toggleSpectrumView(bool))); - - // ui->menu_Window->addAction(ui->inputViewDock->toggleViewAction()); - // ui->menu_Window->addAction(ui->spectraControlDock->toggleViewAction()); - // ui->menu_Window->addAction(m_spectrumToggleViewAction); - // ui->menu_Window->addAction(ui->presetDock->toggleViewAction()); - // ui->menu_Window->addAction(ui->commandsDock->toggleViewAction()); - // ui->menu_Window->addAction(ui->channelDock->toggleViewAction()); - // ui->menu_Window->addAction(ui->featureDock->toggleViewAction()); - - // ui->spectraControlDock->setStyleSheet("QAbstractButton#qt_dockwidget_closebutton{qproperty-toolTip: \"Close\";}"); - // ui->spectraControlDock->setStyleSheet("QAbstractButton#qt_dockwidget_floatbutton{qproperty-toolTip: \"Dock/undock\";}"); - // ui->presetDock->setStyleSheet("QAbstractButton#qt_dockwidget_closebutton{qproperty-toolTip: \"Close\";}"); - // ui->presetDock->setStyleSheet("QAbstractButton#qt_dockwidget_floatbutton{qproperty-toolTip: \"Dock/undock\";}"); - - // ui->tabInputsView->setStyleSheet("QWidget { background: rgb(50,50,50); } " - // "QToolButton::checked { background: rgb(128,70,0); } " - // "QComboBox::item:selected { color: rgb(255,140,0); } " - // "QTabWidget::pane { border: 1px solid #C06900; } " - // "QTabBar::tab:selected { background: rgb(128,70,0); }"); - connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleMessages()), Qt::QueuedConnection); connect(&m_statusTimer, SIGNAL(timeout()), this, SLOT(updateStatus())); @@ -238,38 +191,13 @@ MainWindow::MainWindow(qtwebapp::LoggerWithFile *logger, const MainParser& parse m_pluginManager->loadPluginsNonDiscoverable(m_mainCore->m_settings.getDeviceUserArgs()); splash->showStatusMessage("Add unique feature set...", Qt::white); - // QStringList featureNames; - // m_pluginManager->listFeatures(featureNames); - // ui->featureDock->addAvailableFeatures(featureNames); - addFeatureSet(); - // ui->featureDock->setFeatureUISet(m_featureUIs[0]); - // ui->featureDock->setPresets(m_mainCore->m_settings.getFeatureSetPresets()); - // ui->featureDock->setPluginAPI(m_pluginManager->getPluginAPI()); - - // splash->showStatusMessage("load last device or file input...", Qt::white); - // qDebug() << "MainWindow::MainWindow: select SampleSource from settings or default (file input)..."; - // qDebug() << "MainWindow::MainWindow: look for" - // << m_mainCore->m_settings.getSourceDevice() - // << "at index" << m_mainCore->m_settings.getSourceIndex() - // << "and item index" << m_mainCore->m_settings.getSourceItemIndex(); - - // int deviceIndex = DeviceEnumerator::instance()->getRxSamplingDeviceIndex( - // m_mainCore->m_settings.getSourceDevice(), - // m_mainCore->m_settings.getSourceIndex(), - // m_mainCore->m_settings.getSourceItemIndex()); - // sampleSourceAdd(deviceIndex); // add the first device set with file input device as default if device in settings is not enumerated - // m_deviceUIs.back()->m_deviceAPI->setBuddyLeader(true); // the first device is always the leader - tabChannelsIndexChanged(); // force channel selection list update - + addFeatureSet(); // Create the uniuefeature set m_apiAdapter = new WebAPIAdapter(); if (!parser.getScratch()) { splash->showStatusMessage("load current configuration...", Qt::white); qDebug() << "MainWindow::MainWindow: load current configuration..."; - - // loadDeviceSetPresetSettings(m_mainCore->m_settings.getWorkingPreset(), 0); - // loadFeatureSetPresetSettings(m_mainCore->m_settings.getWorkingFeatureSetPreset(), 0); loadConfiguration(m_mainCore->m_settings.getWorkingConfiguration()); } else @@ -277,18 +205,7 @@ MainWindow::MainWindow(qtwebapp::LoggerWithFile *logger, const MainParser& parse qDebug() << "MainWindow::MainWindow: scratch mode: do not load current configuration"; } - // splash->showStatusMessage("update preset controls...", Qt::white); - // qDebug() << "MainWindow::MainWindow: update preset controls..."; - - // updatePresetControls(); - splash->showStatusMessage("finishing...", Qt::white); - // connect(ui->tabInputsView, SIGNAL(currentChanged(int)), this, SLOT(tabInputViewIndexChanged())); - // connect(ui->tabChannels, SIGNAL(currentChanged(int)), this, SLOT(tabChannelsIndexChanged())); - // connect(ui->channelDock, SIGNAL(addChannel(int)), this, SLOT(channelAddClicked(int))); - // connect(ui->inputViewDock, SIGNAL(deviceChanged(int, int, int)), this, SLOT(samplingDeviceChanged(int, int, int))); - // connect(ui->tabFeatures, SIGNAL(currentChanged(int)), this, SLOT(tabFeaturesIndexChanged())); - // connect(ui->featureDock, SIGNAL(addFeature(int)), this, SLOT(featureAddClicked(int))); QString applicationDirPath = qApp->applicationDirPath(); @@ -300,7 +217,6 @@ MainWindow::MainWindow(qtwebapp::LoggerWithFile *logger, const MainParser& parse } #endif - // ui->featureDock->setWebAPIAdapter(m_apiAdapter); m_requestMapper = new WebAPIRequestMapper(this); m_requestMapper->setAdapter(m_apiAdapter); m_apiHost = parser.getServerAddress(); @@ -314,17 +230,6 @@ MainWindow::MainWindow(qtwebapp::LoggerWithFile *logger, const MainParser& parse m_dspEngine->setMIMOSupport(true); - // if (!parser.getMIMOSupport()) { - // ui->menu_Devices->removeAction(ui->action_sampleMIMOAdd); - // } - -#ifdef __APPLE__ - ui->menuPreferences->removeAction(ui->action_AMBE); -#endif -#if not defined(HAS_LIMERFEUSB) - ui->menuPreferences->removeAction(ui->action_LimeRFE); -#endif - delete splash; // Restore window size and position @@ -349,13 +254,8 @@ MainWindow::~MainWindow() delete m_dateTimeWidget; delete m_showSystemWidget; - // disconnect(ui->tabFeatures, SIGNAL(currentChanged(int)), this, SLOT(tabFeaturesIndexChanged())); - removeAllFeatureSets(); - // delete ui; - // delete m_spectrumToggleViewAction; - delete m_commandKeyReceiver; for (const auto& workspace : m_workspaces) { @@ -529,7 +429,7 @@ void MainWindow::sampleSourceCreate( deviceGUI, &DeviceGUI::deviceChange, this, - [=](int newDeviceIndex){ this->sampleDeviceChangeHandler(deviceGUI, newDeviceIndex); } + [=](int newDeviceIndex){ this->samplingDeviceChangeHandler(deviceGUI, newDeviceIndex); } ); QObject::connect( deviceGUI, @@ -744,7 +644,7 @@ void MainWindow::sampleSinkCreate( deviceGUI, &DeviceGUI::deviceChange, this, - [=](int newDeviceIndex){ this->sampleDeviceChangeHandler(deviceGUI, newDeviceIndex); } + [=](int newDeviceIndex){ this->samplingDeviceChangeHandler(deviceGUI, newDeviceIndex); } ); QObject::connect( deviceGUI, @@ -933,7 +833,7 @@ void MainWindow::sampleMIMOCreate( deviceGUI, &DeviceGUI::deviceChange, this, - [=](int newDeviceIndex){ this->sampleDeviceChangeHandler(deviceGUI, newDeviceIndex); } + [=](int newDeviceIndex){ this->samplingDeviceChangeHandler(deviceGUI, newDeviceIndex); } ); QObject::connect( deviceGUI, @@ -1076,7 +976,7 @@ void MainWindow::removeDeviceSet(int deviceSetIndex) emit m_mainCore->deviceSetRemoved(deviceSetIndex); } -void MainWindow::removeLastDevice() +void MainWindow::removeLastDeviceSet() { int removedDeviceSetIndex = m_deviceUIs.size() - 1; @@ -1200,25 +1100,7 @@ void MainWindow::loadSettings() m_mainCore->m_settings.load(); m_mainCore->m_settings.sortPresets(); - // int middleIndex = m_mainCore->m_settings.getPresetCount() / 2; - // QTreeWidgetItem *treeItem; - - // for (int i = 0; i < m_mainCore->m_settings.getPresetCount(); ++i) - // { - // treeItem = addPresetToTree(m_mainCore->m_settings.getPreset(i)); - - // if (i == middleIndex) { - // ui->presetTree->setCurrentItem(treeItem); - // } - // } - m_mainCore->m_settings.sortCommands(); - - // for (int i = 0; i < m_mainCore->m_settings.getCommandCount(); ++i) - // { - // treeItem = addCommandToTree(m_mainCore->m_settings.getCommand(i)); - // } - m_mainCore->setLoggingOptions(); } @@ -1240,9 +1122,6 @@ void MainWindow::loadDeviceSetPresetSettings(const Preset* preset, int deviceSet // if (!preset->getLayout().isEmpty()) { // restoreState(preset->getLayout()); // } - - // tabifyDockWidget(ui->presetDock, ui->commandsDock); // override this setting - // ui->presetDock->raise(); } void MainWindow::saveDeviceSetPresetSettings(Preset* preset, int deviceSetIndex) @@ -1316,7 +1195,7 @@ void MainWindow::loadConfiguration(const Configuration *configuration, bool from // Device sets while (m_deviceUIs.size() > 0) { - removeLastDevice(); + removeLastDeviceSet(); } // Features m_featureUIs[0]->freeFeatures(); @@ -1554,12 +1433,16 @@ void MainWindow::createMenuBar() QAction *fftAction = preferencesMenu->addAction("FFT"); fftAction->setToolTip("Set FFT cache"); QObject::connect(fftAction, &QAction::triggered, this, &MainWindow::on_action_FFT_triggered); +#ifndef __APPLE__ QAction *ambeAction = preferencesMenu->addAction("AMBE"); ambeAction->setToolTip("AMBE options"); QObject::connect(ambeAction, &QAction::triggered, this, &MainWindow::on_action_AMBE_triggered); +#endif +#if defined(HAS_LIMERFEUSB) QAction *limeRFEAction = preferencesMenu->addAction("Lime RFE"); limeRFEAction->setToolTip("Lime RFE options"); QObject::connect(limeRFEAction, &QAction::triggered, this, &MainWindow::on_action_LimeRFE_triggered); +#endif QMenu *devicesMenu = preferencesMenu->addMenu("Devices"); QAction *userArgumentsAction = devicesMenu->addAction("User arguments"); userArgumentsAction->setToolTip("Device custom user arguments"); @@ -1612,38 +1495,18 @@ void MainWindow::closeEvent(QCloseEvent *closeEvent) s.setValue("mainWindowGeometry", qCompress(saveGeometry()).toBase64()); s.setValue("mainWindowState", qCompress(saveState()).toBase64()); - // saveDeviceSetPresetSettings(m_mainCore->m_settings.getWorkingPreset(), 0); - // saveFeatureSetPresetSettings(m_mainCore->m_settings.getWorkingFeatureSetPreset(), 0); saveConfiguration(m_mainCore->m_settings.getWorkingConfiguration()); m_mainCore->m_settings.save(); while (m_deviceUIs.size() > 0) { - removeLastDevice(); + removeLastDeviceSet(); } closeEvent->accept(); } -void MainWindow::updatePresetControls() -{ - // ui->presetTree->resizeColumnToContents(0); - - // if(ui->presetTree->currentItem() != 0) - // { - // ui->presetDelete->setEnabled(true); - // ui->presetLoad->setEnabled(true); - // } - // else - // { - // ui->presetDelete->setEnabled(false); - // ui->presetLoad->setEnabled(false); - // } -} - void MainWindow::applySettings() { - // loadDeviceSetPresetSettings(m_mainCore->m_settings.getWorkingPreset(), 0); - // loadFeatureSetPresetSettings(m_mainCore->m_settings.getWorkingFeatureSetPreset(), 0); loadConfiguration(m_mainCore->m_settings.getWorkingConfiguration()); m_mainCore->m_settings.sortPresets(); @@ -1717,29 +1580,6 @@ bool MainWindow::handleMessage(const Message& cmd) { MainCore::MsgDeletePreset& notif = (MainCore::MsgDeletePreset&) cmd; const Preset *presetToDelete = notif.getPreset(); - - // remove preset from tree - // for (int ig = 0; ig < ui->presetTree->topLevelItemCount(); ig++) - // { - // QTreeWidgetItem *groupItem = ui->presetTree->topLevelItem(ig); - // if (groupItem->text(0) == presetToDelete->getGroup()) - // { - // for (int ip = 0; ip < groupItem->childCount(); ip++) - // { - // QTreeWidgetItem *presetItem = groupItem->child(ip); - // const Preset* preset = qvariant_cast(presetItem->data(0, Qt::UserRole)); - - // if ((preset->getGroup() == presetToDelete->getGroup()) && - // (preset->getCenterFrequency() == presetToDelete->getCenterFrequency()) && - // (preset->getDescription() == presetToDelete->getDescription()) && - // (preset->getPresetType() == presetToDelete->getPresetType())) - // { - // groupItem->takeChild(ip); - // } - // } - // } - // } - // remove preset from settings m_mainCore->m_settings.deletePreset(presetToDelete); return true; @@ -1773,7 +1613,7 @@ bool MainWindow::handleMessage(const Message& cmd) else if (MainCore::MsgRemoveLastDeviceSet::match(cmd)) { if (m_deviceUIs.size() > 1) { - removeLastDevice(); + removeLastDeviceSet(); } return true; @@ -2019,319 +1859,13 @@ void MainWindow::commandKeysDisconnect(QObject *object, const char *slot) ); } -void MainWindow::on_presetSave_clicked() -{ - // QStringList groups; - // QString group; - // QString description = ""; - - // for(int i = 0; i < ui->presetTree->topLevelItemCount(); i++) { - // groups.append(ui->presetTree->topLevelItem(i)->text(0)); - // } - - // QTreeWidgetItem* item = ui->presetTree->currentItem(); - - // if(item != 0) - // { - // if(item->type() == PGroup) { - // group = item->text(0); - // } else if(item->type() == PItem) { - // group = item->parent()->text(0); - // description = item->text(0); - // } - // } - - // AddPresetDialog dlg(groups, group, this); - - // if (description.length() > 0) { - // dlg.setDescription(description); - // } - - // if(dlg.exec() == QDialog::Accepted) { - // Preset* preset = m_mainCore->m_settings.newPreset(dlg.group(), dlg.description()); - // saveDeviceSetPresetSettings(preset, ui->tabInputsView->currentIndex()); - - // ui->presetTree->setCurrentItem(addPresetToTree(preset)); - // } - - // m_mainCore->m_settings.sortPresets(); -} - -void MainWindow::on_presetUpdate_clicked() -{ - // QTreeWidgetItem* item = ui->presetTree->currentItem(); - // const Preset* changedPreset = 0; - - // if(item != 0) - // { - // if(item->type() == PItem) - // { - // const Preset* preset = qvariant_cast(item->data(0, Qt::UserRole)); - - // if (preset != 0) - // { - // Preset* preset_mod = const_cast(preset); - // saveDeviceSetPresetSettings(preset_mod, ui->tabInputsView->currentIndex()); - // changedPreset = preset; - // } - // } - // } - - // m_mainCore->m_settings.sortPresets(); - // ui->presetTree->clear(); - - // for (int i = 0; i < m_mainCore->m_settings.getPresetCount(); ++i) - // { - // QTreeWidgetItem *item_x = addPresetToTree(m_mainCore->m_settings.getPreset(i)); - // const Preset* preset_x = qvariant_cast(item_x->data(0, Qt::UserRole)); - // if (changedPreset && (preset_x == changedPreset)) { // set cursor on changed preset - // ui->presetTree->setCurrentItem(item_x); - // } - // } -} - -void MainWindow::on_presetEdit_clicked() -{ - // QTreeWidgetItem* item = ui->presetTree->currentItem(); - // QStringList groups; - // bool change = false; - // const Preset *changedPreset = 0; - // QString newGroupName; - - // for(int i = 0; i < ui->presetTree->topLevelItemCount(); i++) { - // groups.append(ui->presetTree->topLevelItem(i)->text(0)); - // } - - // if(item != 0) - // { - // if (item->type() == PItem) - // { - // const Preset* preset = qvariant_cast(item->data(0, Qt::UserRole)); - // AddPresetDialog dlg(groups, preset->getGroup(), this); - // dlg.setDescription(preset->getDescription()); - - // if (dlg.exec() == QDialog::Accepted) - // { - // Preset* preset_mod = const_cast(preset); - // preset_mod->setGroup(dlg.group()); - // preset_mod->setDescription(dlg.description()); - // change = true; - // changedPreset = preset; - // } - // } - // else if (item->type() == PGroup) - // { - // AddPresetDialog dlg(groups, item->text(0), this); - // dlg.showGroupOnly(); - // dlg.setDialogTitle("Edit preset group"); - - // if (dlg.exec() == QDialog::Accepted) - // { - // m_mainCore->m_settings.renamePresetGroup(item->text(0), dlg.group()); - // newGroupName = dlg.group(); - // change = true; - // } - // } - // } - - // if (change) - // { - // m_mainCore->m_settings.sortPresets(); - // ui->presetTree->clear(); - - // for (int i = 0; i < m_mainCore->m_settings.getPresetCount(); ++i) - // { - // QTreeWidgetItem *item_x = addPresetToTree(m_mainCore->m_settings.getPreset(i)); - // const Preset* preset_x = qvariant_cast(item_x->data(0, Qt::UserRole)); - // if (changedPreset && (preset_x == changedPreset)) { // set cursor on changed preset - // ui->presetTree->setCurrentItem(item_x); - // } - // } - - // if (!changedPreset) // on group name change set cursor on the group that has been changed - // { - // for(int i = 0; i < ui->presetTree->topLevelItemCount(); i++) - // { - // QTreeWidgetItem* item = ui->presetTree->topLevelItem(i); - - // if (item->text(0) == newGroupName) { - // ui->presetTree->setCurrentItem(item); - // } - // } - // } - // } -} - -void MainWindow::on_presetExport_clicked() -{ - // QTreeWidgetItem* item = ui->presetTree->currentItem(); - - // if(item != 0) { - // if(item->type() == PItem) - // { - // const Preset* preset = qvariant_cast(item->data(0, Qt::UserRole)); - // QString base64Str = preset->serialize().toBase64(); - // QString fileName = QFileDialog::getSaveFileName(this, - // tr("Open preset export file"), ".", tr("Preset export files (*.prex)"), 0, QFileDialog::DontUseNativeDialog); - - // if (fileName != "") - // { - // QFileInfo fileInfo(fileName); - - // if (fileInfo.suffix() != "prex") { - // fileName += ".prex"; - // } - - // QFile exportFile(fileName); - - // if (exportFile.open(QIODevice::WriteOnly | QIODevice::Text)) - // { - // QTextStream outstream(&exportFile); - // outstream << base64Str; - // exportFile.close(); - // } - // else - // { - // QMessageBox::information(this, tr("Message"), tr("Cannot open file for writing")); - // } - // } - // } - // } -} - -void MainWindow::on_presetImport_clicked() -{ - // QTreeWidgetItem* item = ui->presetTree->currentItem(); - - // if(item != 0) - // { - // QString group; - - // if (item->type() == PGroup) { - // group = item->text(0); - // } else if (item->type() == PItem) { - // group = item->parent()->text(0); - // } else { - // return; - // } - - // QString fileName = QFileDialog::getOpenFileName(this, - // tr("Open preset export file"), ".", tr("Preset export files (*.prex)"), 0, QFileDialog::DontUseNativeDialog); - - // if (fileName != "") - // { - // QFile exportFile(fileName); - - // if (exportFile.open(QIODevice::ReadOnly | QIODevice::Text)) - // { - // QByteArray base64Str; - // QTextStream instream(&exportFile); - // instream >> base64Str; - // exportFile.close(); - - // Preset* preset = m_mainCore->m_settings.newPreset("", ""); - // preset->deserialize(QByteArray::fromBase64(base64Str)); - // preset->setGroup(group); // override with current group - - // ui->presetTree->setCurrentItem(addPresetToTree(preset)); - // } - // else - // { - // QMessageBox::information(this, tr("Message"), tr("Cannot open file for reading")); - // } - // } - // } -} - void MainWindow::on_action_saveAll_triggered() { - // saveDeviceSetPresetSettings(m_mainCore->m_settings.getWorkingPreset(), ui->tabInputsView->currentIndex()); - // saveFeatureSetPresetSettings(m_mainCore->m_settings.getWorkingFeatureSetPreset(), ui->tabFeatures->currentIndex()); saveConfiguration(m_mainCore->m_settings.getWorkingConfiguration()); m_mainCore->m_settings.save(); QMessageBox::information(this, tr("Done"), tr("All curent settings saved")); } -void MainWindow::on_presetLoad_clicked() -{ - // qDebug() << "MainWindow::on_presetLoad_clicked"; - - // QTreeWidgetItem* item = ui->presetTree->currentItem(); - - // if(item == 0) - // { - // qDebug("MainWindow::on_presetLoad_clicked: item null"); - // updatePresetControls(); - // return; - // } - - // const Preset* preset = qvariant_cast(item->data(0, Qt::UserRole)); - - // if(preset == 0) - // { - // qDebug("MainWindow::on_presetLoad_clicked: preset null"); - // return; - // } - - // loadDeviceSetPresetSettings(preset, ui->tabInputsView->currentIndex()); -} - -void MainWindow::on_presetDelete_clicked() -{ - // QTreeWidgetItem* item = ui->presetTree->currentItem(); - - // if (item == 0) - // { - // updatePresetControls(); - // return; - // } - // else - // { - // if (item->type() == PItem) - // { - // const Preset* preset = qvariant_cast(item->data(0, Qt::UserRole)); - - // if (preset) - // { - // if(QMessageBox::question(this, tr("Delete Preset"), tr("Do you want to delete preset '%1'?").arg(preset->getDescription()), QMessageBox::No | QMessageBox::Yes, QMessageBox::No) == QMessageBox::Yes) { - // delete item; - // m_mainCore->m_settings.deletePreset(preset); - // } - // } - // } - // else if (item->type() == PGroup) - // { - // if (QMessageBox::question(this, - // tr("Delete preset group"), - // tr("Do you want to delete preset group '%1'?") - // .arg(item->text(0)), QMessageBox::No | QMessageBox::Yes, QMessageBox::No) == QMessageBox::Yes) - // { - // m_mainCore->m_settings.deletePresetGroup(item->text(0)); - - // ui->presetTree->clear(); - - // for (int i = 0; i < m_mainCore->m_settings.getPresetCount(); ++i) { - // addPresetToTree(m_mainCore->m_settings.getPreset(i)); - // } - // } - // } - // } -} - -void MainWindow::on_presetTree_currentItemChanged(QTreeWidgetItem *current, QTreeWidgetItem *previous) -{ - (void) current; - (void) previous; - // updatePresetControls(); -} - -void MainWindow::on_presetTree_itemActivated(QTreeWidgetItem *item, int column) -{ - (void) item; - (void) column; - // on_presetLoad_clicked(); -} - void MainWindow::on_action_Quick_Start_triggered() { QDesktopServices::openUrl(QUrl("https://github.com/f4exb/sdrangel/wiki/Quick-start")); @@ -2476,7 +2010,7 @@ void MainWindow::on_action_LimeRFE_triggered() #endif } -void MainWindow::sampleDeviceChangeHandler(DeviceGUI *deviceGUI, int newDeviceIndex) +void MainWindow::samplingDeviceChangeHandler(DeviceGUI *deviceGUI, int newDeviceIndex) { int deviceType = (int) deviceGUI->getDeviceType(); int deviceSetIndex = deviceGUI->getIndex(); @@ -2687,12 +2221,9 @@ void MainWindow::channelAddClicked(Workspace *workspace, int deviceSetIndex, int void MainWindow::featureAddClicked(Workspace *workspace, int featureIndex) { qDebug("MainWindow::featureAddClicked: W%d feature at %d", workspace->getIndex(), featureIndex); - // // Do it in the currently selected source tab - int currentFeatureTabIndex = 0; - // if (currentFeatureTabIndex >= 0) - // { - FeatureUISet *featureUISet = m_featureUIs[currentFeatureTabIndex]; + int currentFeatureSetIndex = 0; // Do it in the unique set + FeatureUISet *featureUISet = m_featureUIs[currentFeatureSetIndex]; qDebug("MainWindow::featureAddClicked: m_apiAdapter: %p", m_apiAdapter); PluginAPI::FeatureRegistrations *featureRegistrations = m_pluginManager->getFeatureRegistrations(); // Available feature plugins PluginInterface *pluginInterface = (*featureRegistrations)[featureIndex].m_plugin; @@ -2709,7 +2240,6 @@ void MainWindow::featureAddClicked(Workspace *workspace, int featureIndex) this, [=](int wsIndexDest){ this->featureMove(gui, wsIndexDest); } ); - // } } void MainWindow::featureMove(FeatureGUI *gui, int wsIndexDestnation) @@ -2824,20 +2354,6 @@ void MainWindow::openDeviceSetPresetsDialog(QPoint p, DeviceGUI *deviceGUI) dialog.populateTree((int) deviceGUI->getDeviceType()); dialog.move(p); dialog.exec(); - - // if (dialog.wasPresetLoaded()) - // { - // for (int i = 0; i < m_featureUIs[0]->getNumberOfFeatures(); i++) - // { - // FeatureGUI *gui = m_featureUIs[0]->getFeatureGuiAt(i); - // QObject::connect( - // gui, - // &FeatureGUI::moveToWorkspace, - // this, - // [=](int wsIndexDest){ this->featureMove(gui, wsIndexDest); } - // ); - // } - // } } void MainWindow::deleteFeature(int featureSetIndex, int featureIndex) @@ -2855,80 +2371,6 @@ void MainWindow::on_action_About_triggered() dlg.exec(); } -void MainWindow::on_action_removeLastDevice_triggered() -{ - if (m_deviceUIs.size() > 1) - { - removeLastDevice(); - } -} - -void MainWindow::on_action_addFeatureSet_triggered() -{ - addFeatureSet(); -} - -void MainWindow::on_action_removeLastFeatureSet_triggered() -{ - if (m_featureUIs.size() > 1) { - removeFeatureSet(m_featureUIs.size() - 1); - } -} - -void MainWindow::tabInputViewIndexChanged() -{ - // int inputViewIndex = ui->tabInputsView->currentIndex(); - - // if (inputViewIndex >= 0) { - // ui->inputViewDock->setCurrentTabIndex(inputViewIndex); - // } - - // if ((inputViewIndex >= 0) && (m_mainCore->m_masterTabIndex >= 0) && (inputViewIndex != m_mainCore->m_masterTabIndex)) - // { - // DeviceUISet *deviceUI = m_deviceUIs[inputViewIndex]; - // DeviceUISet *lastdeviceUI = m_deviceUIs[m_mainCore->m_masterTabIndex]; - // lastdeviceUI->m_mainWindowState = saveState(); - // restoreState(deviceUI->m_mainWindowState); - // m_mainCore->m_masterTabIndex = inputViewIndex; - // } - - // ui->tabSpectra->setCurrentIndex(inputViewIndex); - // ui->tabChannels->setCurrentIndex(inputViewIndex); - // ui->tabSpectraGUI->setCurrentIndex(inputViewIndex); -} - -void MainWindow::tabChannelsIndexChanged() -{ - // int channelsTabIndex = ui->tabChannels->currentIndex(); - - // if (channelsTabIndex >= 0) - // { - // DeviceUISet *deviceUI = m_deviceUIs[channelsTabIndex]; - // QList channelNames; - // ui->channelDock->resetAvailableChannels(); - - // if (deviceUI->m_deviceSourceEngine) // source device - // { - // m_pluginManager->listRxChannels(channelNames); - // ui->channelDock->addAvailableChannels(channelNames); - // } - // else if (deviceUI->m_deviceSinkEngine) // sink device - // { - // m_pluginManager->listTxChannels(channelNames); - // ui->channelDock->addAvailableChannels(channelNames); - // } - // else if (deviceUI->m_deviceMIMOEngine) // MIMO device - // { - // m_pluginManager->listMIMOChannels(channelNames); - // ui->channelDock->addAvailableChannels(channelNames); - // m_pluginManager->listRxChannels(channelNames); - // ui->channelDock->addAvailableChannels(channelNames); - // m_pluginManager->listTxChannels(channelNames); - // ui->channelDock->addAvailableChannels(channelNames); - // } - // } -} - void MainWindow::updateStatus() { m_dateTimeWidget->setText(QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss t")); diff --git a/sdrgui/mainwindow.h b/sdrgui/mainwindow.h index 772b47399..d3b407054 100644 --- a/sdrgui/mainwindow.h +++ b/sdrgui/mainwindow.h @@ -137,11 +137,10 @@ private: void createMenuBar(); void createStatusBar(); void closeEvent(QCloseEvent*); - void updatePresetControls(); void applySettings(); void removeDeviceSet(int deviceSetIndex); - void removeLastDevice(); + void removeLastDeviceSet(); void addFeatureSet(); void removeFeatureSet(unsigned int featureSetIndex); void removeAllFeatureSets(); @@ -172,28 +171,9 @@ private: private slots: void handleMessages(); void handleWorkspaceVisibility(Workspace *workspace, bool visibility); - void updateStatus(); - void addWorkspace(); - void viewAllWorkspaces(); - void removeEmptyWorkspaces(); - void loadConfiguration(const Configuration *configuration, bool fromDialog = false); - void saveConfiguration(Configuration *configuration); - void sampleSourceAdd(Workspace *deviceWorkspace, Workspace *spectrumWorkspace, int deviceIndex); - void sampleSinkAdd(Workspace *workspace, Workspace *spectrumWorkspace, int deviceIndex); - void sampleMIMOAdd(Workspace *workspace, Workspace *spectrumWorkspace, int deviceIndex); - void sampleDeviceChangeHandler(DeviceGUI *deviceGUI, int newDeviceIndex); void on_action_View_Fullscreen_toggled(bool checked); - void on_presetSave_clicked(); - void on_presetUpdate_clicked(); - void on_presetEdit_clicked(); - void on_presetExport_clicked(); - void on_presetImport_clicked(); void on_action_saveAll_triggered(); - void on_presetLoad_clicked(); - void on_presetDelete_clicked(); - void on_presetTree_currentItemChanged(QTreeWidgetItem *current, QTreeWidgetItem *previous); - void on_presetTree_itemActivated(QTreeWidgetItem *item, int column); void on_action_Configurations_triggered(); void on_action_Audio_triggered(); void on_action_Logging_triggered(); @@ -203,6 +183,21 @@ private slots: void on_action_My_Position_triggered(); void on_action_DeviceUserArguments_triggered(); void on_action_commands_triggered(); + void on_action_Quick_Start_triggered(); + void on_action_Main_Window_triggered(); + void on_action_Loaded_Plugins_triggered(); + void on_action_About_triggered(); + + void updateStatus(); + void addWorkspace(); + void viewAllWorkspaces(); + void removeEmptyWorkspaces(); + void loadConfiguration(const Configuration *configuration, bool fromDialog = false); + void saveConfiguration(Configuration *configuration); + void sampleSourceAdd(Workspace *deviceWorkspace, Workspace *spectrumWorkspace, int deviceIndex); + void sampleSinkAdd(Workspace *workspace, Workspace *spectrumWorkspace, int deviceIndex); + void sampleMIMOAdd(Workspace *workspace, Workspace *spectrumWorkspace, int deviceIndex); + void samplingDeviceChangeHandler(DeviceGUI *deviceGUI, int newDeviceIndex); void channelAddClicked(Workspace *workspace, int deviceSetIndex, int channelIndex); void featureAddClicked(Workspace *workspace, int featureIndex); void featureMove(FeatureGUI *gui, int wsIndexDestnation); @@ -212,15 +207,6 @@ private slots: void mainSpectrumShow(MainSpectrumGUI *gui); void showAllChannels(int deviceSetIndex); void openDeviceSetPresetsDialog(QPoint p, DeviceGUI *deviceGUI); - void on_action_Quick_Start_triggered(); - void on_action_Main_Window_triggered(); - void on_action_Loaded_Plugins_triggered(); - void on_action_About_triggered(); - void on_action_removeLastDevice_triggered(); - void on_action_addFeatureSet_triggered(); - void on_action_removeLastFeatureSet_triggered(); - void tabInputViewIndexChanged(); - void tabChannelsIndexChanged(); void commandKeyPressed(Qt::Key key, Qt::KeyboardModifiers keyModifiers, bool release); void fftWisdomProcessFinished(int exitCode, QProcess::ExitStatus exitStatus); }; From e366f5eba6ef5af035e81c965f558cdcee33e1c7 Mon Sep 17 00:00:00 2001 From: f4exb Date: Fri, 15 Apr 2022 18:59:15 +0200 Subject: [PATCH 027/115] Massive UI revamping (v7): SSB demod: fixed AGC time QDIal value change handler connection --- plugins/channelrx/demodssb/ssbdemodgui.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/channelrx/demodssb/ssbdemodgui.cpp b/plugins/channelrx/demodssb/ssbdemodgui.cpp index 2449d299b..7b266b878 100644 --- a/plugins/channelrx/demodssb/ssbdemodgui.cpp +++ b/plugins/channelrx/demodssb/ssbdemodgui.cpp @@ -703,6 +703,7 @@ void SSBDemodGUI::makeUIConnections() QObject::connect(ui->volume, &QDial::valueChanged, this, &SSBDemodGUI::on_volume_valueChanged); QObject::connect(ui->agc, &ButtonSwitch::toggled, this, &SSBDemodGUI::on_agc_toggled); QObject::connect(ui->agcClamping, &ButtonSwitch::toggled, this, &SSBDemodGUI::on_agcClamping_toggled); + QObject::connect(ui->agcTimeLog2, &QDial::valueChanged, this, &SSBDemodGUI::on_agcTimeLog2_valueChanged); QObject::connect(ui->agcPowerThreshold, &QDial::valueChanged, this, &SSBDemodGUI::on_agcPowerThreshold_valueChanged); QObject::connect(ui->agcThresholdGate, &QDial::valueChanged, this, &SSBDemodGUI::on_agcThresholdGate_valueChanged); QObject::connect(ui->audioMute, &QToolButton::toggled, this, &SSBDemodGUI::on_audioMute_toggled); From edd9cb18d7c391d7f993937c047211bad8633fc5 Mon Sep 17 00:00:00 2001 From: f4exb Date: Fri, 15 Apr 2022 18:59:29 +0200 Subject: [PATCH 028/115] Massive UI revamping (v7): duplicate channel --- sdrgui/channel/channelgui.cpp | 16 ++- sdrgui/channel/channelgui.h | 5 +- sdrgui/device/devicegui.h | 2 +- sdrgui/device/deviceuiset.cpp | 18 +++ sdrgui/gui/channeladddialog.h | 2 +- sdrgui/mainwindow.cpp | 215 +++++++++++++++++++++++++++++++--- sdrgui/mainwindow.h | 3 +- 7 files changed, 239 insertions(+), 22 deletions(-) diff --git a/sdrgui/channel/channelgui.cpp b/sdrgui/channel/channelgui.cpp index d7ae3ab46..89f008f96 100644 --- a/sdrgui/channel/channelgui.cpp +++ b/sdrgui/channel/channelgui.cpp @@ -91,6 +91,12 @@ ChannelGUI::ChannelGUI(QWidget *parent) : m_closeButton->setIcon(closeIcon); m_closeButton->setToolTip("Close channel"); + m_duplicateButton = new QPushButton(); + m_duplicateButton->setFixedSize(20, 20); + QIcon m_duplicateIcon(":/duplicate.png"); + m_duplicateButton->setIcon(m_duplicateIcon); + m_duplicateButton->setToolTip("Duplicate channel"); + m_statusFrequency = new QLabel(); // QFont font = m_statusFrequency->font(); // font.setPointSize(8); @@ -135,7 +141,8 @@ ChannelGUI::ChannelGUI(QWidget *parent) : m_bottomLayout = new QHBoxLayout(); m_bottomLayout->setContentsMargins(0, 0, 0, 0); - m_bottomLayout->addWidget(m_statusFrequency ); + m_bottomLayout->addWidget(m_duplicateButton); + m_bottomLayout->addWidget(m_statusFrequency); m_bottomLayout->addWidget(m_statusLabel); m_sizeGripBottomRight = new QSizeGrip(this); m_sizeGripBottomRight->setStyleSheet("QSizeGrip { background-color: rgb(128, 128, 128); width: 10px; height: 10px; }"); @@ -157,6 +164,7 @@ ChannelGUI::ChannelGUI(QWidget *parent) : connect(this, SIGNAL(forceShrink()), this, SLOT(shrinkWindow())); connect(m_hideButton, SIGNAL(clicked()), this, SLOT(hide())); connect(m_closeButton, SIGNAL(clicked()), this, SLOT(close())); + connect(m_duplicateButton, SIGNAL(clicked()), this, SLOT(duplicateChannel())); connect( m_rollupContents, @@ -270,6 +278,12 @@ void ChannelGUI::onWidgetRolled(QWidget *widget, bool show) } } +void ChannelGUI::duplicateChannel() +{ + qDebug("ChannelGUI::duplicateChannel"); + emit duplicateChannelEmitted(); +} + void ChannelGUI::shrinkWindow() { qDebug("ChannelGUI::shrinkWindow"); diff --git a/sdrgui/channel/channelgui.h b/sdrgui/channel/channelgui.h index cc0116fa4..7ab78dc6d 100644 --- a/sdrgui/channel/channelgui.h +++ b/sdrgui/channel/channelgui.h @@ -78,7 +78,7 @@ public: void setIndex(int index); int getIndex() const { return m_channelIndex; } void setDeviceSetIndex(int index); - int getDeviceSetIndex() const { return m_channelIndex; } + int getDeviceSetIndex() const { return m_deviceSetIndex; } void setStatusFrequency(qint64 frequency); void setStatusText(const QString& text); @@ -113,6 +113,7 @@ private: QPushButton *m_shrinkButton; QPushButton *m_hideButton; QPushButton *m_closeButton; + QPushButton *m_duplicateButton; QLabel *m_statusFrequency; QLabel *m_statusLabel; QVBoxLayout *m_layouts; @@ -130,11 +131,13 @@ private slots: void showHelp(); void openMoveToWorkspaceDialog(); void onWidgetRolled(QWidget *widget, bool show); + void duplicateChannel(); signals: void closing(); void moveToWorkspace(int workspaceIndex); void forceShrink(); + void duplicateChannelEmitted(); }; #endif // SDRGUI_CHANNEL_CHANNELGUI_H_ diff --git a/sdrgui/device/devicegui.h b/sdrgui/device/devicegui.h index 595a0a112..aee35e306 100644 --- a/sdrgui/device/devicegui.h +++ b/sdrgui/device/devicegui.h @@ -141,7 +141,7 @@ signals: void deviceChange(int newDeviceIndex); void showSpectrum(int deviceSetIndex); void showAllChannels(int deviceSetIndex); - void addChannelEmitted(int channelIndex); + void addChannelEmitted(int channelPluginIndex); void deviceSetPresetsDialogRequested(QPoint, DeviceGUI*); }; diff --git a/sdrgui/device/deviceuiset.cpp b/sdrgui/device/deviceuiset.cpp index e04f387ed..1ecd73d63 100644 --- a/sdrgui/device/deviceuiset.cpp +++ b/sdrgui/device/deviceuiset.cpp @@ -330,6 +330,12 @@ void DeviceUISet::loadRxChannelSettings(const Preset *preset, PluginAPI *pluginA this, [=](int wsIndexDest){ MainWindow::getInstance()->channelMove(rxChannelGUI, wsIndexDest); } ); + QObject::connect( + rxChannelGUI, + &ChannelGUI::duplicateChannelEmitted, + this, + [=](){ MainWindow::getInstance()->channelDuplicate(rxChannelGUI); } + ); } } } @@ -444,6 +450,12 @@ void DeviceUISet::loadTxChannelSettings(const Preset *preset, PluginAPI *pluginA this, [=](int wsIndexDest){ MainWindow::getInstance()->channelMove(txChannelGUI, wsIndexDest); } ); + QObject::connect( + txChannelGUI, + &ChannelGUI::duplicateChannelEmitted, + this, + [=](){ MainWindow::getInstance()->channelDuplicate(txChannelGUI); } + ); } } } @@ -560,6 +572,12 @@ void DeviceUISet::loadMIMOChannelSettings(const Preset *preset, PluginAPI *plugi this, [=](int wsIndexDest){ MainWindow::getInstance()->channelMove(mimoChannelGUI, wsIndexDest); } ); + QObject::connect( + mimoChannelGUI, + &ChannelGUI::duplicateChannelEmitted, + this, + [=](){ MainWindow::getInstance()->channelDuplicate(mimoChannelGUI); } + ); } } } diff --git a/sdrgui/gui/channeladddialog.h b/sdrgui/gui/channeladddialog.h index 6959f1bbe..f0a8fefaa 100644 --- a/sdrgui/gui/channeladddialog.h +++ b/sdrgui/gui/channeladddialog.h @@ -48,7 +48,7 @@ private slots: void apply(QAbstractButton*); signals: - void addChannel(int); + void addChannel(int channelPluginIndex); }; #endif /* SDRGUI_GUI_CHANNELADDDIALOG_H_ */ diff --git a/sdrgui/mainwindow.cpp b/sdrgui/mainwindow.cpp index 89e298a3d..66963e309 100644 --- a/sdrgui/mainwindow.cpp +++ b/sdrgui/mainwindow.cpp @@ -316,7 +316,7 @@ void MainWindow::sampleSourceAdd(Workspace *deviceWorkspace, Workspace *spectrum m_deviceUIs.back()->m_deviceGUI, &DeviceGUI::addChannelEmitted, this, - [=](int channelIndex){ this->channelAddClicked(deviceWorkspace, deviceSetIndex, channelIndex); } + [=](int channelPluginIndex){ this->channelAddClicked(deviceWorkspace, deviceSetIndex, channelPluginIndex); } ); deviceWorkspace->addToMdiArea(m_deviceUIs.back()->m_deviceGUI); @@ -531,7 +531,7 @@ void MainWindow::sampleSinkAdd(Workspace *deviceWorkspace, Workspace *spectrumWo m_deviceUIs.back()->m_deviceGUI, &DeviceGUI::addChannelEmitted, this, - [=](int channelIndex){ this->channelAddClicked(deviceWorkspace, deviceSetIndex, channelIndex); } + [=](int channelPluginIndex){ this->channelAddClicked(deviceWorkspace, deviceSetIndex, channelPluginIndex); } ); deviceWorkspace->addToMdiArea(m_deviceUIs.back()->m_deviceGUI); @@ -754,7 +754,7 @@ void MainWindow::sampleMIMOAdd(Workspace *deviceWorkspace, Workspace *spectrumWo m_deviceUIs.back()->m_deviceGUI, &DeviceGUI::addChannelEmitted, this, - [=](int channelIndex){ this->channelAddClicked(deviceWorkspace, deviceSetIndex, channelIndex); } + [=](int channelPluginIndex){ this->channelAddClicked(deviceWorkspace, deviceSetIndex, channelPluginIndex); } ); deviceWorkspace->addToMdiArea(m_deviceUIs.back()->m_deviceGUI); @@ -2059,7 +2059,7 @@ void MainWindow::sampleSourceChange(int deviceSetIndex, int newDeviceIndex, Work deviceUISet->m_deviceGUI, &DeviceGUI::addChannelEmitted, this, - [=](int channelIndex){ this->channelAddClicked(workspace, deviceSetIndex, channelIndex); } + [=](int channelPluginIndex){ this->channelAddClicked(workspace, deviceSetIndex, channelPluginIndex); } ); } } @@ -2088,7 +2088,7 @@ void MainWindow::sampleSinkChange(int deviceSetIndex, int newDeviceIndex, Worksp deviceUISet->m_deviceGUI, &DeviceGUI::addChannelEmitted, this, - [=](int channelIndex){ this->channelAddClicked(workspace, deviceSetIndex, channelIndex); } + [=](int channelPluginIndex){ this->channelAddClicked(workspace, deviceSetIndex, channelPluginIndex); } ); } } @@ -2116,14 +2116,189 @@ void MainWindow::sampleMIMOChange(int deviceSetIndex, int newDeviceIndex, Worksp deviceUISet->m_deviceGUI, &DeviceGUI::addChannelEmitted, this, - [=](int channelIndex){ this->channelAddClicked(workspace, deviceSetIndex, channelIndex); } + [=](int channelPluginIndex){ this->channelAddClicked(workspace, deviceSetIndex, channelPluginIndex); } ); } } -void MainWindow::channelAddClicked(Workspace *workspace, int deviceSetIndex, int channelIndex) +void MainWindow::channelDuplicate(ChannelGUI *sourceChannelGUI) { - if (deviceSetIndex >= 0) + int deviceSetIndex = sourceChannelGUI->getDeviceSetIndex(); + int channelIndex = sourceChannelGUI->getIndex(); + + qDebug("MainWindow::channelDuplicate: %s at %d:%d in workspace %d", + qPrintable(sourceChannelGUI->getTitle()), deviceSetIndex, channelIndex, sourceChannelGUI->getWorkspaceIndex()); + + if (deviceSetIndex < (int) m_deviceUIs.size()) + { + DeviceUISet *deviceUI = m_deviceUIs[deviceSetIndex]; + ChannelAPI *sourceChannelAPI = deviceUI->getChannelAt(channelIndex); + DeviceAPI *deviceAPI = deviceUI->m_deviceAPI; + ChannelGUI *gui = nullptr; + + if (deviceUI->m_deviceSourceEngine) // source device => Rx channels + { + PluginAPI::ChannelRegistrations *channelRegistrations = m_pluginManager->getRxChannelRegistrations(); + PluginInterface *pluginInterface = nullptr; + + for (const auto& channelRegistration : *channelRegistrations) + { + if (channelRegistration.m_channelIdURI == sourceChannelAPI->getURI()) + { + pluginInterface = channelRegistration.m_plugin; + break; + } + } + + if (pluginInterface) + { + ChannelAPI *channelAPI; + BasebandSampleSink *rxChannel; + pluginInterface->createRxChannel(deviceUI->m_deviceAPI, &rxChannel, &channelAPI); + gui = pluginInterface->createRxChannelGUI(deviceUI, rxChannel); + deviceUI->registerRxChannelInstance(channelAPI, gui); + gui->setDeviceType(ChannelGUI::DeviceRx); + gui->setIndex(channelAPI->getIndexInDeviceSet()); + QByteArray b = sourceChannelGUI->serialize(); + gui->deserialize(b); + } + } + else if (deviceUI->m_deviceSinkEngine) // sink device => Tx channels + { + PluginAPI::ChannelRegistrations *channelRegistrations = m_pluginManager->getTxChannelRegistrations(); // Available channel plugins + PluginInterface *pluginInterface = nullptr; + + for (const auto& channelRegistration : *channelRegistrations) + { + if (channelRegistration.m_channelIdURI == sourceChannelAPI->getURI()) + { + pluginInterface = channelRegistration.m_plugin; + break; + } + } + + if (pluginInterface) + { + ChannelAPI *channelAPI; + BasebandSampleSource *txChannel; + pluginInterface->createTxChannel(deviceUI->m_deviceAPI, &txChannel, &channelAPI); + gui = pluginInterface->createTxChannelGUI(deviceUI, txChannel); + deviceUI->registerTxChannelInstance(channelAPI, gui); + gui->setDeviceType(ChannelGUI::DeviceTx); + gui->setIndex(channelAPI->getIndexInDeviceSet()); + QByteArray b = sourceChannelGUI->serialize(); + gui->deserialize(b); + } + } + else if (deviceUI->m_deviceMIMOEngine) // MIMO device => Any type of channel is possible + { + PluginAPI::ChannelRegistrations *rxChannelRegistrations = m_pluginManager->getRxChannelRegistrations(); + PluginAPI::ChannelRegistrations *txChannelRegistrations = m_pluginManager->getTxChannelRegistrations(); + PluginAPI::ChannelRegistrations *mimoChannelRegistrations = m_pluginManager->getMIMOChannelRegistrations(); + PluginInterface *pluginInterface = nullptr; + + for (const auto& channelRegistration : *rxChannelRegistrations) + { + if (channelRegistration.m_channelIdURI == sourceChannelAPI->getURI()) + { + pluginInterface = channelRegistration.m_plugin; + break; + } + } + + if (pluginInterface) // Rx channel + { + ChannelAPI *channelAPI; + BasebandSampleSink *rxChannel; + pluginInterface->createRxChannel(deviceUI->m_deviceAPI, &rxChannel, &channelAPI); + gui = pluginInterface->createRxChannelGUI(deviceUI, rxChannel); + deviceUI->registerRxChannelInstance(channelAPI, gui); + gui->setDeviceType(ChannelGUI::DeviceMIMO); + gui->setIndex(channelAPI->getIndexInDeviceSet()); + QByteArray b = sourceChannelGUI->serialize(); + gui->deserialize(b); + } + else + { + for (const auto& channelRegistration : *txChannelRegistrations) + { + if (channelRegistration.m_channelIdURI == sourceChannelAPI->getURI()) + { + pluginInterface = channelRegistration.m_plugin; + break; + } + } + + if (pluginInterface) // Tx channel + { + ChannelAPI *channelAPI; + BasebandSampleSource *txChannel; + pluginInterface->createTxChannel(deviceUI->m_deviceAPI, &txChannel, &channelAPI); + gui = pluginInterface->createTxChannelGUI(deviceUI, txChannel); + deviceUI->registerTxChannelInstance(channelAPI, gui); + gui->setDeviceType(ChannelGUI::DeviceMIMO); + gui->setIndex(channelAPI->getIndexInDeviceSet()); + QByteArray b = sourceChannelGUI->serialize(); + gui->deserialize(b); + } + else + { + for (const auto& channelRegistration : *mimoChannelRegistrations) + { + if (channelRegistration.m_channelIdURI == sourceChannelAPI->getURI()) + { + pluginInterface = channelRegistration.m_plugin; + break; + } + } + + if (pluginInterface) + { + ChannelAPI *channelAPI; + MIMOChannel *mimoChannel; + pluginInterface->createMIMOChannel(deviceUI->m_deviceAPI, &mimoChannel, &channelAPI); + gui = pluginInterface->createMIMOChannelGUI(deviceUI, mimoChannel); + deviceUI->registerChannelInstance(channelAPI, gui); + gui->setDeviceType(ChannelGUI::DeviceMIMO); + gui->setIndex(channelAPI->getIndexInDeviceSet()); + QByteArray b = sourceChannelGUI->serialize(); + gui->deserialize(b); + } + } + } + } + + int workspaceIndex = sourceChannelGUI->getWorkspaceIndex(); + Workspace *workspace = workspaceIndex < m_workspaces.size() ? m_workspaces[sourceChannelGUI->getWorkspaceIndex()] : nullptr; + + if (gui && workspace) + { + QObject::connect( + gui, + &ChannelGUI::moveToWorkspace, + this, + [=](int wsIndexDest){ this->channelMove(gui, wsIndexDest); } + ); + QObject::connect( + gui, + &ChannelGUI::duplicateChannelEmitted, + this, + [=](){ this->channelDuplicate(gui); } + ); + + gui->setDeviceSetIndex(deviceSetIndex); + gui->setToolTip(deviceAPI->getSamplingDeviceDisplayName()); + gui->setWorkspaceIndex(workspace->getIndex()); + qDebug("MainWindow::channelDuplicate: adding %s to workspace #%d", + qPrintable(gui->getTitle()), workspace->getIndex()); + workspace->addToMdiArea((QMdiSubWindow*) gui); + } + } +} + +void MainWindow::channelAddClicked(Workspace *workspace, int deviceSetIndex, int channelPluginIndex) +{ + if (deviceSetIndex < (int) m_deviceUIs.size()) { DeviceUISet *deviceUI = m_deviceUIs[deviceSetIndex]; ChannelGUI *gui = nullptr; @@ -2132,7 +2307,7 @@ void MainWindow::channelAddClicked(Workspace *workspace, int deviceSetIndex, int if (deviceUI->m_deviceSourceEngine) // source device => Rx channels { PluginAPI::ChannelRegistrations *channelRegistrations = m_pluginManager->getRxChannelRegistrations(); // Available channel plugins - PluginInterface *pluginInterface = (*channelRegistrations)[channelIndex].m_plugin; + PluginInterface *pluginInterface = (*channelRegistrations)[channelPluginIndex].m_plugin; ChannelAPI *channelAPI; BasebandSampleSink *rxChannel; pluginInterface->createRxChannel(deviceUI->m_deviceAPI, &rxChannel, &channelAPI); @@ -2144,7 +2319,7 @@ void MainWindow::channelAddClicked(Workspace *workspace, int deviceSetIndex, int else if (deviceUI->m_deviceSinkEngine) // sink device => Tx channels { PluginAPI::ChannelRegistrations *channelRegistrations = m_pluginManager->getTxChannelRegistrations(); // Available channel plugins - PluginInterface *pluginInterface = (*channelRegistrations)[channelIndex].m_plugin; + PluginInterface *pluginInterface = (*channelRegistrations)[channelPluginIndex].m_plugin; ChannelAPI *channelAPI; BasebandSampleSource *txChannel; pluginInterface->createTxChannel(deviceUI->m_deviceAPI, &txChannel, &channelAPI); @@ -2159,12 +2334,12 @@ void MainWindow::channelAddClicked(Workspace *workspace, int deviceSetIndex, int int nbRxChannels = deviceUI->getNumberOfAvailableRxChannels(); int nbTxChannels = deviceUI->getNumberOfAvailableTxChannels(); qDebug("MainWindow::channelAddClicked: MIMO: dev %d : nbMIMO: %d nbRx: %d nbTx: %d selected: %d", - deviceSetIndex, nbMIMOChannels, nbRxChannels, nbTxChannels, channelIndex); + deviceSetIndex, nbMIMOChannels, nbRxChannels, nbTxChannels, channelPluginIndex); - if (channelIndex < nbMIMOChannels) + if (channelPluginIndex < nbMIMOChannels) { PluginAPI::ChannelRegistrations *channelRegistrations = m_pluginManager->getMIMOChannelRegistrations(); // Available channel plugins - PluginInterface *pluginInterface = (*channelRegistrations)[channelIndex].m_plugin; + PluginInterface *pluginInterface = (*channelRegistrations)[channelPluginIndex].m_plugin; ChannelAPI *channelAPI; MIMOChannel *mimoChannel; pluginInterface->createMIMOChannel(deviceUI->m_deviceAPI, &mimoChannel, &channelAPI); @@ -2172,10 +2347,10 @@ void MainWindow::channelAddClicked(Workspace *workspace, int deviceSetIndex, int deviceUI->registerChannelInstance(channelAPI, gui); gui->setIndex(channelAPI->getIndexInDeviceSet()); } - else if (channelIndex < nbMIMOChannels + nbRxChannels) // Rx + else if (channelPluginIndex < nbMIMOChannels + nbRxChannels) // Rx { PluginAPI::ChannelRegistrations *channelRegistrations = m_pluginManager->getRxChannelRegistrations(); // Available channel plugins - PluginInterface *pluginInterface = (*channelRegistrations)[channelIndex - nbMIMOChannels].m_plugin; + PluginInterface *pluginInterface = (*channelRegistrations)[channelPluginIndex - nbMIMOChannels].m_plugin; ChannelAPI *channelAPI; BasebandSampleSink *rxChannel; pluginInterface->createRxChannel(deviceUI->m_deviceAPI, &rxChannel, &channelAPI); @@ -2183,10 +2358,10 @@ void MainWindow::channelAddClicked(Workspace *workspace, int deviceSetIndex, int deviceUI->registerRxChannelInstance(channelAPI, gui); gui->setIndex(channelAPI->getIndexInDeviceSet()); } - else if (channelIndex < nbMIMOChannels + nbRxChannels + nbTxChannels) + else if (channelPluginIndex < nbMIMOChannels + nbRxChannels + nbTxChannels) { PluginAPI::ChannelRegistrations *channelRegistrations = m_pluginManager->getTxChannelRegistrations(); // Available channel plugins - PluginInterface *pluginInterface = (*channelRegistrations)[channelIndex - nbMIMOChannels - nbRxChannels].m_plugin; + PluginInterface *pluginInterface = (*channelRegistrations)[channelPluginIndex - nbMIMOChannels - nbRxChannels].m_plugin; ChannelAPI *channelAPI; BasebandSampleSource *txChannel; pluginInterface->createTxChannel(deviceUI->m_deviceAPI, &txChannel, &channelAPI); @@ -2206,6 +2381,12 @@ void MainWindow::channelAddClicked(Workspace *workspace, int deviceSetIndex, int this, [=](int wsIndexDest){ this->channelMove(gui, wsIndexDest); } ); + QObject::connect( + gui, + &ChannelGUI::duplicateChannelEmitted, + this, + [=](){ this->channelDuplicate(gui); } + ); gui->setDeviceSetIndex(deviceSetIndex); gui->setToolTip(deviceAPI->getSamplingDeviceDisplayName()); diff --git a/sdrgui/mainwindow.h b/sdrgui/mainwindow.h index d3b407054..adf825bc3 100644 --- a/sdrgui/mainwindow.h +++ b/sdrgui/mainwindow.h @@ -82,6 +82,7 @@ public: public slots: void channelMove(ChannelGUI *gui, int wsIndexDestnation); + void channelDuplicate(ChannelGUI *gui); private: enum { @@ -198,7 +199,7 @@ private slots: void sampleSinkAdd(Workspace *workspace, Workspace *spectrumWorkspace, int deviceIndex); void sampleMIMOAdd(Workspace *workspace, Workspace *spectrumWorkspace, int deviceIndex); void samplingDeviceChangeHandler(DeviceGUI *deviceGUI, int newDeviceIndex); - void channelAddClicked(Workspace *workspace, int deviceSetIndex, int channelIndex); + void channelAddClicked(Workspace *workspace, int deviceSetIndex, int channelPluginIndex); void featureAddClicked(Workspace *workspace, int featureIndex); void featureMove(FeatureGUI *gui, int wsIndexDestnation); void openFeaturePresetsDialog(QPoint p, Workspace *workspace); From 4e2a4b7f6ccb1bc09d453a3824e7a632bb012693 Mon Sep 17 00:00:00 2001 From: f4exb Date: Fri, 15 Apr 2022 19:53:50 +0200 Subject: [PATCH 029/115] Massive UI revamping (v7): more cleanup --- .../beamsteeringcwmodgui.cpp | 1 - .../interferometer/interferometergui.cpp | 1 - .../channelrx/chanalyzer/chanalyzergui.cpp | 1 - plugins/channelrx/demodadsb/adsbdemodgui.cpp | 1 - plugins/channelrx/demodais/aisdemodgui.cpp | 1 - plugins/channelrx/demodam/amdemodgui.cpp | 1 - plugins/channelrx/demodapt/aptdemodgui.cpp | 1 - plugins/channelrx/demodatv/atvdemodgui.cpp | 1 - plugins/channelrx/demodbfm/bfmdemodgui.cpp | 1 - .../demodchirpchat/chirpchatdemodgui.cpp | 1 - plugins/channelrx/demoddab/dabdemodgui.cpp | 1 - plugins/channelrx/demoddatv/datvdemodgui.cpp | 1 - plugins/channelrx/demoddsd/dsddemodgui.cpp | 1 - .../channelrx/demodfreedv/freedvdemodgui.cpp | 1 - plugins/channelrx/demodnfm/nfmdemodgui.cpp | 1 - .../channelrx/demodpacket/packetdemodgui.cpp | 1 - .../channelrx/demodpager/pagerdemodgui.cpp | 1 - .../demodradiosonde/radiosondedemodgui.cpp | 1 - plugins/channelrx/demodssb/ssbdemodgui.cpp | 2 - plugins/channelrx/demodvor/vordemodgui.cpp | 1 - .../channelrx/demodvorsc/vordemodscgui.cpp | 1 - plugins/channelrx/demodwfm/wfmdemodgui.cpp | 1 - plugins/channelrx/filesink/filesinkgui.cpp | 1 - .../channelrx/freqtracker/freqtrackergui.cpp | 1 - plugins/channelrx/localsink/localsinkgui.cpp | 1 - .../channelrx/noisefigure/noisefiguregui.cpp | 1 - .../radioastronomy/radioastronomygui.cpp | 1 - .../channelrx/radioclock/radioclockgui.cpp | 1 - .../channelrx/remotesink/remotesinkgui.cpp | 1 - .../sigmffilesink/sigmffilesinkgui.cpp | 1 - plugins/channelrx/udpsink/udpsinkgui.cpp | 1 - .../channeltx/filesource/filesourcegui.cpp | 1 - .../channeltx/localsource/localsourcegui.cpp | 1 - .../mod802.15.4/ieee_802_15_4_modgui.cpp | 1 - plugins/channeltx/modais/aismodgui.cpp | 1 - plugins/channeltx/modam/ammodgui.cpp | 1 - plugins/channeltx/modatv/atvmodgui.cpp | 1 - .../modchirpchat/chirpchatmodgui.cpp | 1 - plugins/channeltx/moddatv/datvmodgui.cpp | 1 - plugins/channeltx/modfreedv/freedvmodgui.cpp | 1 - plugins/channeltx/modnfm/nfmmodgui.cpp | 1 - plugins/channeltx/modpacket/packetmodgui.cpp | 1 - plugins/channeltx/modssb/ssbmodgui.cpp | 1 - plugins/channeltx/modwfm/wfmmodgui.cpp | 1 - .../remotesource/remotesourcegui.cpp | 1 - plugins/channeltx/udpsource/udpsourcegui.cpp | 1 - plugins/feature/afc/afcgui.cpp | 2 - plugins/feature/ais/aisgui.cpp | 2 - .../feature/antennatools/antennatoolsgui.cpp | 2 - plugins/feature/aprs/aprsgui.cpp | 2 - .../demodanalyzer/demodanalyzergui.cpp | 2 - .../gs232controller/gs232controllergui.cpp | 2 - .../jogdialcontrollergui.cpp | 2 - plugins/feature/map/mapgui.cpp | 1 - plugins/feature/pertester/pertestergui.cpp | 1 - plugins/feature/radiosonde/radiosondegui.cpp | 2 - .../feature/rigctlserver/rigctlservergui.cpp | 1 - .../satellitetracker/satellitetrackergui.cpp | 1 - plugins/feature/simpleptt/simplepttgui.cpp | 1 - .../feature/startracker/startrackergui.cpp | 1 - .../feature/vorlocalizer/vorlocalizergui.cpp | 1 - sdrgui/CMakeLists.txt | 12 +- sdrgui/device/deviceuiset.cpp | 11 +- sdrgui/device/deviceuiset.h | 5 +- sdrgui/feature/featureuiset.cpp | 5 - sdrgui/feature/featureuiset.h | 1 - sdrgui/gui/channelsdock.cpp | 112 ----------- sdrgui/gui/channelsdock.h | 59 ------ sdrgui/gui/channelwindow.cpp | 36 ---- sdrgui/gui/channelwindow.h | 27 --- sdrgui/gui/featuresdock.cpp | 131 ------------- sdrgui/gui/featuresdock.h | 68 ------- sdrgui/gui/featurewindow.cpp | 64 ------ sdrgui/gui/featurewindow.h | 46 ----- sdrgui/gui/samplingdevicesdock.cpp | 185 ------------------ sdrgui/gui/samplingdevicesdock.h | 81 -------- sdrgui/mainwindow.cpp | 2 - 77 files changed, 6 insertions(+), 909 deletions(-) delete mode 100644 sdrgui/gui/channelsdock.cpp delete mode 100644 sdrgui/gui/channelsdock.h delete mode 100644 sdrgui/gui/channelwindow.cpp delete mode 100644 sdrgui/gui/channelwindow.h delete mode 100644 sdrgui/gui/featuresdock.cpp delete mode 100644 sdrgui/gui/featuresdock.h delete mode 100644 sdrgui/gui/featurewindow.cpp delete mode 100644 sdrgui/gui/featurewindow.h delete mode 100644 sdrgui/gui/samplingdevicesdock.cpp delete mode 100644 sdrgui/gui/samplingdevicesdock.h diff --git a/plugins/channelmimo/beamsteeringcwmod/beamsteeringcwmodgui.cpp b/plugins/channelmimo/beamsteeringcwmod/beamsteeringcwmodgui.cpp index 9e73950b7..f3587d03e 100644 --- a/plugins/channelmimo/beamsteeringcwmod/beamsteeringcwmodgui.cpp +++ b/plugins/channelmimo/beamsteeringcwmod/beamsteeringcwmodgui.cpp @@ -123,7 +123,6 @@ BeamSteeringCWModGUI::BeamSteeringCWModGUI(PluginAPI* pluginAPI, DeviceUISet *de m_settings.setRollupState(&m_rollupState); m_deviceUISet->addChannelMarker(&m_channelMarker); - m_deviceUISet->addRollupWidget(this); connect(getInputMessageQueue(), SIGNAL(messageEnqueued()), this, SLOT(handleSourceMessages())); diff --git a/plugins/channelmimo/interferometer/interferometergui.cpp b/plugins/channelmimo/interferometer/interferometergui.cpp index 19e01348d..6d57ff9da 100644 --- a/plugins/channelmimo/interferometer/interferometergui.cpp +++ b/plugins/channelmimo/interferometer/interferometergui.cpp @@ -147,7 +147,6 @@ InterferometerGUI::InterferometerGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUI m_settings.setSpectrumGUI(ui->spectrumGUI); m_deviceUISet->addChannelMarker(&m_channelMarker); - m_deviceUISet->addRollupWidget(this); ui->spectrumGUI->setBuddies(m_spectrumVis, ui->glSpectrum); ui->scopeGUI->setBuddies(m_scopeVis->getInputMessageQueue(), m_scopeVis, ui->glScope); diff --git a/plugins/channelrx/chanalyzer/chanalyzergui.cpp b/plugins/channelrx/chanalyzer/chanalyzergui.cpp index fb839e645..1e7cd0253 100644 --- a/plugins/channelrx/chanalyzer/chanalyzergui.cpp +++ b/plugins/channelrx/chanalyzer/chanalyzergui.cpp @@ -549,7 +549,6 @@ ChannelAnalyzerGUI::ChannelAnalyzerGUI(PluginAPI* pluginAPI, DeviceUISet *device setTitleColor(m_channelMarker.getColor()); m_deviceUISet->addChannelMarker(&m_channelMarker); - m_deviceUISet->addRollupWidget(this); ui->spectrumGUI->setBuddies(m_spectrumVis, ui->glSpectrum); ui->scopeGUI->setBuddies(m_scopeVis->getInputMessageQueue(), m_scopeVis, ui->glScope); diff --git a/plugins/channelrx/demodadsb/adsbdemodgui.cpp b/plugins/channelrx/demodadsb/adsbdemodgui.cpp index ac1e0f0fa..67c260912 100644 --- a/plugins/channelrx/demodadsb/adsbdemodgui.cpp +++ b/plugins/channelrx/demodadsb/adsbdemodgui.cpp @@ -3740,7 +3740,6 @@ ADSBDemodGUI::ADSBDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Baseb m_settings.setRollupState(&m_rollupState); m_deviceUISet->addChannelMarker(&m_channelMarker); - m_deviceUISet->addRollupWidget(this); connect(&m_channelMarker, SIGNAL(changedByCursor()), this, SLOT(channelMarkerChangedByCursor())); connect(&m_channelMarker, SIGNAL(highlightedByCursor()), this, SLOT(channelMarkerHighlightedByCursor())); diff --git a/plugins/channelrx/demodais/aisdemodgui.cpp b/plugins/channelrx/demodais/aisdemodgui.cpp index 2d9c805bf..cc81405a2 100644 --- a/plugins/channelrx/demodais/aisdemodgui.cpp +++ b/plugins/channelrx/demodais/aisdemodgui.cpp @@ -503,7 +503,6 @@ AISDemodGUI::AISDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Baseban m_settings.setRollupState(&m_rollupState); m_deviceUISet->addChannelMarker(&m_channelMarker); - m_deviceUISet->addRollupWidget(this); connect(&m_channelMarker, SIGNAL(changedByCursor()), this, SLOT(channelMarkerChangedByCursor())); connect(&m_channelMarker, SIGNAL(highlightedByCursor()), this, SLOT(channelMarkerHighlightedByCursor())); diff --git a/plugins/channelrx/demodam/amdemodgui.cpp b/plugins/channelrx/demodam/amdemodgui.cpp index 7a3eda71d..8a4020f6a 100644 --- a/plugins/channelrx/demodam/amdemodgui.cpp +++ b/plugins/channelrx/demodam/amdemodgui.cpp @@ -293,7 +293,6 @@ AMDemodGUI::AMDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandS m_settings.setRollupState(&m_rollupState); m_deviceUISet->addChannelMarker(&m_channelMarker); - m_deviceUISet->addRollupWidget(this); connect(&m_channelMarker, SIGNAL(changedByCursor()), this, SLOT(channelMarkerChangedByCursor())); connect(&m_channelMarker, SIGNAL(highlightedByCursor()), this, SLOT(channelMarkerHighlightedByCursor())); diff --git a/plugins/channelrx/demodapt/aptdemodgui.cpp b/plugins/channelrx/demodapt/aptdemodgui.cpp index 43720e084..bb1e42acb 100644 --- a/plugins/channelrx/demodapt/aptdemodgui.cpp +++ b/plugins/channelrx/demodapt/aptdemodgui.cpp @@ -634,7 +634,6 @@ APTDemodGUI::APTDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Baseban m_settings.setRollupState(&m_rollupState); m_deviceUISet->addChannelMarker(&m_channelMarker); - m_deviceUISet->addRollupWidget(this); connect(&m_channelMarker, SIGNAL(changedByCursor()), this, SLOT(channelMarkerChangedByCursor())); connect(&m_channelMarker, SIGNAL(highlightedByCursor()), this, SLOT(channelMarkerHighlightedByCursor())); diff --git a/plugins/channelrx/demodatv/atvdemodgui.cpp b/plugins/channelrx/demodatv/atvdemodgui.cpp index e76ff36ed..a11a48317 100644 --- a/plugins/channelrx/demodatv/atvdemodgui.cpp +++ b/plugins/channelrx/demodatv/atvdemodgui.cpp @@ -261,7 +261,6 @@ ATVDemodGUI::ATVDemodGUI(PluginAPI* objPluginAPI, DeviceUISet *deviceUISet, Base setTitleColor(m_channelMarker.getColor()); m_deviceUISet->addChannelMarker(&m_channelMarker); - m_deviceUISet->addRollupWidget(this); ui->scopeGUI->setBuddies(m_scopeVis->getInputMessageQueue(), m_scopeVis, ui->glScope); diff --git a/plugins/channelrx/demodbfm/bfmdemodgui.cpp b/plugins/channelrx/demodbfm/bfmdemodgui.cpp index dfb2fd838..921a35a0d 100644 --- a/plugins/channelrx/demodbfm/bfmdemodgui.cpp +++ b/plugins/channelrx/demodbfm/bfmdemodgui.cpp @@ -422,7 +422,6 @@ BFMDemodGUI::BFMDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Baseban m_settings.setRollupState(&m_rollupState); m_deviceUISet->addChannelMarker(&m_channelMarker); - m_deviceUISet->addRollupWidget(this); connect(&m_channelMarker, SIGNAL(changedByCursor()), this, SLOT(channelMarkerChangedByCursor())); connect(&m_channelMarker, SIGNAL(highlightedByCursor()), this, SLOT(channelMarkerHighlightedByCursor())); diff --git a/plugins/channelrx/demodchirpchat/chirpchatdemodgui.cpp b/plugins/channelrx/demodchirpchat/chirpchatdemodgui.cpp index 412a12e14..e7eefe4c9 100644 --- a/plugins/channelrx/demodchirpchat/chirpchatdemodgui.cpp +++ b/plugins/channelrx/demodchirpchat/chirpchatdemodgui.cpp @@ -420,7 +420,6 @@ ChirpChatDemodGUI::ChirpChatDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUI connect(&m_channelMarker, SIGNAL(highlightedByCursor()), this, SLOT(channelMarkerHighlightedByCursor())); m_deviceUISet->addChannelMarker(&m_channelMarker); - m_deviceUISet->addRollupWidget(this); ui->spectrumGUI->setBuddies(m_spectrumVis, ui->glSpectrum); diff --git a/plugins/channelrx/demoddab/dabdemodgui.cpp b/plugins/channelrx/demoddab/dabdemodgui.cpp index 6c7feeddc..8e1729f12 100644 --- a/plugins/channelrx/demoddab/dabdemodgui.cpp +++ b/plugins/channelrx/demoddab/dabdemodgui.cpp @@ -489,7 +489,6 @@ DABDemodGUI::DABDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Baseban m_settings.setRollupState(&m_rollupState); m_deviceUISet->addChannelMarker(&m_channelMarker); - m_deviceUISet->addRollupWidget(this); connect(&m_channelMarker, SIGNAL(changedByCursor()), this, SLOT(channelMarkerChangedByCursor())); connect(&m_channelMarker, SIGNAL(highlightedByCursor()), this, SLOT(channelMarkerHighlightedByCursor())); diff --git a/plugins/channelrx/demoddatv/datvdemodgui.cpp b/plugins/channelrx/demoddatv/datvdemodgui.cpp index 2d745594a..83ffed036 100644 --- a/plugins/channelrx/demoddatv/datvdemodgui.cpp +++ b/plugins/channelrx/demoddatv/datvdemodgui.cpp @@ -276,7 +276,6 @@ DATVDemodGUI::DATVDemodGUI(PluginAPI* objPluginAPI, DeviceUISet *deviceUISet, Ba connect(&m_objChannelMarker, SIGNAL(highlightedByCursor()), this, SLOT(channelMarkerHighlightedByCursor())); m_deviceUISet->addChannelMarker(&m_objChannelMarker); - m_deviceUISet->addRollupWidget(this); // QPixmap pixmapTarget = QPixmap(":/film.png"); // pixmapTarget = pixmapTarget.scaled(16, 16, Qt::KeepAspectRatio, Qt::SmoothTransformation); diff --git a/plugins/channelrx/demoddsd/dsddemodgui.cpp b/plugins/channelrx/demoddsd/dsddemodgui.cpp index 5bd437c50..83fd0db09 100644 --- a/plugins/channelrx/demoddsd/dsddemodgui.cpp +++ b/plugins/channelrx/demoddsd/dsddemodgui.cpp @@ -387,7 +387,6 @@ DSDDemodGUI::DSDDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Baseban m_channelMarker.setVisible(true); // activate signal on the last setting only m_deviceUISet->addChannelMarker(&m_channelMarker); - m_deviceUISet->addRollupWidget(this); connect(&m_channelMarker, SIGNAL(changedByCursor()), this, SLOT(channelMarkerChangedByCursor())); connect(&m_channelMarker, SIGNAL(highlightedByCursor()), this, SLOT(channelMarkerHighlightedByCursor())); diff --git a/plugins/channelrx/demodfreedv/freedvdemodgui.cpp b/plugins/channelrx/demodfreedv/freedvdemodgui.cpp index 75ce5b79a..a1fed8784 100644 --- a/plugins/channelrx/demodfreedv/freedvdemodgui.cpp +++ b/plugins/channelrx/demodfreedv/freedvdemodgui.cpp @@ -307,7 +307,6 @@ FreeDVDemodGUI::FreeDVDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, B m_settings.setRollupState(&m_rollupState); m_deviceUISet->addChannelMarker(&m_channelMarker); - m_deviceUISet->addRollupWidget(this); connect(&m_channelMarker, SIGNAL(changedByCursor()), this, SLOT(channelMarkerChangedByCursor())); connect(&m_channelMarker, SIGNAL(highlightedByCursor()), this, SLOT(channelMarkerHighlightedByCursor())); diff --git a/plugins/channelrx/demodnfm/nfmdemodgui.cpp b/plugins/channelrx/demodnfm/nfmdemodgui.cpp index 3825bb7ef..e975c1b3d 100644 --- a/plugins/channelrx/demodnfm/nfmdemodgui.cpp +++ b/plugins/channelrx/demodnfm/nfmdemodgui.cpp @@ -424,7 +424,6 @@ NFMDemodGUI::NFMDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Baseban m_settings.setRollupState(&m_rollupState); m_deviceUISet->addChannelMarker(&m_channelMarker); - m_deviceUISet->addRollupWidget(this); connect(&m_channelMarker, SIGNAL(changedByCursor()), this, SLOT(channelMarkerChangedByCursor())); connect(&m_channelMarker, SIGNAL(highlightedByCursor()), this, SLOT(channelMarkerHighlightedByCursor())); diff --git a/plugins/channelrx/demodpacket/packetdemodgui.cpp b/plugins/channelrx/demodpacket/packetdemodgui.cpp index 6a344d1ab..3490a5dc3 100644 --- a/plugins/channelrx/demodpacket/packetdemodgui.cpp +++ b/plugins/channelrx/demodpacket/packetdemodgui.cpp @@ -464,7 +464,6 @@ PacketDemodGUI::PacketDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, B m_settings.setRollupState(&m_rollupState); m_deviceUISet->addChannelMarker(&m_channelMarker); - m_deviceUISet->addRollupWidget(this); connect(&m_channelMarker, SIGNAL(changedByCursor()), this, SLOT(channelMarkerChangedByCursor())); connect(&m_channelMarker, SIGNAL(highlightedByCursor()), this, SLOT(channelMarkerHighlightedByCursor())); diff --git a/plugins/channelrx/demodpager/pagerdemodgui.cpp b/plugins/channelrx/demodpager/pagerdemodgui.cpp index 14472b25c..97ca224dd 100644 --- a/plugins/channelrx/demodpager/pagerdemodgui.cpp +++ b/plugins/channelrx/demodpager/pagerdemodgui.cpp @@ -530,7 +530,6 @@ PagerDemodGUI::PagerDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Bas m_settings.setRollupState(&m_rollupState); m_deviceUISet->addChannelMarker(&m_channelMarker); - m_deviceUISet->addRollupWidget(this); connect(&m_channelMarker, SIGNAL(changedByCursor()), this, SLOT(channelMarkerChangedByCursor())); connect(&m_channelMarker, SIGNAL(highlightedByCursor()), this, SLOT(channelMarkerHighlightedByCursor())); diff --git a/plugins/channelrx/demodradiosonde/radiosondedemodgui.cpp b/plugins/channelrx/demodradiosonde/radiosondedemodgui.cpp index 0cf731d3a..14c093e8f 100644 --- a/plugins/channelrx/demodradiosonde/radiosondedemodgui.cpp +++ b/plugins/channelrx/demodradiosonde/radiosondedemodgui.cpp @@ -609,7 +609,6 @@ RadiosondeDemodGUI::RadiosondeDemodGUI(PluginAPI* pluginAPI, DeviceUISet *device m_settings.setRollupState(&m_rollupState); m_deviceUISet->addChannelMarker(&m_channelMarker); - m_deviceUISet->addRollupWidget(this); connect(&m_channelMarker, SIGNAL(changedByCursor()), this, SLOT(channelMarkerChangedByCursor())); connect(&m_channelMarker, SIGNAL(highlightedByCursor()), this, SLOT(channelMarkerHighlightedByCursor())); diff --git a/plugins/channelrx/demodssb/ssbdemodgui.cpp b/plugins/channelrx/demodssb/ssbdemodgui.cpp index 7b266b878..f4b4392a9 100644 --- a/plugins/channelrx/demodssb/ssbdemodgui.cpp +++ b/plugins/channelrx/demodssb/ssbdemodgui.cpp @@ -350,8 +350,6 @@ SSBDemodGUI::SSBDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Baseban m_settings.setRollupState(&m_rollupState); m_deviceUISet->addChannelMarker(&m_channelMarker); - m_deviceUISet->addRollupWidget(this); - connect(&m_channelMarker, SIGNAL(changedByCursor()), this, SLOT(channelMarkerChangedByCursor())); connect(&m_channelMarker, SIGNAL(highlightedByCursor()), this, SLOT(channelMarkerHighlightedByCursor())); connect(getInputMessageQueue(), SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages())); diff --git a/plugins/channelrx/demodvor/vordemodgui.cpp b/plugins/channelrx/demodvor/vordemodgui.cpp index 54cb5904b..b91b77238 100644 --- a/plugins/channelrx/demodvor/vordemodgui.cpp +++ b/plugins/channelrx/demodvor/vordemodgui.cpp @@ -1208,7 +1208,6 @@ VORDemodGUI::VORDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Baseban m_settings.setRollupState(&m_rollupState); m_deviceUISet->addChannelMarker(&m_channelMarker); - m_deviceUISet->addRollupWidget(this); connect(&m_channelMarker, SIGNAL(changedByCursor()), this, SLOT(channelMarkerChangedByCursor())); connect(&m_channelMarker, SIGNAL(highlightedByCursor()), this, SLOT(channelMarkerHighlightedByCursor())); diff --git a/plugins/channelrx/demodvorsc/vordemodscgui.cpp b/plugins/channelrx/demodvorsc/vordemodscgui.cpp index dbabc1268..9e741f7da 100644 --- a/plugins/channelrx/demodvorsc/vordemodscgui.cpp +++ b/plugins/channelrx/demodvorsc/vordemodscgui.cpp @@ -322,7 +322,6 @@ VORDemodSCGUI::VORDemodSCGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Bas m_settings.setRollupState(&m_rollupState); m_deviceUISet->addChannelMarker(&m_channelMarker); - m_deviceUISet->addRollupWidget(this); connect(&m_channelMarker, SIGNAL(changedByCursor()), this, SLOT(channelMarkerChangedByCursor())); connect(&m_channelMarker, SIGNAL(highlightedByCursor()), this, SLOT(channelMarkerHighlightedByCursor())); diff --git a/plugins/channelrx/demodwfm/wfmdemodgui.cpp b/plugins/channelrx/demodwfm/wfmdemodgui.cpp index 0e0862d69..56264e6ad 100644 --- a/plugins/channelrx/demodwfm/wfmdemodgui.cpp +++ b/plugins/channelrx/demodwfm/wfmdemodgui.cpp @@ -254,7 +254,6 @@ WFMDemodGUI::WFMDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Baseban m_settings.setRollupState(&m_rollupState); m_deviceUISet->addChannelMarker(&m_channelMarker); - m_deviceUISet->addRollupWidget(this); connect(&m_channelMarker, SIGNAL(changedByCursor()), this, SLOT(channelMarkerChangedByCursor())); connect(&m_channelMarker, SIGNAL(highlightedByCursor()), this, SLOT(channelMarkerHighlightedByCursor())); diff --git a/plugins/channelrx/filesink/filesinkgui.cpp b/plugins/channelrx/filesink/filesinkgui.cpp index cf1993061..42c0e0baa 100644 --- a/plugins/channelrx/filesink/filesinkgui.cpp +++ b/plugins/channelrx/filesink/filesinkgui.cpp @@ -220,7 +220,6 @@ FileSinkGUI::FileSinkGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Baseban m_settings.setRollupState(&m_rollupState); m_deviceUISet->addChannelMarker(&m_channelMarker); - m_deviceUISet->addRollupWidget(this); connect(&m_channelMarker, SIGNAL(changedByCursor()), this, SLOT(channelMarkerChangedByCursor())); connect(&m_channelMarker, SIGNAL(highlightedByCursor()), this, SLOT(channelMarkerHighlightedByCursor())); diff --git a/plugins/channelrx/freqtracker/freqtrackergui.cpp b/plugins/channelrx/freqtracker/freqtrackergui.cpp index ba57f429e..378c1c426 100644 --- a/plugins/channelrx/freqtracker/freqtrackergui.cpp +++ b/plugins/channelrx/freqtracker/freqtrackergui.cpp @@ -351,7 +351,6 @@ FreqTrackerGUI::FreqTrackerGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, B m_settings.setRollupState(&m_rollupState); m_deviceUISet->addChannelMarker(&m_channelMarker); - m_deviceUISet->addRollupWidget(this); ui->glSpectrum->setCenterFrequency(0); m_pllChannelMarker.blockSignals(true); diff --git a/plugins/channelrx/localsink/localsinkgui.cpp b/plugins/channelrx/localsink/localsinkgui.cpp index 558c270c2..33dcea644 100644 --- a/plugins/channelrx/localsink/localsinkgui.cpp +++ b/plugins/channelrx/localsink/localsinkgui.cpp @@ -126,7 +126,6 @@ LocalSinkGUI::LocalSinkGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Baseb m_settings.setRollupState(&m_rollupState); m_deviceUISet->addChannelMarker(&m_channelMarker); - m_deviceUISet->addRollupWidget(this); connect(getInputMessageQueue(), SIGNAL(messageEnqueued()), this, SLOT(handleSourceMessages())); diff --git a/plugins/channelrx/noisefigure/noisefiguregui.cpp b/plugins/channelrx/noisefigure/noisefiguregui.cpp index 0f9a9c23f..2728c2605 100644 --- a/plugins/channelrx/noisefigure/noisefiguregui.cpp +++ b/plugins/channelrx/noisefigure/noisefiguregui.cpp @@ -630,7 +630,6 @@ NoiseFigureGUI::NoiseFigureGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, B m_settings.setRollupState(&m_rollupState); m_deviceUISet->addChannelMarker(&m_channelMarker); - m_deviceUISet->addRollupWidget(this); connect(&m_channelMarker, SIGNAL(changedByCursor()), this, SLOT(channelMarkerChangedByCursor())); connect(&m_channelMarker, SIGNAL(highlightedByCursor()), this, SLOT(channelMarkerHighlightedByCursor())); diff --git a/plugins/channelrx/radioastronomy/radioastronomygui.cpp b/plugins/channelrx/radioastronomy/radioastronomygui.cpp index fb73a4a6a..7e74542e2 100644 --- a/plugins/channelrx/radioastronomy/radioastronomygui.cpp +++ b/plugins/channelrx/radioastronomy/radioastronomygui.cpp @@ -2112,7 +2112,6 @@ RadioAstronomyGUI::RadioAstronomyGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUI m_settings.setRollupState(&m_rollupState); m_deviceUISet->addChannelMarker(&m_channelMarker); - m_deviceUISet->addRollupWidget(this); connect(&m_channelMarker, SIGNAL(changedByCursor()), this, SLOT(channelMarkerChangedByCursor())); connect(&m_channelMarker, SIGNAL(highlightedByCursor()), this, SLOT(channelMarkerHighlightedByCursor())); diff --git a/plugins/channelrx/radioclock/radioclockgui.cpp b/plugins/channelrx/radioclock/radioclockgui.cpp index f660370ac..7f92628a2 100644 --- a/plugins/channelrx/radioclock/radioclockgui.cpp +++ b/plugins/channelrx/radioclock/radioclockgui.cpp @@ -324,7 +324,6 @@ RadioClockGUI::RadioClockGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Bas m_settings.setScopeGUI(ui->scopeGUI); m_deviceUISet->addChannelMarker(&m_channelMarker); - m_deviceUISet->addRollupWidget(this); connect(&m_channelMarker, SIGNAL(changedByCursor()), this, SLOT(channelMarkerChangedByCursor())); connect(&m_channelMarker, SIGNAL(highlightedByCursor()), this, SLOT(channelMarkerHighlightedByCursor())); diff --git a/plugins/channelrx/remotesink/remotesinkgui.cpp b/plugins/channelrx/remotesink/remotesinkgui.cpp index c2c114c4c..886d2248a 100644 --- a/plugins/channelrx/remotesink/remotesinkgui.cpp +++ b/plugins/channelrx/remotesink/remotesinkgui.cpp @@ -123,7 +123,6 @@ RemoteSinkGUI::RemoteSinkGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Bas m_settings.setRollupState(&m_rollupState); m_deviceUISet->addChannelMarker(&m_channelMarker); - m_deviceUISet->addRollupWidget(this); connect(getInputMessageQueue(), SIGNAL(messageEnqueued()), this, SLOT(handleSourceMessages())); diff --git a/plugins/channelrx/sigmffilesink/sigmffilesinkgui.cpp b/plugins/channelrx/sigmffilesink/sigmffilesinkgui.cpp index f94e3b221..52113da64 100644 --- a/plugins/channelrx/sigmffilesink/sigmffilesinkgui.cpp +++ b/plugins/channelrx/sigmffilesink/sigmffilesinkgui.cpp @@ -206,7 +206,6 @@ SigMFFileSinkGUI::SigMFFileSinkGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISe m_settings.setRollupState(&m_rollupState); m_deviceUISet->addChannelMarker(&m_channelMarker); - m_deviceUISet->addRollupWidget(this); connect(&m_channelMarker, SIGNAL(changedByCursor()), this, SLOT(channelMarkerChangedByCursor())); connect(&m_channelMarker, SIGNAL(highlightedByCursor()), this, SLOT(channelMarkerHighlightedByCursor())); diff --git a/plugins/channelrx/udpsink/udpsinkgui.cpp b/plugins/channelrx/udpsink/udpsinkgui.cpp index c2c9baaf4..939cb5a5f 100644 --- a/plugins/channelrx/udpsink/udpsinkgui.cpp +++ b/plugins/channelrx/udpsink/udpsinkgui.cpp @@ -201,7 +201,6 @@ UDPSinkGUI::UDPSinkGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandS m_settings.setRollupState(&m_rollupState); m_deviceUISet->addChannelMarker(&m_channelMarker); - m_deviceUISet->addRollupWidget(this); connect(&m_channelMarker, SIGNAL(changedByCursor()), this, SLOT(channelMarkerChangedByCursor())); connect(&m_channelMarker, SIGNAL(highlightedByCursor()), this, SLOT(channelMarkerHighlightedByCursor())); diff --git a/plugins/channeltx/filesource/filesourcegui.cpp b/plugins/channeltx/filesource/filesourcegui.cpp index 4fa0fcc23..63c2a3a3e 100644 --- a/plugins/channeltx/filesource/filesourcegui.cpp +++ b/plugins/channeltx/filesource/filesourcegui.cpp @@ -210,7 +210,6 @@ FileSourceGUI::FileSourceGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Bas m_settings.setRollupState(&m_rollupState); m_deviceUISet->addChannelMarker(&m_channelMarker); - m_deviceUISet->addRollupWidget(this); connect(&m_channelMarker, SIGNAL(changedByCursor()), this, SLOT(channelMarkerChangedByCursor())); connect(getInputMessageQueue(), SIGNAL(messageEnqueued()), this, SLOT(handleSourceMessages())); diff --git a/plugins/channeltx/localsource/localsourcegui.cpp b/plugins/channeltx/localsource/localsourcegui.cpp index d16375d26..316894394 100644 --- a/plugins/channeltx/localsource/localsourcegui.cpp +++ b/plugins/channeltx/localsource/localsourcegui.cpp @@ -121,7 +121,6 @@ LocalSourceGUI::LocalSourceGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, B m_settings.setRollupState(&m_rollupState); m_deviceUISet->addChannelMarker(&m_channelMarker); - m_deviceUISet->addRollupWidget(this); connect(getInputMessageQueue(), SIGNAL(messageEnqueued()), this, SLOT(handleSourceMessages())); diff --git a/plugins/channeltx/mod802.15.4/ieee_802_15_4_modgui.cpp b/plugins/channeltx/mod802.15.4/ieee_802_15_4_modgui.cpp index bf811a207..e9dfc2485 100644 --- a/plugins/channeltx/mod802.15.4/ieee_802_15_4_modgui.cpp +++ b/plugins/channeltx/mod802.15.4/ieee_802_15_4_modgui.cpp @@ -454,7 +454,6 @@ IEEE_802_15_4_ModGUI::IEEE_802_15_4_ModGUI(PluginAPI* pluginAPI, DeviceUISet *de m_channelMarker.setVisible(true); // activate signal on the last setting only m_deviceUISet->addChannelMarker(&m_channelMarker); - m_deviceUISet->addRollupWidget(this); connect(&m_channelMarker, SIGNAL(changedByCursor()), this, SLOT(channelMarkerChangedByCursor())); diff --git a/plugins/channeltx/modais/aismodgui.cpp b/plugins/channeltx/modais/aismodgui.cpp index 7a1fec6cd..cb4a2db98 100644 --- a/plugins/channeltx/modais/aismodgui.cpp +++ b/plugins/channeltx/modais/aismodgui.cpp @@ -477,7 +477,6 @@ AISModGUI::AISModGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSam m_channelMarker.setVisible(true); // activate signal on the last setting only m_deviceUISet->addChannelMarker(&m_channelMarker); - m_deviceUISet->addRollupWidget(this); connect(&m_channelMarker, SIGNAL(changedByCursor()), this, SLOT(channelMarkerChangedByCursor())); diff --git a/plugins/channeltx/modam/ammodgui.cpp b/plugins/channeltx/modam/ammodgui.cpp index e73ee98ef..1c0b4003b 100644 --- a/plugins/channeltx/modam/ammodgui.cpp +++ b/plugins/channeltx/modam/ammodgui.cpp @@ -385,7 +385,6 @@ AMModGUI::AMModGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSampl m_settings.setCWKeyerGUI(ui->cwKeyerGUI); m_deviceUISet->addChannelMarker(&m_channelMarker); - m_deviceUISet->addRollupWidget(this); connect(&m_channelMarker, SIGNAL(changedByCursor()), this, SLOT(channelMarkerChangedByCursor())); diff --git a/plugins/channeltx/modatv/atvmodgui.cpp b/plugins/channeltx/modatv/atvmodgui.cpp index ec5e1ea7a..e9046d924 100644 --- a/plugins/channeltx/modatv/atvmodgui.cpp +++ b/plugins/channeltx/modatv/atvmodgui.cpp @@ -96,7 +96,6 @@ ATVModGUI::ATVModGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSam m_settings.setRollupState(&m_rollupState); m_deviceUISet->addChannelMarker(&m_channelMarker); - m_deviceUISet->addRollupWidget(this); connect(&m_channelMarker, SIGNAL(changedByCursor()), this, SLOT(channelMarkerChangedByCursor())); diff --git a/plugins/channeltx/modchirpchat/chirpchatmodgui.cpp b/plugins/channeltx/modchirpchat/chirpchatmodgui.cpp index a7623d984..289c4ba21 100644 --- a/plugins/channeltx/modchirpchat/chirpchatmodgui.cpp +++ b/plugins/channeltx/modchirpchat/chirpchatmodgui.cpp @@ -450,7 +450,6 @@ ChirpChatModGUI::ChirpChatModGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, m_channelMarker.setVisible(true); // activate signal on the last setting only m_deviceUISet->addChannelMarker(&m_channelMarker); - m_deviceUISet->addRollupWidget(this); connect(&m_channelMarker, SIGNAL(changedByCursor()), this, SLOT(channelMarkerChangedByCursor())); diff --git a/plugins/channeltx/moddatv/datvmodgui.cpp b/plugins/channeltx/moddatv/datvmodgui.cpp index 837452b17..6443d7779 100644 --- a/plugins/channeltx/moddatv/datvmodgui.cpp +++ b/plugins/channeltx/moddatv/datvmodgui.cpp @@ -96,7 +96,6 @@ DATVModGUI::DATVModGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandS m_settings.setRollupState(&m_rollupState); m_deviceUISet->addChannelMarker(&m_channelMarker); - m_deviceUISet->addRollupWidget(this); connect(&m_channelMarker, SIGNAL(changedByCursor()), this, SLOT(channelMarkerChangedByCursor())); diff --git a/plugins/channeltx/modfreedv/freedvmodgui.cpp b/plugins/channeltx/modfreedv/freedvmodgui.cpp index 55fa8dcdf..789cd8a59 100644 --- a/plugins/channeltx/modfreedv/freedvmodgui.cpp +++ b/plugins/channeltx/modfreedv/freedvmodgui.cpp @@ -395,7 +395,6 @@ FreeDVModGUI::FreeDVModGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Baseb m_channelMarker.setVisible(true); m_deviceUISet->addChannelMarker(&m_channelMarker); - m_deviceUISet->addRollupWidget(this); connect(&m_channelMarker, SIGNAL(changedByCursor()), this, SLOT(channelMarkerChangedByCursor())); diff --git a/plugins/channeltx/modnfm/nfmmodgui.cpp b/plugins/channeltx/modnfm/nfmmodgui.cpp index 2fe399f89..899a7878c 100644 --- a/plugins/channeltx/modnfm/nfmmodgui.cpp +++ b/plugins/channeltx/modnfm/nfmmodgui.cpp @@ -465,7 +465,6 @@ NFMModGUI::NFMModGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSam m_channelMarker.setVisible(true); // activate signal on the last setting only m_deviceUISet->addChannelMarker(&m_channelMarker); - m_deviceUISet->addRollupWidget(this); connect(&m_channelMarker, SIGNAL(changedByCursor()), this, SLOT(channelMarkerChangedByCursor())); diff --git a/plugins/channeltx/modpacket/packetmodgui.cpp b/plugins/channeltx/modpacket/packetmodgui.cpp index a647320c4..bd98f95d9 100644 --- a/plugins/channeltx/modpacket/packetmodgui.cpp +++ b/plugins/channeltx/modpacket/packetmodgui.cpp @@ -491,7 +491,6 @@ PacketModGUI::PacketModGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Baseb m_channelMarker.setVisible(true); // activate signal on the last setting only m_deviceUISet->addChannelMarker(&m_channelMarker); - m_deviceUISet->addRollupWidget(this); connect(&m_channelMarker, SIGNAL(changedByCursor()), this, SLOT(channelMarkerChangedByCursor())); diff --git a/plugins/channeltx/modssb/ssbmodgui.cpp b/plugins/channeltx/modssb/ssbmodgui.cpp index 6150d3928..cb7c99b91 100644 --- a/plugins/channeltx/modssb/ssbmodgui.cpp +++ b/plugins/channeltx/modssb/ssbmodgui.cpp @@ -472,7 +472,6 @@ SSBModGUI::SSBModGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSam setTitleColor(m_channelMarker.getColor()); m_deviceUISet->addChannelMarker(&m_channelMarker); - m_deviceUISet->addRollupWidget(this); connect(&m_channelMarker, SIGNAL(changedByCursor()), this, SLOT(channelMarkerChangedByCursor())); diff --git a/plugins/channeltx/modwfm/wfmmodgui.cpp b/plugins/channeltx/modwfm/wfmmodgui.cpp index 991aaec65..473922fc5 100644 --- a/plugins/channeltx/modwfm/wfmmodgui.cpp +++ b/plugins/channeltx/modwfm/wfmmodgui.cpp @@ -399,7 +399,6 @@ WFMModGUI::WFMModGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSam m_channelMarker.setVisible(true); // activate signal on the last setting only m_deviceUISet->addChannelMarker(&m_channelMarker); - m_deviceUISet->addRollupWidget(this); connect(&m_channelMarker, SIGNAL(changedByCursor()), this, SLOT(channelMarkerChangedByCursor())); diff --git a/plugins/channeltx/remotesource/remotesourcegui.cpp b/plugins/channeltx/remotesource/remotesourcegui.cpp index aecba396e..c1d3fea70 100644 --- a/plugins/channeltx/remotesource/remotesourcegui.cpp +++ b/plugins/channeltx/remotesource/remotesourcegui.cpp @@ -196,7 +196,6 @@ RemoteSourceGUI::RemoteSourceGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, m_settings.setRollupState(&m_rollupState); m_deviceUISet->addChannelMarker(&m_channelMarker); - m_deviceUISet->addRollupWidget(this); connect(&m_channelMarker, SIGNAL(changedByCursor()), this, SLOT(channelMarkerChangedByCursor())); connect(getInputMessageQueue(), SIGNAL(messageEnqueued()), this, SLOT(handleSourceMessages())); diff --git a/plugins/channeltx/udpsource/udpsourcegui.cpp b/plugins/channeltx/udpsource/udpsourcegui.cpp index 8e41b5402..5ac765630 100644 --- a/plugins/channeltx/udpsource/udpsourcegui.cpp +++ b/plugins/channeltx/udpsource/udpsourcegui.cpp @@ -154,7 +154,6 @@ UDPSourceGUI::UDPSourceGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Baseb m_channelMarker.setVisible(true); // activate signal on the last setting only m_deviceUISet->addChannelMarker(&m_channelMarker); - m_deviceUISet->addRollupWidget(this); connect(&m_channelMarker, SIGNAL(changedByCursor()), this, SLOT(channelMarkerChangedByCursor())); diff --git a/plugins/feature/afc/afcgui.cpp b/plugins/feature/afc/afcgui.cpp index d83f986fe..e6e4d606c 100644 --- a/plugins/feature/afc/afcgui.cpp +++ b/plugins/feature/afc/afcgui.cpp @@ -145,8 +145,6 @@ AFCGUI::AFCGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISet, Feature *featur m_afc = reinterpret_cast(feature); m_afc->setMessageQueueToGUI(&m_inputMessageQueue); - m_featureUISet->addRollupWidget(this); - connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &))); connect(getInputMessageQueue(), SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages())); diff --git a/plugins/feature/ais/aisgui.cpp b/plugins/feature/ais/aisgui.cpp index 455d52f9b..5bbdd19d8 100644 --- a/plugins/feature/ais/aisgui.cpp +++ b/plugins/feature/ais/aisgui.cpp @@ -186,8 +186,6 @@ AISGUI::AISGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISet, Feature *featur m_ais = reinterpret_cast(feature); m_ais->setMessageQueueToGUI(&m_inputMessageQueue); - m_featureUISet->addRollupWidget(this); - connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &))); connect(getInputMessageQueue(), SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages())); diff --git a/plugins/feature/antennatools/antennatoolsgui.cpp b/plugins/feature/antennatools/antennatoolsgui.cpp index 07dc57f00..add2c2cec 100644 --- a/plugins/feature/antennatools/antennatoolsgui.cpp +++ b/plugins/feature/antennatools/antennatoolsgui.cpp @@ -123,8 +123,6 @@ AntennaToolsGUI::AntennaToolsGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISe m_antennatools = reinterpret_cast(feature); m_antennatools->setMessageQueueToGUI(&m_inputMessageQueue); - m_featureUISet->addRollupWidget(this); - connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &))); connect(getInputMessageQueue(), SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages())); diff --git a/plugins/feature/aprs/aprsgui.cpp b/plugins/feature/aprs/aprsgui.cpp index bc4949c8c..2c4430d05 100644 --- a/plugins/feature/aprs/aprsgui.cpp +++ b/plugins/feature/aprs/aprsgui.cpp @@ -439,8 +439,6 @@ APRSGUI::APRSGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISet, Feature *feat m_aprs = reinterpret_cast(feature); m_aprs->setMessageQueueToGUI(&m_inputMessageQueue); - m_featureUISet->addRollupWidget(this); - connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &))); connect(getInputMessageQueue(), SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages())); diff --git a/plugins/feature/demodanalyzer/demodanalyzergui.cpp b/plugins/feature/demodanalyzer/demodanalyzergui.cpp index 4d42071bb..7846641bd 100644 --- a/plugins/feature/demodanalyzer/demodanalyzergui.cpp +++ b/plugins/feature/demodanalyzer/demodanalyzergui.cpp @@ -169,8 +169,6 @@ DemodAnalyzerGUI::DemodAnalyzerGUI(PluginAPI* pluginAPI, FeatureUISet *featureUI ui->glScope->connectTimer(MainCore::instance()->getMasterTimer()); connect(&MainCore::instance()->getMasterTimer(), SIGNAL(timeout()), this, SLOT(tick())); - m_featureUISet->addRollupWidget(this); - m_settings.setSpectrumGUI(ui->spectrumGUI); m_settings.setScopeGUI(ui->scopeGUI); m_settings.setRollupState(&m_rollupState); diff --git a/plugins/feature/gs232controller/gs232controllergui.cpp b/plugins/feature/gs232controller/gs232controllergui.cpp index ffba05030..a7aebbdb5 100644 --- a/plugins/feature/gs232controller/gs232controllergui.cpp +++ b/plugins/feature/gs232controller/gs232controllergui.cpp @@ -148,8 +148,6 @@ GS232ControllerGUI::GS232ControllerGUI(PluginAPI* pluginAPI, FeatureUISet *featu m_gs232Controller = reinterpret_cast(feature); m_gs232Controller->setMessageQueueToGUI(&m_inputMessageQueue); - m_featureUISet->addRollupWidget(this); - connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &))); connect(getInputMessageQueue(), SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages())); diff --git a/plugins/feature/jogdialcontroller/jogdialcontrollergui.cpp b/plugins/feature/jogdialcontroller/jogdialcontrollergui.cpp index 56a038a48..9757e4919 100644 --- a/plugins/feature/jogdialcontroller/jogdialcontrollergui.cpp +++ b/plugins/feature/jogdialcontroller/jogdialcontrollergui.cpp @@ -154,8 +154,6 @@ JogdialControllerGUI::JogdialControllerGUI(PluginAPI* pluginAPI, FeatureUISet *f m_jogdialController = reinterpret_cast(feature); m_jogdialController->setMessageQueueToGUI(&m_inputMessageQueue); - m_featureUISet->addRollupWidget(this); - connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &))); connect(getInputMessageQueue(), SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages())); diff --git a/plugins/feature/map/mapgui.cpp b/plugins/feature/map/mapgui.cpp index de5401908..fb4e1cce9 100644 --- a/plugins/feature/map/mapgui.cpp +++ b/plugins/feature/map/mapgui.cpp @@ -206,7 +206,6 @@ MapGUI::MapGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISet, Feature *featur m_map = reinterpret_cast(feature); m_map->setMessageQueueToGUI(&m_inputMessageQueue); - m_featureUISet->addRollupWidget(this); m_settings.setRollupState(&m_rollupState); connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &))); diff --git a/plugins/feature/pertester/pertestergui.cpp b/plugins/feature/pertester/pertestergui.cpp index d53ce34c3..87f22b3a7 100644 --- a/plugins/feature/pertester/pertestergui.cpp +++ b/plugins/feature/pertester/pertestergui.cpp @@ -134,7 +134,6 @@ PERTesterGUI::PERTesterGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISet, Fea m_perTester = reinterpret_cast(feature); m_perTester->setMessageQueueToGUI(&m_inputMessageQueue); - m_featureUISet->addRollupWidget(this); m_settings.setRollupState(&m_rollupState); connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &))); diff --git a/plugins/feature/radiosonde/radiosondegui.cpp b/plugins/feature/radiosonde/radiosondegui.cpp index af4416ecb..235bd15d4 100644 --- a/plugins/feature/radiosonde/radiosondegui.cpp +++ b/plugins/feature/radiosonde/radiosondegui.cpp @@ -137,8 +137,6 @@ RadiosondeGUI::RadiosondeGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISet, F m_radiosonde = reinterpret_cast(feature); m_radiosonde->setMessageQueueToGUI(&m_inputMessageQueue); - m_featureUISet->addRollupWidget(this); - connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &))); connect(getInputMessageQueue(), SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages())); diff --git a/plugins/feature/rigctlserver/rigctlservergui.cpp b/plugins/feature/rigctlserver/rigctlservergui.cpp index be2b11a10..0ba6772b7 100644 --- a/plugins/feature/rigctlserver/rigctlservergui.cpp +++ b/plugins/feature/rigctlserver/rigctlservergui.cpp @@ -134,7 +134,6 @@ RigCtlServerGUI::RigCtlServerGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISe m_rigCtlServer = reinterpret_cast(feature); m_rigCtlServer->setMessageQueueToGUI(&m_inputMessageQueue); - m_featureUISet->addRollupWidget(this); m_settings.setRollupState(&m_rollupState); connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &))); diff --git a/plugins/feature/satellitetracker/satellitetrackergui.cpp b/plugins/feature/satellitetracker/satellitetrackergui.cpp index 157de5899..3a84b41ab 100644 --- a/plugins/feature/satellitetracker/satellitetrackergui.cpp +++ b/plugins/feature/satellitetracker/satellitetrackergui.cpp @@ -256,7 +256,6 @@ SatelliteTrackerGUI::SatelliteTrackerGUI(PluginAPI* pluginAPI, FeatureUISet *fea m_satelliteTracker = reinterpret_cast(feature); m_satelliteTracker->setMessageQueueToGUI(&m_inputMessageQueue); - m_featureUISet->addRollupWidget(this); m_settings.setRollupState(&m_rollupState); connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &))); diff --git a/plugins/feature/simpleptt/simplepttgui.cpp b/plugins/feature/simpleptt/simplepttgui.cpp index 9ec1e52d5..e92a2c7b4 100644 --- a/plugins/feature/simpleptt/simplepttgui.cpp +++ b/plugins/feature/simpleptt/simplepttgui.cpp @@ -158,7 +158,6 @@ SimplePTTGUI::SimplePTTGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISet, Fea m_simplePTT = reinterpret_cast(feature); m_simplePTT->setMessageQueueToGUI(&m_inputMessageQueue); - m_featureUISet->addRollupWidget(this); m_settings.setRollupState(&m_rollupState); connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &))); diff --git a/plugins/feature/startracker/startrackergui.cpp b/plugins/feature/startracker/startrackergui.cpp index fb85c5ed0..243bb41c4 100644 --- a/plugins/feature/startracker/startrackergui.cpp +++ b/plugins/feature/startracker/startrackergui.cpp @@ -262,7 +262,6 @@ StarTrackerGUI::StarTrackerGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISet, m_starTracker = reinterpret_cast(feature); m_starTracker->setMessageQueueToGUI(&m_inputMessageQueue); - m_featureUISet->addRollupWidget(this); m_settings.setRollupState(&m_rollupState); connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &))); diff --git a/plugins/feature/vorlocalizer/vorlocalizergui.cpp b/plugins/feature/vorlocalizer/vorlocalizergui.cpp index e27fc2332..16f2948bb 100644 --- a/plugins/feature/vorlocalizer/vorlocalizergui.cpp +++ b/plugins/feature/vorlocalizer/vorlocalizergui.cpp @@ -1243,7 +1243,6 @@ VORLocalizerGUI::VORLocalizerGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISe connect(&MainCore::instance()->getMasterTimer(), SIGNAL(timeout()), this, SLOT(tick())); // 50 ms - m_featureUISet->addRollupWidget(this); m_settings.setRollupState(&m_rollupState); connect(getInputMessageQueue(), SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages())); diff --git a/sdrgui/CMakeLists.txt b/sdrgui/CMakeLists.txt index f42591168..95842d5aa 100644 --- a/sdrgui/CMakeLists.txt +++ b/sdrgui/CMakeLists.txt @@ -16,8 +16,6 @@ set(sdrgui_SOURCES gui/basicfeaturesettingsdialog.cpp gui/buttonswitch.cpp gui/channeladddialog.cpp - gui/channelsdock.cpp - gui/channelwindow.cpp gui/clickablelabel.cpp gui/colormapper.cpp gui/commanditem.cpp @@ -39,9 +37,7 @@ set(sdrgui_SOURCES gui/fmpreemphasisdialog.cpp gui/featureadddialog.cpp gui/featurelayout.cpp - gui/featuresdock.cpp gui/featurepresetsdialog.cpp - gui/featurewindow.cpp gui/fftwisdomdialog.cpp gui/flowlayout.cpp gui/glscope.cpp @@ -63,7 +59,6 @@ set(sdrgui_SOURCES gui/rollupcontents.cpp gui/rollupwidget.cpp gui/samplingdevicedialog.cpp - gui/samplingdevicesdock.cpp gui/scaleengine.cpp gui/scaledimage.cpp gui/sdrangelsplash.cpp @@ -119,8 +114,6 @@ set(sdrgui_HEADERS gui/basicfeaturesettingsdialog.h gui/buttonswitch.h gui/channeladddialog.h - gui/channelsdock.h - gui/channelwindow.h gui/colormapper.h gui/commanditem.h gui/commandsdialog.h @@ -142,9 +135,7 @@ set(sdrgui_HEADERS gui/fmpreemphasisdialog.h gui/featureadddialog.h gui/featurelayout.h - gui/featuresdock.h gui/featurepresetsdialog.h - gui/featurewindow.h gui/fftwisdomdialog.h gui/flowlayout.h gui/glscope.h @@ -167,7 +158,6 @@ set(sdrgui_HEADERS gui/rollupcontents.h gui/rollupwidget.h gui/samplingdevicedialog.h - gui/samplingdevicesdock.h gui/scaleengine.h gui/scaledimage.h gui/sdrangelsplash.h @@ -210,7 +200,7 @@ set(sdrgui_HEADERS ) set(sdrgui_FORMS - mainwindow.ui + # mainwindow.ui gui/aboutdialog.ui gui/addpresetdialog.ui gui/ambedevicesdialog.ui diff --git a/sdrgui/device/deviceuiset.cpp b/sdrgui/device/deviceuiset.cpp index 1ecd73d63..90a8dd5f9 100644 --- a/sdrgui/device/deviceuiset.cpp +++ b/sdrgui/device/deviceuiset.cpp @@ -24,7 +24,7 @@ #include "dsp/dspdevicesinkengine.h" #include "gui/glspectrum.h" #include "gui/glspectrumgui.h" -#include "gui/channelwindow.h" +// #include "gui/channelwindow.h" #include "gui/workspace.h" #include "device/devicegui.h" #include "device/deviceset.h" @@ -48,7 +48,7 @@ DeviceUISet::DeviceUISet(int deviceSetIndex, DeviceSet *deviceSet) m_spectrumGUI = new GLSpectrumGUI; m_spectrumGUI->setBuddies(m_spectrumVis, m_spectrum); m_mainSpectrumGUI = new MainSpectrumGUI(m_spectrum, m_spectrumGUI); - m_channelWindow = new ChannelWindow; + // m_channelWindow = new ChannelWindow; m_deviceAPI = nullptr; m_deviceGUI = nullptr; m_deviceSourceEngine = nullptr; @@ -69,7 +69,7 @@ DeviceUISet::DeviceUISet(int deviceSetIndex, DeviceSet *deviceSet) DeviceUISet::~DeviceUISet() { - delete m_channelWindow; + // delete m_channelWindow; delete m_mainSpectrumGUI; // delete m_spectrumGUI; // done above // delete m_spectrum; @@ -97,11 +97,6 @@ void DeviceUISet::addChannelMarker(ChannelMarker* channelMarker) m_spectrum->addChannelMarker(channelMarker); } -void DeviceUISet::addRollupWidget(QWidget *widget) -{ - m_channelWindow->addRollupWidget(widget); -} - void DeviceUISet::registerRxChannelInstance(ChannelAPI *channelAPI, ChannelGUI* channelGUI) { m_channelInstanceRegistrations.append(ChannelInstanceRegistration(channelAPI, channelGUI, 0)); diff --git a/sdrgui/device/deviceuiset.h b/sdrgui/device/deviceuiset.h index f0e940fda..397944c25 100644 --- a/sdrgui/device/deviceuiset.h +++ b/sdrgui/device/deviceuiset.h @@ -28,7 +28,7 @@ class SpectrumVis; class GLSpectrum; class GLSpectrumGUI; class MainSpectrumGUI; -class ChannelWindow; +// class ChannelWindow; class DeviceAPI; class DeviceSet; class DSPDeviceSourceEngine; @@ -56,7 +56,7 @@ public: GLSpectrum *m_spectrum; GLSpectrumGUI *m_spectrumGUI; MainSpectrumGUI *m_mainSpectrumGUI; - ChannelWindow *m_channelWindow; + // ChannelWindow *m_channelWindow; DeviceAPI *m_deviceAPI; DeviceGUI *m_deviceGUI; DSPDeviceSourceEngine *m_deviceSourceEngine; @@ -76,7 +76,6 @@ public: GLSpectrum *getSpectrum() { return m_spectrum; } //!< Direct spectrum getter void setSpectrumScalingFactor(float scalef); void addChannelMarker(ChannelMarker* channelMarker); //!< Add channel marker to spectrum - void addRollupWidget(QWidget *widget); //!< Add rollup widget to channel window int getNumberOfChannels() const { return m_channelInstanceRegistrations.size(); } void freeChannels(); diff --git a/sdrgui/feature/featureuiset.cpp b/sdrgui/feature/featureuiset.cpp index 37ab5279c..54a290ec2 100644 --- a/sdrgui/feature/featureuiset.cpp +++ b/sdrgui/feature/featureuiset.cpp @@ -36,11 +36,6 @@ FeatureUISet::~FeatureUISet() freeFeatures(); } -void FeatureUISet::addRollupWidget(QWidget *) // TODO: remove -{ - // m_featureWindow->addRollupWidget(widget); -} - void FeatureUISet::registerFeatureInstance(FeatureGUI* featureGUI, Feature *feature) { m_featureInstanceRegistrations.append(FeatureInstanceRegistration(featureGUI, feature)); diff --git a/sdrgui/feature/featureuiset.h b/sdrgui/feature/featureuiset.h index e557527cf..ddd8d15dc 100644 --- a/sdrgui/feature/featureuiset.h +++ b/sdrgui/feature/featureuiset.h @@ -40,7 +40,6 @@ public: FeatureUISet(int tabIndex, FeatureSet *featureSet); ~FeatureUISet(); - void addRollupWidget(QWidget *widget); //!< Add feature rollup widget to feature window int getNumberOfFeatures() const { return m_featureInstanceRegistrations.size(); } void registerFeatureInstance(FeatureGUI* featureGUI, Feature *feature); void deleteFeature(int featureIndex); diff --git a/sdrgui/gui/channelsdock.cpp b/sdrgui/gui/channelsdock.cpp deleted file mode 100644 index 2b1010e21..000000000 --- a/sdrgui/gui/channelsdock.cpp +++ /dev/null @@ -1,112 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////////// -// Copyright (C) 2020 F4EXB // -// written by Edouard Griffiths // -// // -// This program 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 as version 3 of the License, or // -// (at your option) any later version. // -// // -// This program 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 V3 for more details. // -// // -// You should have received a copy of the GNU General Public License // -// along with this program. If not, see . // -/////////////////////////////////////////////////////////////////////////////////// - -#include -#include -#include -#include - -#include "channelsdock.h" - -ChannelsDock::ChannelsDock(QWidget *parent, Qt::WindowFlags flags) : - QDockWidget(parent, flags), - m_channelAddDialog(this) -{ - m_titleBar = new QWidget(); - m_titleBarLayout = new QHBoxLayout(); - m_titleBarLayout->setMargin(0); - m_titleBar->setLayout(m_titleBarLayout); - - m_titleLabel = new QLabel(); - m_titleLabel->setText(QString("Channels")); - - m_addChannelButton = new QPushButton(); - QIcon addIcon(":/create.png"); - m_addChannelButton->setIcon(addIcon); - m_addChannelButton->setToolTip("Add channels"); - m_addChannelButton->setFixedSize(16, 16); - - m_normalButton = new QPushButton(); - QIcon normalIcon = style()->standardIcon(QStyle::SP_TitleBarNormalButton, 0, this); - m_normalButton->setIcon(normalIcon); - m_normalButton->setToolTip("Dock/undock"); - m_normalButton->setFixedSize(12, 12); - - m_closeButton = new QPushButton(); - QIcon closeIcon = style()->standardIcon(QStyle::SP_TitleBarCloseButton, 0, this); - m_closeButton->setIcon(closeIcon); - m_closeButton->setToolTip("Close"); - m_closeButton->setFixedSize(12, 12); - - m_titleBarLayout->addWidget(m_addChannelButton); - m_titleBarLayout->addWidget(m_titleLabel); - m_titleBarLayout->addWidget(m_normalButton); - m_titleBarLayout->addWidget(m_closeButton); - setTitleBarWidget(m_titleBar); - - QObject::connect( - m_addChannelButton, - &QPushButton::clicked, - this, - &ChannelsDock::addChannelDialog - ); - - QObject::connect( - m_normalButton, - &QPushButton::clicked, - this, - &ChannelsDock::toggleFloating - ); - - QObject::connect( - &m_channelAddDialog, - &ChannelAddDialog::addChannel, - this, - &ChannelsDock::addChannelEmitted - ); - - connect(m_closeButton, SIGNAL(clicked()), this, SLOT(hide())); -} - -ChannelsDock::~ChannelsDock() -{ - delete m_closeButton; - delete m_normalButton; - delete m_addChannelButton; - delete m_titleLabel; - delete m_titleBarLayout; - delete m_titleBar; -} - -void ChannelsDock::toggleFloating() -{ - setFloating(!isFloating()); -} - -void ChannelsDock::addChannelDialog() -{ - m_channelAddDialog.exec(); - -} - -void ChannelsDock::addChannelEmitted(int channelIndex) -{ - if (channelIndex >= 0) { - emit addChannel(channelIndex); - } -} \ No newline at end of file diff --git a/sdrgui/gui/channelsdock.h b/sdrgui/gui/channelsdock.h deleted file mode 100644 index 96138354e..000000000 --- a/sdrgui/gui/channelsdock.h +++ /dev/null @@ -1,59 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////////// -// Copyright (C) 2020 F4EXB // -// written by Edouard Griffiths // -// // -// This program 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 as version 3 of the License, or // -// (at your option) any later version. // -// // -// This program 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 V3 for more details. // -// // -// You should have received a copy of the GNU General Public License // -// along with this program. If not, see . // -/////////////////////////////////////////////////////////////////////////////////// - -#ifndef SDRGUI_GUI_CHANNELDOCK_H_ -#define SDRGUI_GUI_CHANNELDOCK_H_ - -#include - -#include "channeladddialog.h" - -class QHBoxLayout; -class QLabel; -class QPushButton; -class QStringList; - -class ChannelsDock : public QDockWidget -{ - Q_OBJECT -public: - ChannelsDock(QWidget *parent = nullptr, Qt::WindowFlags flags = Qt::WindowFlags()); - ~ChannelsDock(); - - void resetAvailableChannels() { m_channelAddDialog.resetChannelNames(); } - void addAvailableChannels(const QStringList& channelNames) { m_channelAddDialog.addChannelNames(channelNames); } - -private: - QPushButton *m_addChannelButton; - QWidget *m_titleBar; - QHBoxLayout *m_titleBarLayout; - QLabel *m_titleLabel; - QPushButton *m_normalButton; - QPushButton *m_closeButton; - ChannelAddDialog m_channelAddDialog; - -private slots: - void toggleFloating(); - void addChannelDialog(); - void addChannelEmitted(int channelIndex); - -signals: - void addChannel(int); -}; - -#endif // SDRGUI_GUI_CHANNELDOCK_H_ diff --git a/sdrgui/gui/channelwindow.cpp b/sdrgui/gui/channelwindow.cpp deleted file mode 100644 index 2849bf75f..000000000 --- a/sdrgui/gui/channelwindow.cpp +++ /dev/null @@ -1,36 +0,0 @@ -#include -#include -#include -#include -#include "gui/channelwindow.h" -#include "gui/rollupwidget.h" - -ChannelWindow::ChannelWindow(QWidget* parent) : - QScrollArea(parent) -{ - m_container = new QWidget(this); - m_layout = new QBoxLayout(QBoxLayout::TopToBottom, m_container); - setWidget(m_container); - setWidgetResizable(true); - setBackgroundRole(QPalette::Base); - m_layout->setMargin(3); - m_layout->setSpacing(3); -} - -void ChannelWindow::addRollupWidget(QWidget* rollupWidget) -{ - rollupWidget->setParent(m_container); - m_container->layout()->addWidget(rollupWidget); -} - -void ChannelWindow::resizeEvent(QResizeEvent* event) -{ - if(event->size().height() > event->size().width()) { - m_layout->setDirection(QBoxLayout::TopToBottom); - m_layout->setAlignment(Qt::AlignTop); - } else { - m_layout->setDirection(QBoxLayout::LeftToRight); - m_layout->setAlignment(Qt::AlignLeft); - } - QScrollArea::resizeEvent(event); -} diff --git a/sdrgui/gui/channelwindow.h b/sdrgui/gui/channelwindow.h deleted file mode 100644 index 2a3dd7566..000000000 --- a/sdrgui/gui/channelwindow.h +++ /dev/null @@ -1,27 +0,0 @@ -#ifndef INCLUDE_CHANNELWINDOW_H -#define INCLUDE_CHANNELWINDOW_H - -#include - -#include "export.h" - -class QBoxLayout; -class QSpacerItem; -class RollupWidget; - -class SDRGUI_API ChannelWindow : public QScrollArea { - Q_OBJECT - -public: - ChannelWindow(QWidget* parent = NULL); - - void addRollupWidget(QWidget* rollupWidget); - -protected: - QWidget* m_container; - QBoxLayout* m_layout; - - void resizeEvent(QResizeEvent* event); -}; - -#endif // INCLUDE_CHANNELWINDOW_H diff --git a/sdrgui/gui/featuresdock.cpp b/sdrgui/gui/featuresdock.cpp deleted file mode 100644 index 844e7973f..000000000 --- a/sdrgui/gui/featuresdock.cpp +++ /dev/null @@ -1,131 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////////// -// Copyright (C) 2020 F4EXB // -// written by Edouard Griffiths // -// // -// This program 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 as version 3 of the License, or // -// (at your option) any later version. // -// // -// This program 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 V3 for more details. // -// // -// You should have received a copy of the GNU General Public License // -// along with this program. If not, see . // -/////////////////////////////////////////////////////////////////////////////////// - -#include -#include -#include -#include - -#include "featuresdock.h" - -FeaturesDock::FeaturesDock(QWidget *parent, Qt::WindowFlags flags) : - QDockWidget(parent, flags), - m_featureAddDialog(this) -{ - m_titleBar = new QWidget(); - m_titleBarLayout = new QHBoxLayout(); - m_titleBarLayout->setMargin(0); - m_titleBar->setLayout(m_titleBarLayout); - - m_titleLabel = new QLabel(); - m_titleLabel->setText(QString("Features")); - - m_addFeatureButton = new QPushButton(); - QIcon addIcon(":/create.png"); - m_addFeatureButton->setIcon(addIcon); - m_addFeatureButton->setToolTip("Add features"); - m_addFeatureButton->setFixedSize(16, 16); - - m_presetsButton = new QPushButton(); - QIcon presetsIcon(":/star.png"); - m_presetsButton->setIcon(presetsIcon); - m_presetsButton->setToolTip("Feature presets"); - m_presetsButton->setFixedSize(16, 16); - - m_normalButton = new QPushButton(); - QIcon normalIcon = style()->standardIcon(QStyle::SP_TitleBarNormalButton, 0, this); - m_normalButton->setIcon(normalIcon); - m_normalButton->setToolTip("Dock/undock"); - m_normalButton->setFixedSize(12, 12); - - m_closeButton = new QPushButton(); - QIcon closeIcon = style()->standardIcon(QStyle::SP_TitleBarCloseButton, 0, this); - m_closeButton->setIcon(closeIcon); - m_closeButton->setToolTip("Close"); - m_closeButton->setFixedSize(12, 12); - - m_titleBarLayout->addWidget(m_addFeatureButton); - m_titleBarLayout->addWidget(m_presetsButton); - m_titleBarLayout->addWidget(m_titleLabel); - m_titleBarLayout->addWidget(m_normalButton); - m_titleBarLayout->addWidget(m_closeButton); - setTitleBarWidget(m_titleBar); - - QObject::connect( - m_addFeatureButton, - &QPushButton::clicked, - this, - &FeaturesDock::addFeatureDialog - ); - - QObject::connect( - m_presetsButton, - &QPushButton::clicked, - this, - &FeaturesDock::presetsDialog - ); - - QObject::connect( - m_normalButton, - &QPushButton::clicked, - this, - &FeaturesDock::toggleFloating - ); - - QObject::connect( - &m_featureAddDialog, - &FeatureAddDialog::addFeature, - this, - &FeaturesDock::addFeatureEmitted - ); - - connect(m_closeButton, SIGNAL(clicked()), this, SLOT(hide())); -} - -FeaturesDock::~FeaturesDock() -{ - delete m_closeButton; - delete m_normalButton; - delete m_addFeatureButton; - delete m_titleLabel; - delete m_titleBarLayout; - delete m_titleBar; -} - -void FeaturesDock::toggleFloating() -{ - setFloating(!isFloating()); -} - -void FeaturesDock::addFeatureDialog() -{ - m_featureAddDialog.exec(); -} - -void FeaturesDock::presetsDialog() -{ - m_featurePresetsDialog.populateTree(); - m_featurePresetsDialog.exec(); -} - -void FeaturesDock::addFeatureEmitted(int featureIndex) -{ - if (featureIndex >= 0) { - emit addFeature(featureIndex); - } -} diff --git a/sdrgui/gui/featuresdock.h b/sdrgui/gui/featuresdock.h deleted file mode 100644 index f50af852c..000000000 --- a/sdrgui/gui/featuresdock.h +++ /dev/null @@ -1,68 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////////// -// Copyright (C) 2020 F4EXB // -// written by Edouard Griffiths // -// // -// This program 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 as version 3 of the License, or // -// (at your option) any later version. // -// // -// This program 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 V3 for more details. // -// // -// You should have received a copy of the GNU General Public License // -// along with this program. If not, see . // -/////////////////////////////////////////////////////////////////////////////////// - -#ifndef SDRGUI_GUI_FEATURESDOCK_H_ -#define SDRGUI_GUI_FEATURESDOCK_H_ - -#include - -#include "featureadddialog.h" -#include "featurepresetsdialog.h" - -class QHBoxLayout; -class QLabel; -class QPushButton; -class QStringList; - -class FeaturesDock : public QDockWidget -{ - Q_OBJECT -public: - FeaturesDock(QWidget *parent = nullptr, Qt::WindowFlags flags = Qt::WindowFlags()); - ~FeaturesDock(); - - void resetAvailableFeatures() { m_featureAddDialog.resetFeatureNames(); } - void addAvailableFeatures(const QStringList& featureNames) { m_featureAddDialog.addFeatureNames(featureNames); } - - void setPresets(QList* presets) { m_featurePresetsDialog.setPresets(presets); } - void setFeatureUISet(FeatureUISet *featureUISet) { m_featurePresetsDialog.setFeatureUISet(featureUISet); } - void setPluginAPI(PluginAPI *pluginAPI) { m_featurePresetsDialog.setPluginAPI(pluginAPI); } - void setWebAPIAdapter(WebAPIAdapterInterface *apiAdapter) { m_featurePresetsDialog.setWebAPIAdapter(apiAdapter); } - -private: - QPushButton *m_addFeatureButton; - QPushButton *m_presetsButton; - QWidget *m_titleBar; - QHBoxLayout *m_titleBarLayout; - QLabel *m_titleLabel; - QPushButton *m_normalButton; - QPushButton *m_closeButton; - FeatureAddDialog m_featureAddDialog; - FeaturePresetsDialog m_featurePresetsDialog; - -private slots: - void toggleFloating(); - void addFeatureDialog(); - void presetsDialog(); - void addFeatureEmitted(int featureIndex); - -signals: - void addFeature(int); -}; - -#endif // SDRGUI_GUI_FEATURESDOCK_H_ diff --git a/sdrgui/gui/featurewindow.cpp b/sdrgui/gui/featurewindow.cpp deleted file mode 100644 index af9230b1e..000000000 --- a/sdrgui/gui/featurewindow.cpp +++ /dev/null @@ -1,64 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////////// -// Copyright (C) 2020 Edouard Griffiths, F4EXB // -// // -// This program 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 as version 3 of the License, or // -// (at your option) any later version. // -// // -// This program 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 V3 for more details. // -// // -// You should have received a copy of the GNU General Public License // -// along with this program. If not, see . // -/////////////////////////////////////////////////////////////////////////////////// - -#include -#include - -#include "featurewindow.h" -#include "rollupwidget.h" - -FeatureWindow::FeatureWindow(QWidget* parent) : - QScrollArea(parent) -{ - m_container = new QWidget(this); - m_container->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); - m_splitter = new QSplitter(); - m_layout = new FeatureLayout(m_container, 3, 3, 3); - setWidget(m_container); - setWidgetResizable(true); - setBackgroundRole(QPalette::Base); - m_layout->addWidget(m_splitter); // Splitter must be added first -} - -void FeatureWindow::addRollupWidget(QWidget* rollupWidget) -{ - if (rollupWidget->sizePolicy().verticalPolicy() == QSizePolicy::Expanding) - { - rollupWidget->setParent(m_splitter); - m_splitter->addWidget(rollupWidget); - } - else - { - rollupWidget->setParent(m_container); - m_layout->addWidget(rollupWidget); - } -} - -void FeatureWindow::resizeEvent(QResizeEvent* event) -{ - if (event->size().height() > event->size().width()) - { - m_layout->setOrientation(Qt::Vertical); - m_splitter->setOrientation(Qt::Vertical); - } - else - { - m_layout->setOrientation(Qt::Horizontal); - m_splitter->setOrientation(Qt::Horizontal); - } - QScrollArea::resizeEvent(event); -} diff --git a/sdrgui/gui/featurewindow.h b/sdrgui/gui/featurewindow.h deleted file mode 100644 index 09889d5e9..000000000 --- a/sdrgui/gui/featurewindow.h +++ /dev/null @@ -1,46 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////////// -// Copyright (C) 2020 Edouard Griffiths, F4EXB // -// // -// This program 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 as version 3 of the License, or // -// (at your option) any later version. // -// // -// This program 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 V3 for more details. // -// // -// You should have received a copy of the GNU General Public License // -// along with this program. If not, see . // -/////////////////////////////////////////////////////////////////////////////////// - -#ifndef INCLUDE_FEATUREWINDOW_H -#define INCLUDE_FEATUREWINDOW_H - -#include -#include - -#include "export.h" -#include "featurelayout.h" - -class QBoxLayout; -class QSpacerItem; -class RollupWidget; - -class SDRGUI_API FeatureWindow : public QScrollArea { - Q_OBJECT - -public: - FeatureWindow(QWidget* parent = nullptr); - void addRollupWidget(QWidget* rollupWidget); - -protected: - QWidget* m_container; - FeatureLayout* m_layout; - QSplitter* m_splitter; - - void resizeEvent(QResizeEvent* event); -}; - -#endif // INCLUDE_FEATUREWINDOW_H diff --git a/sdrgui/gui/samplingdevicesdock.cpp b/sdrgui/gui/samplingdevicesdock.cpp deleted file mode 100644 index 6cdea954b..000000000 --- a/sdrgui/gui/samplingdevicesdock.cpp +++ /dev/null @@ -1,185 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////////// -// Copyright (C) 2020 F4EXB // -// written by Edouard Griffiths // -// // -// This program 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 as version 3 of the License, or // -// (at your option) any later version. // -// // -// This program 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 V3 for more details. // -// // -// You should have received a copy of the GNU General Public License // -// along with this program. If not, see . // -/////////////////////////////////////////////////////////////////////////////////// - -#include -#include -#include -#include - -#include "device/deviceenumerator.h" -#include "samplingdevicesdock.h" - -SamplingDevicesDock::SamplingDevicesDock(QWidget *parent, Qt::WindowFlags flags) : - QDockWidget(parent, flags), - m_currentTabIndex(0) -{ - m_titleBar = new QWidget(); - m_titleBarLayout = new QHBoxLayout(); - m_titleBarLayout->setMargin(1); - m_titleBar->setLayout(m_titleBarLayout); - - m_titleLabel = new QLabel(); - m_titleLabel->setText(QString("Sampling device")); // will be changed dynamically - - m_changeDeviceButton = new QPushButton(); - QIcon changeIcon(":/swap.png"); - m_changeDeviceButton->setIcon(changeIcon); - m_changeDeviceButton->setToolTip("Change device"); - m_changeDeviceButton->setFixedSize(16, 16); - - m_reloadDeviceButton = new QPushButton(); - QIcon reloadIcon(":/recycle.png"); - m_reloadDeviceButton->setIcon(reloadIcon); - m_reloadDeviceButton->setToolTip("Reload device"); - m_reloadDeviceButton->setFixedSize(16, 16); - - m_normalButton = new QPushButton(); - QIcon normalIcon = style()->standardIcon(QStyle::SP_TitleBarNormalButton, 0, this); - m_normalButton->setIcon(normalIcon); - m_normalButton->setToolTip("Dock/undock"); - m_normalButton->setFixedSize(12, 12); - - m_closeButton = new QPushButton(); - QIcon closeIcon = style()->standardIcon(QStyle::SP_TitleBarCloseButton, 0, this); - m_closeButton->setIcon(closeIcon); - m_closeButton->setToolTip("Close"); - m_closeButton->setFixedSize(12, 12); - - m_titleBarLayout->addWidget(m_changeDeviceButton); - m_titleBarLayout->addWidget(m_reloadDeviceButton); - m_titleBarLayout->addWidget(m_titleLabel); - m_titleBarLayout->addWidget(m_normalButton); - m_titleBarLayout->addWidget(m_closeButton); - setTitleBarWidget(m_titleBar); - - QObject::connect( - m_changeDeviceButton, - &QPushButton::clicked, - this, - &SamplingDevicesDock::openChangeDeviceDialog - ); - - QObject::connect( - m_reloadDeviceButton, - &QPushButton::clicked, - this, - &SamplingDevicesDock::reloadDevice - ); - - QObject::connect( - m_normalButton, - &QPushButton::clicked, - this, - &SamplingDevicesDock::toggleFloating - ); - - connect(m_closeButton, SIGNAL(clicked()), this, SLOT(hide())); -} - -SamplingDevicesDock::~SamplingDevicesDock() -{ - for (int i = 0; i < m_devicesInfo.size(); i++) { - delete m_devicesInfo[i].m_samplingDeviceDialog; - } - - delete m_closeButton; - delete m_normalButton; - delete m_reloadDeviceButton; - delete m_changeDeviceButton; - delete m_titleLabel; - delete m_titleBarLayout; - delete m_titleBar; -} - -void SamplingDevicesDock::addDevice(int deviceType, int deviceTabIndex) -{ - m_devicesInfo.push_back(DeviceInfo{ - deviceType, - deviceTabIndex, - new SamplingDeviceDialog(deviceType, this) - }); - - setCurrentTabIndex(deviceTabIndex); -} - -void SamplingDevicesDock::removeLastDevice() -{ - if (m_devicesInfo.size() > 0) - { - delete m_devicesInfo.back().m_samplingDeviceDialog; - m_devicesInfo.pop_back(); - } -} - -void SamplingDevicesDock::setCurrentTabIndex(int deviceTabIndex) -{ - m_currentTabIndex = deviceTabIndex; - QString newTitle; - m_devicesInfo[m_currentTabIndex].m_samplingDeviceDialog->getDeviceId(newTitle); - int newTitleSize = newTitle.size(); - - if (newTitleSize > 0) - { - if (newTitleSize > 40) { - newTitle.chop(newTitleSize - 40); - } - - m_titleLabel->setText(newTitle); - } -} - -void SamplingDevicesDock::setSelectedDeviceIndex(int deviceTabIndex, int deviceIndex) -{ - if (deviceTabIndex < m_devicesInfo.size()) - { - m_devicesInfo[deviceTabIndex].m_samplingDeviceDialog->setSelectedDeviceIndex(deviceIndex); - setCurrentTabIndex(m_currentTabIndex); // update title - } -} - -void SamplingDevicesDock::toggleFloating() -{ - setFloating(!isFloating()); -} - -void SamplingDevicesDock::reloadDevice() -{ - emit deviceChanged( - m_devicesInfo[m_currentTabIndex].m_deviceType, - m_devicesInfo[m_currentTabIndex].m_deviceTabIndex, - m_devicesInfo[m_currentTabIndex].m_samplingDeviceDialog->getSelectedDeviceIndex() - ); -} - -void SamplingDevicesDock::openChangeDeviceDialog() -{ - if (m_currentTabIndex < m_devicesInfo.size()) - { - m_devicesInfo[m_currentTabIndex].m_samplingDeviceDialog->exec(); - - if (m_devicesInfo[m_currentTabIndex].m_samplingDeviceDialog->hasChanged()) - { - setCurrentTabIndex(m_currentTabIndex); // update title - emit deviceChanged( - m_devicesInfo[m_currentTabIndex].m_deviceType, - m_devicesInfo[m_currentTabIndex].m_deviceTabIndex, - m_devicesInfo[m_currentTabIndex].m_samplingDeviceDialog->getSelectedDeviceIndex() - ); - } - } -} diff --git a/sdrgui/gui/samplingdevicesdock.h b/sdrgui/gui/samplingdevicesdock.h deleted file mode 100644 index a1eb75ca5..000000000 --- a/sdrgui/gui/samplingdevicesdock.h +++ /dev/null @@ -1,81 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////////// -// Copyright (C) 2020 F4EXB // -// written by Edouard Griffiths // -// // -// This program 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 as version 3 of the License, or // -// (at your option) any later version. // -// // -// This program 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 V3 for more details. // -// // -// You should have received a copy of the GNU General Public License // -// along with this program. If not, see . // -/////////////////////////////////////////////////////////////////////////////////// - -#ifndef SDRGUI_GUI_SAMPLINGDEVICESDOCK_H_ -#define SDRGUI_GUI_SAMPLINGDEVICESDOCK_H_ - -#include -#include - -#include "samplingdevicedialog.h" - -class QHBoxLayout; -class QLabel; -class QPushButton; -class SamplingDeviceDialog; - -class SamplingDevicesDock : public QDockWidget -{ - Q_OBJECT -public: - SamplingDevicesDock(QWidget *parent = nullptr, Qt::WindowFlags flags = Qt::WindowFlags()); - ~SamplingDevicesDock(); - - void addDevice(int deviceType, int deviceTabIndex); - void removeLastDevice(); - void setCurrentTabIndex(int deviceTabIndex); - void setSelectedDeviceIndex(int deviceTabIndex, int deviceIndex); - -private: - struct DeviceInfo - { - DeviceInfo(int deviceType, int deviceTabIndex, SamplingDeviceDialog *samplingDeviceDialog) : - m_deviceType(deviceType), - m_deviceTabIndex(deviceTabIndex), - m_samplingDeviceDialog(samplingDeviceDialog) - {} - DeviceInfo(const DeviceInfo& other) : - m_deviceType(other.m_deviceType), - m_deviceTabIndex(other.m_deviceTabIndex), - m_samplingDeviceDialog(other.m_samplingDeviceDialog) - {} - int m_deviceType; - int m_deviceTabIndex; - SamplingDeviceDialog *m_samplingDeviceDialog; - }; - - QPushButton *m_changeDeviceButton; - QPushButton *m_reloadDeviceButton; - QWidget *m_titleBar; - QHBoxLayout *m_titleBarLayout; - QLabel *m_titleLabel; - QPushButton *m_normalButton; - QPushButton *m_closeButton; - QList m_devicesInfo; - int m_currentTabIndex; - -private slots: - void toggleFloating(); - void reloadDevice(); - void openChangeDeviceDialog(); - -signals: - void deviceChanged(int deviceType, int deviceTabIndex, int newDeviceIndex); -}; - -#endif // SDRGUI_GUI_SAMPLINGDEVICESDOCK_H_ \ No newline at end of file diff --git a/sdrgui/mainwindow.cpp b/sdrgui/mainwindow.cpp index 66963e309..44aa30a21 100644 --- a/sdrgui/mainwindow.cpp +++ b/sdrgui/mainwindow.cpp @@ -55,8 +55,6 @@ #include "gui/pluginsdialog.h" #include "gui/aboutdialog.h" #include "gui/rollupwidget.h" -#include "gui/channelwindow.h" -#include "gui/featurewindow.h" #include "gui/audiodialog.h" #include "gui/loggingdialog.h" #include "gui/deviceuserargsdialog.h" From 103301f19adbd7cc0d0be1aa74a4280649cf13df Mon Sep 17 00:00:00 2001 From: f4exb Date: Sat, 16 Apr 2022 16:45:53 +0200 Subject: [PATCH 030/115] Massive UI revamping (v7): Implemented channel move to another device and more --- doc/img/exit_round.xcf | Bin 0 -> 36228 bytes .../beamsteeringcwmod/beamsteeringcwmod.cpp | 12 ++ .../beamsteeringcwmod/beamsteeringcwmod.h | 4 +- .../beamsteeringcwmod/beamsteeringcwmodgui.h | 1 + .../interferometer/interferometer.cpp | 12 ++ .../interferometer/interferometer.h | 2 + .../interferometer/interferometergui.h | 1 + plugins/channelrx/chanalyzer/chanalyzer.cpp | 12 ++ plugins/channelrx/chanalyzer/chanalyzer.h | 2 + plugins/channelrx/chanalyzer/chanalyzergui.h | 1 + plugins/channelrx/demodadsb/adsbdemod.cpp | 12 ++ plugins/channelrx/demodadsb/adsbdemod.h | 2 + plugins/channelrx/demodadsb/adsbdemodgui.h | 1 + plugins/channelrx/demodais/aisdemod.cpp | 12 ++ plugins/channelrx/demodais/aisdemod.h | 2 + plugins/channelrx/demodais/aisdemodgui.h | 1 + plugins/channelrx/demodam/amdemod.cpp | 12 ++ plugins/channelrx/demodam/amdemod.h | 2 + plugins/channelrx/demodam/amdemodgui.h | 1 + plugins/channelrx/demodapt/aptdemod.cpp | 12 ++ plugins/channelrx/demodapt/aptdemod.h | 2 + plugins/channelrx/demodapt/aptdemodgui.h | 1 + plugins/channelrx/demodatv/atvdemod.cpp | 12 ++ plugins/channelrx/demodatv/atvdemod.h | 2 + plugins/channelrx/demodatv/atvdemodgui.h | 1 + plugins/channelrx/demodbfm/bfmdemod.cpp | 12 ++ plugins/channelrx/demodbfm/bfmdemod.h | 2 + plugins/channelrx/demodbfm/bfmdemodgui.h | 1 + .../demodchirpchat/chirpchatdemod.cpp | 12 ++ .../channelrx/demodchirpchat/chirpchatdemod.h | 2 + .../demodchirpchat/chirpchatdemodgui.h | 1 + plugins/channelrx/demoddab/dabdemod.cpp | 12 ++ plugins/channelrx/demoddab/dabdemod.h | 2 + plugins/channelrx/demoddab/dabdemodgui.h | 1 + plugins/channelrx/demoddatv/datvdemod.cpp | 12 ++ plugins/channelrx/demoddatv/datvdemod.h | 4 +- plugins/channelrx/demoddatv/datvdemodgui.cpp | 74 ++++---- plugins/channelrx/demoddatv/datvdemodgui.h | 3 +- plugins/channelrx/demoddsd/dsddemod.cpp | 12 ++ plugins/channelrx/demoddsd/dsddemod.h | 2 + plugins/channelrx/demoddsd/dsddemodgui.h | 1 + plugins/channelrx/demodfreedv/freedvdemod.cpp | 12 ++ plugins/channelrx/demodfreedv/freedvdemod.h | 2 + .../channelrx/demodfreedv/freedvdemodgui.h | 1 + .../channelrx/demodfreedv/freedvdemodgui.ui | 52 ++++-- plugins/channelrx/demodnfm/nfmdemod.cpp | 12 ++ plugins/channelrx/demodnfm/nfmdemod.h | 2 + plugins/channelrx/demodnfm/nfmdemodgui.h | 1 + plugins/channelrx/demodpacket/packetdemod.cpp | 12 ++ plugins/channelrx/demodpacket/packetdemod.h | 2 + .../channelrx/demodpacket/packetdemodgui.h | 1 + plugins/channelrx/demodpager/pagerdemod.cpp | 12 ++ plugins/channelrx/demodpager/pagerdemod.h | 2 + plugins/channelrx/demodpager/pagerdemodgui.h | 1 + .../demodradiosonde/radiosondedemod.cpp | 12 ++ .../demodradiosonde/radiosondedemod.h | 2 + .../demodradiosonde/radiosondedemodgui.h | 1 + plugins/channelrx/demodssb/ssbdemod.cpp | 12 ++ plugins/channelrx/demodssb/ssbdemod.h | 2 + plugins/channelrx/demodssb/ssbdemodgui.h | 1 + plugins/channelrx/demodssb/ssbdemodgui.ui | 33 ++-- plugins/channelrx/demodvor/vordemod.cpp | 12 ++ plugins/channelrx/demodvor/vordemod.h | 2 + plugins/channelrx/demodvor/vordemodgui.h | 1 + plugins/channelrx/demodvorsc/vordemodsc.cpp | 12 ++ plugins/channelrx/demodvorsc/vordemodsc.h | 2 + plugins/channelrx/demodvorsc/vordemodscgui.h | 1 + plugins/channelrx/demodwfm/wfmdemod.cpp | 12 ++ plugins/channelrx/demodwfm/wfmdemod.h | 2 + plugins/channelrx/demodwfm/wfmdemodgui.h | 1 + plugins/channelrx/filesink/filesink.cpp | 12 ++ plugins/channelrx/filesink/filesink.h | 2 + plugins/channelrx/filesink/filesinkgui.h | 1 + plugins/channelrx/freqtracker/freqtracker.cpp | 12 ++ plugins/channelrx/freqtracker/freqtracker.h | 2 + .../channelrx/freqtracker/freqtrackergui.h | 1 + plugins/channelrx/localsink/localsink.cpp | 12 ++ plugins/channelrx/localsink/localsink.h | 2 + plugins/channelrx/localsink/localsinkgui.h | 1 + plugins/channelrx/noisefigure/noisefigure.cpp | 12 ++ plugins/channelrx/noisefigure/noisefigure.h | 2 + .../channelrx/noisefigure/noisefiguregui.h | 1 + .../radioastronomy/radioastronomy.cpp | 12 ++ .../channelrx/radioastronomy/radioastronomy.h | 2 + .../radioastronomy/radioastronomygui.h | 1 + plugins/channelrx/radioclock/radioclock.cpp | 12 ++ plugins/channelrx/radioclock/radioclock.h | 2 + plugins/channelrx/radioclock/radioclockgui.h | 1 + plugins/channelrx/remotesink/remotesink.cpp | 12 ++ plugins/channelrx/remotesink/remotesink.h | 2 + plugins/channelrx/remotesink/remotesinkgui.h | 1 + .../channelrx/sigmffilesink/sigmffilesink.cpp | 12 ++ .../channelrx/sigmffilesink/sigmffilesink.h | 2 + .../sigmffilesink/sigmffilesinkgui.h | 1 + plugins/channelrx/udpsink/udpsink.cpp | 12 ++ plugins/channelrx/udpsink/udpsink.h | 2 + plugins/channelrx/udpsink/udpsinkgui.h | 1 + plugins/channeltx/filesource/filesource.cpp | 12 ++ plugins/channeltx/filesource/filesource.h | 2 + plugins/channeltx/filesource/filesourcegui.h | 1 + plugins/channeltx/localsource/localsource.cpp | 12 ++ plugins/channeltx/localsource/localsource.h | 2 + .../channeltx/localsource/localsourcegui.h | 1 + .../mod802.15.4/ieee_802_15_4_mod.cpp | 12 ++ .../channeltx/mod802.15.4/ieee_802_15_4_mod.h | 2 + .../mod802.15.4/ieee_802_15_4_modgui.h | 1 + .../mod802.15.4/ieee_802_15_4_modgui.ui | 72 +++++--- plugins/channeltx/modais/aismod.cpp | 12 ++ plugins/channeltx/modais/aismod.h | 2 + plugins/channeltx/modais/aismodgui.h | 1 + plugins/channeltx/modam/ammod.cpp | 12 ++ plugins/channeltx/modam/ammod.h | 2 + plugins/channeltx/modam/ammodgui.h | 1 + plugins/channeltx/modam/ammodgui.ui | 44 +++-- plugins/channeltx/modatv/atvmod.cpp | 12 ++ plugins/channeltx/modatv/atvmod.h | 2 + plugins/channeltx/modatv/atvmodgui.h | 1 + .../channeltx/modchirpchat/chirpchatmod.cpp | 12 ++ plugins/channeltx/modchirpchat/chirpchatmod.h | 2 + .../channeltx/modchirpchat/chirpchatmodgui.h | 1 + plugins/channeltx/moddatv/datvmod.cpp | 12 ++ plugins/channeltx/moddatv/datvmod.h | 2 + plugins/channeltx/moddatv/datvmodgui.h | 1 + plugins/channeltx/modfreedv/freedvmod.cpp | 12 ++ plugins/channeltx/modfreedv/freedvmod.h | 2 + plugins/channeltx/modfreedv/freedvmodgui.h | 1 + plugins/channeltx/modnfm/nfmmod.cpp | 12 ++ plugins/channeltx/modnfm/nfmmod.h | 2 + plugins/channeltx/modnfm/nfmmodgui.h | 1 + plugins/channeltx/modnfm/nfmmodgui.ui | 16 +- plugins/channeltx/modpacket/packetmod.cpp | 12 ++ plugins/channeltx/modpacket/packetmod.h | 2 + plugins/channeltx/modpacket/packetmodgui.h | 1 + plugins/channeltx/modssb/ssbmod.cpp | 12 ++ plugins/channeltx/modssb/ssbmod.h | 2 + plugins/channeltx/modssb/ssbmodgui.h | 1 + plugins/channeltx/modssb/ssbmodgui.ui | 56 +++--- plugins/channeltx/modwfm/wfmmod.cpp | 12 ++ plugins/channeltx/modwfm/wfmmod.h | 2 + plugins/channeltx/modwfm/wfmmodgui.h | 1 + .../channeltx/remotesource/remotesource.cpp | 12 ++ plugins/channeltx/remotesource/remotesource.h | 3 +- .../channeltx/remotesource/remotesourcegui.h | 1 + plugins/channeltx/udpsource/udpsource.cpp | 12 ++ plugins/channeltx/udpsource/udpsource.h | 2 + plugins/channeltx/udpsource/udpsourcegui.h | 1 + plugins/channeltx/udpsource/udpsourcegui.ui | 78 +++++++-- plugins/samplesink/testsink/testsinkgui.ui | 35 +++- sdrbase/channel/channelapi.cpp | 1 - sdrbase/channel/channelapi.h | 5 +- sdrgui/CMakeLists.txt | 2 + sdrgui/channel/channelgui.cpp | 22 ++- sdrgui/channel/channelgui.h | 5 + sdrgui/device/deviceuiset.cpp | 37 ++++ sdrgui/device/deviceuiset.h | 6 +- sdrgui/gui/devicesetselectiondialog.cpp | 65 +++++++ sdrgui/gui/devicesetselectiondialog.h | 73 ++++++++ sdrgui/mainwindow.cpp | 165 ++++++++++++------ sdrgui/mainwindow.h | 4 +- sdrgui/resources/exit_round.png | Bin 0 -> 8704 bytes sdrgui/resources/res.qrc | 1 + 161 files changed, 1312 insertions(+), 231 deletions(-) create mode 100644 doc/img/exit_round.xcf create mode 100644 sdrgui/gui/devicesetselectiondialog.cpp create mode 100644 sdrgui/gui/devicesetselectiondialog.h create mode 100644 sdrgui/resources/exit_round.png diff --git a/doc/img/exit_round.xcf b/doc/img/exit_round.xcf new file mode 100644 index 0000000000000000000000000000000000000000..46ddba6e9e4d8b68aa49fa0735600feee10bde17 GIT binary patch literal 36228 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 SH>_c(^T$7a2SdJv@&5p|9y|&F literal 0 HcmV?d00001 diff --git a/plugins/channelmimo/beamsteeringcwmod/beamsteeringcwmod.cpp b/plugins/channelmimo/beamsteeringcwmod/beamsteeringcwmod.cpp index cd21f50b5..97b7e8074 100644 --- a/plugins/channelmimo/beamsteeringcwmod/beamsteeringcwmod.cpp +++ b/plugins/channelmimo/beamsteeringcwmod/beamsteeringcwmod.cpp @@ -80,6 +80,18 @@ BeamSteeringCWMod::~BeamSteeringCWMod() delete m_thread; } +void BeamSteeringCWMod::setDeviceAPI(DeviceAPI *deviceAPI) +{ + if (deviceAPI != m_deviceAPI) + { + m_deviceAPI->removeChannelSinkAPI(this); + m_deviceAPI->removeMIMOChannel(this); + m_deviceAPI = deviceAPI; + m_deviceAPI->addMIMOChannel(this); + m_deviceAPI->addChannelSinkAPI(this); + } +} + void BeamSteeringCWMod::startSources() { qDebug("BeamSteeringCWMod::startSources"); diff --git a/plugins/channelmimo/beamsteeringcwmod/beamsteeringcwmod.h b/plugins/channelmimo/beamsteeringcwmod/beamsteeringcwmod.h index 04f56beeb..0d50058a3 100644 --- a/plugins/channelmimo/beamsteeringcwmod/beamsteeringcwmod.h +++ b/plugins/channelmimo/beamsteeringcwmod/beamsteeringcwmod.h @@ -88,8 +88,10 @@ public: BeamSteeringCWMod(DeviceAPI *deviceAPI); virtual ~BeamSteeringCWMod(); virtual void destroy() { delete this; } + virtual void setDeviceAPI(DeviceAPI *deviceAPI); + virtual DeviceAPI *getDeviceAPI() { return m_deviceAPI; } - virtual void startSinks() {} + virtual void startSinks() {} virtual void stopSinks() {} virtual void startSources(); //!< thread start() virtual void stopSources(); //!< thread exit() and wait() diff --git a/plugins/channelmimo/beamsteeringcwmod/beamsteeringcwmodgui.h b/plugins/channelmimo/beamsteeringcwmod/beamsteeringcwmodgui.h index 4c88cdd67..093b9f245 100644 --- a/plugins/channelmimo/beamsteeringcwmod/beamsteeringcwmodgui.h +++ b/plugins/channelmimo/beamsteeringcwmod/beamsteeringcwmodgui.h @@ -56,6 +56,7 @@ public: virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } virtual bool getHidden() const { return m_settings.m_hidden; } + virtual ChannelMarker& getChannelMarker() { return m_channelMarker; } private: Ui::BeamSteeringCWModGUI* ui; diff --git a/plugins/channelmimo/interferometer/interferometer.cpp b/plugins/channelmimo/interferometer/interferometer.cpp index 17e06f543..47afd801d 100644 --- a/plugins/channelmimo/interferometer/interferometer.cpp +++ b/plugins/channelmimo/interferometer/interferometer.cpp @@ -82,6 +82,18 @@ Interferometer::~Interferometer() delete m_thread; } +void Interferometer::setDeviceAPI(DeviceAPI *deviceAPI) +{ + if (deviceAPI != m_deviceAPI) + { + m_deviceAPI->removeChannelSinkAPI(this); + m_deviceAPI->removeMIMOChannel(this); + m_deviceAPI = deviceAPI; + m_deviceAPI->addMIMOChannel(this); + m_deviceAPI->addChannelSinkAPI(this); + } +} + void Interferometer::startSinks() { if (m_deviceSampleRate != 0) { diff --git a/plugins/channelmimo/interferometer/interferometer.h b/plugins/channelmimo/interferometer/interferometer.h index 9cb6b8118..403569e76 100644 --- a/plugins/channelmimo/interferometer/interferometer.h +++ b/plugins/channelmimo/interferometer/interferometer.h @@ -88,6 +88,8 @@ public: Interferometer(DeviceAPI *deviceAPI); virtual ~Interferometer(); virtual void destroy() { delete this; } + virtual void setDeviceAPI(DeviceAPI *deviceAPI); + virtual DeviceAPI *getDeviceAPI() { return m_deviceAPI; } virtual void startSinks(); //!< thread start() virtual void stopSinks(); //!< thread exit() and wait() diff --git a/plugins/channelmimo/interferometer/interferometergui.h b/plugins/channelmimo/interferometer/interferometergui.h index 2d8ac1670..47919d1e0 100644 --- a/plugins/channelmimo/interferometer/interferometergui.h +++ b/plugins/channelmimo/interferometer/interferometergui.h @@ -55,6 +55,7 @@ public: virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } virtual bool getHidden() const { return m_settings.m_hidden; } + virtual ChannelMarker& getChannelMarker() { return m_channelMarker; } private: Ui::InterferometerGUI* ui; diff --git a/plugins/channelrx/chanalyzer/chanalyzer.cpp b/plugins/channelrx/chanalyzer/chanalyzer.cpp index d009a329b..9a00de8aa 100644 --- a/plugins/channelrx/chanalyzer/chanalyzer.cpp +++ b/plugins/channelrx/chanalyzer/chanalyzer.cpp @@ -92,6 +92,18 @@ ChannelAnalyzer::~ChannelAnalyzer() qDebug("ChannelAnalyzer::~ChannelAnalyzer: done"); } +void ChannelAnalyzer::setDeviceAPI(DeviceAPI *deviceAPI) +{ + if (deviceAPI != m_deviceAPI) + { + m_deviceAPI->removeChannelSinkAPI(this); + m_deviceAPI->removeChannelSink(this); + m_deviceAPI = deviceAPI; + m_deviceAPI->addChannelSink(this); + m_deviceAPI->addChannelSinkAPI(this); + } +} + int ChannelAnalyzer::getChannelSampleRate() { DeviceSampleSource *source = m_deviceAPI->getSampleSource(); diff --git a/plugins/channelrx/chanalyzer/chanalyzer.h b/plugins/channelrx/chanalyzer/chanalyzer.h index 5cc6b5542..7bbb36f98 100644 --- a/plugins/channelrx/chanalyzer/chanalyzer.h +++ b/plugins/channelrx/chanalyzer/chanalyzer.h @@ -64,6 +64,8 @@ public: ChannelAnalyzer(DeviceAPI *deviceAPI); virtual ~ChannelAnalyzer(); virtual void destroy() { delete this; } + virtual void setDeviceAPI(DeviceAPI *deviceAPI); + virtual DeviceAPI *getDeviceAPI() { return m_deviceAPI; } SpectrumVis *getSpectrumVis() { return &m_spectrumVis; } ScopeVis *getScopeVis() { return &m_scopeVis; } void setScopeVis(ScopeVis *scopeVis) { m_basebandSink->setScopeVis(scopeVis); } diff --git a/plugins/channelrx/chanalyzer/chanalyzergui.h b/plugins/channelrx/chanalyzer/chanalyzergui.h index bfd8016ed..c44a9f8a9 100644 --- a/plugins/channelrx/chanalyzer/chanalyzergui.h +++ b/plugins/channelrx/chanalyzer/chanalyzergui.h @@ -57,6 +57,7 @@ public: virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } virtual bool getHidden() const { return m_settings.m_hidden; } + virtual ChannelMarker& getChannelMarker() { return m_channelMarker; } public slots: void channelMarkerChangedByCursor(); diff --git a/plugins/channelrx/demodadsb/adsbdemod.cpp b/plugins/channelrx/demodadsb/adsbdemod.cpp index 6bf711ab5..2764ff2e3 100644 --- a/plugins/channelrx/demodadsb/adsbdemod.cpp +++ b/plugins/channelrx/demodadsb/adsbdemod.cpp @@ -109,6 +109,18 @@ ADSBDemod::~ADSBDemod() delete m_thread; } +void ADSBDemod::setDeviceAPI(DeviceAPI *deviceAPI) +{ + if (deviceAPI != m_deviceAPI) + { + m_deviceAPI->removeChannelSinkAPI(this); + m_deviceAPI->removeChannelSink(this); + m_deviceAPI = deviceAPI; + m_deviceAPI->addChannelSink(this); + m_deviceAPI->addChannelSinkAPI(this); + } +} + uint32_t ADSBDemod::getNumberOfDeviceStreams() const { return m_deviceAPI->getNbSourceStreams(); diff --git a/plugins/channelrx/demodadsb/adsbdemod.h b/plugins/channelrx/demodadsb/adsbdemod.h index db4a80797..69214119f 100644 --- a/plugins/channelrx/demodadsb/adsbdemod.h +++ b/plugins/channelrx/demodadsb/adsbdemod.h @@ -65,6 +65,8 @@ public: ADSBDemod(DeviceAPI *deviceAPI); virtual ~ADSBDemod(); virtual void destroy() { delete this; } + virtual void setDeviceAPI(DeviceAPI *deviceAPI); + virtual DeviceAPI *getDeviceAPI() { return m_deviceAPI; } using BasebandSampleSink::feed; virtual void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool po); diff --git a/plugins/channelrx/demodadsb/adsbdemodgui.h b/plugins/channelrx/demodadsb/adsbdemodgui.h index d3d1fa96b..95553329f 100644 --- a/plugins/channelrx/demodadsb/adsbdemodgui.h +++ b/plugins/channelrx/demodadsb/adsbdemodgui.h @@ -760,6 +760,7 @@ public: virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } virtual bool getHidden() const { return m_settings.m_hidden; } + virtual ChannelMarker& getChannelMarker() { return m_channelMarker; } void highlightAircraft(Aircraft *aircraft); void targetAircraft(Aircraft *aircraft); diff --git a/plugins/channelrx/demodais/aisdemod.cpp b/plugins/channelrx/demodais/aisdemod.cpp index 9079b2b93..67904a891 100644 --- a/plugins/channelrx/demodais/aisdemod.cpp +++ b/plugins/channelrx/demodais/aisdemod.cpp @@ -97,6 +97,18 @@ AISDemod::~AISDemod() delete m_basebandSink; } +void AISDemod::setDeviceAPI(DeviceAPI *deviceAPI) +{ + if (deviceAPI != m_deviceAPI) + { + m_deviceAPI->removeChannelSinkAPI(this); + m_deviceAPI->removeChannelSink(this); + m_deviceAPI = deviceAPI; + m_deviceAPI->addChannelSink(this); + m_deviceAPI->addChannelSinkAPI(this); + } +} + uint32_t AISDemod::getNumberOfDeviceStreams() const { return m_deviceAPI->getNbSourceStreams(); diff --git a/plugins/channelrx/demodais/aisdemod.h b/plugins/channelrx/demodais/aisdemod.h index 713f05229..7e8d3017b 100644 --- a/plugins/channelrx/demodais/aisdemod.h +++ b/plugins/channelrx/demodais/aisdemod.h @@ -93,6 +93,8 @@ public: AISDemod(DeviceAPI *deviceAPI); virtual ~AISDemod(); virtual void destroy() { delete this; } + virtual void setDeviceAPI(DeviceAPI *deviceAPI); + virtual DeviceAPI *getDeviceAPI() { return m_deviceAPI; } using BasebandSampleSink::feed; virtual void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool po); diff --git a/plugins/channelrx/demodais/aisdemodgui.h b/plugins/channelrx/demodais/aisdemodgui.h index a77d95f25..5856f1852 100644 --- a/plugins/channelrx/demodais/aisdemodgui.h +++ b/plugins/channelrx/demodais/aisdemodgui.h @@ -65,6 +65,7 @@ public: virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } virtual bool getHidden() const { return m_settings.m_hidden; } + virtual ChannelMarker& getChannelMarker() { return m_channelMarker; } public slots: void channelMarkerChangedByCursor(); diff --git a/plugins/channelrx/demodam/amdemod.cpp b/plugins/channelrx/demodam/amdemod.cpp index b420cf5b5..325aad209 100644 --- a/plugins/channelrx/demodam/amdemod.cpp +++ b/plugins/channelrx/demodam/amdemod.cpp @@ -113,6 +113,18 @@ AMDemod::~AMDemod() delete m_basebandSink; } +void AMDemod::setDeviceAPI(DeviceAPI *deviceAPI) +{ + if (deviceAPI != m_deviceAPI) + { + m_deviceAPI->removeChannelSinkAPI(this); + m_deviceAPI->removeChannelSink(this); + m_deviceAPI = deviceAPI; + m_deviceAPI->addChannelSink(this); + m_deviceAPI->addChannelSinkAPI(this); + } +} + uint32_t AMDemod::getNumberOfDeviceStreams() const { return m_deviceAPI->getNbSourceStreams(); diff --git a/plugins/channelrx/demodam/amdemod.h b/plugins/channelrx/demodam/amdemod.h index 145f36748..156684802 100644 --- a/plugins/channelrx/demodam/amdemod.h +++ b/plugins/channelrx/demodam/amdemod.h @@ -64,6 +64,8 @@ public: AMDemod(DeviceAPI *deviceAPI); virtual ~AMDemod(); virtual void destroy() { delete this; } + virtual void setDeviceAPI(DeviceAPI *deviceAPI); + virtual DeviceAPI *getDeviceAPI() { return m_deviceAPI; } using BasebandSampleSink::feed; virtual void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool po); diff --git a/plugins/channelrx/demodam/amdemodgui.h b/plugins/channelrx/demodam/amdemodgui.h index 7e838efc0..7d2440134 100644 --- a/plugins/channelrx/demodam/amdemodgui.h +++ b/plugins/channelrx/demodam/amdemodgui.h @@ -39,6 +39,7 @@ public: virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } virtual bool getHidden() const { return m_settings.m_hidden; } + virtual ChannelMarker& getChannelMarker() { return m_channelMarker; } public slots: void channelMarkerChangedByCursor(); diff --git a/plugins/channelrx/demodapt/aptdemod.cpp b/plugins/channelrx/demodapt/aptdemod.cpp index 96b01b626..54b1a8f8f 100644 --- a/plugins/channelrx/demodapt/aptdemod.cpp +++ b/plugins/channelrx/demodapt/aptdemod.cpp @@ -115,6 +115,18 @@ APTDemod::~APTDemod() delete m_basebandSink; } +void APTDemod::setDeviceAPI(DeviceAPI *deviceAPI) +{ + if (deviceAPI != m_deviceAPI) + { + m_deviceAPI->removeChannelSinkAPI(this); + m_deviceAPI->removeChannelSink(this); + m_deviceAPI = deviceAPI; + m_deviceAPI->addChannelSink(this); + m_deviceAPI->addChannelSinkAPI(this); + } +} + uint32_t APTDemod::getNumberOfDeviceStreams() const { return m_deviceAPI->getNbSourceStreams(); diff --git a/plugins/channelrx/demodapt/aptdemod.h b/plugins/channelrx/demodapt/aptdemod.h index 303ade6da..65ef1ac13 100644 --- a/plugins/channelrx/demodapt/aptdemod.h +++ b/plugins/channelrx/demodapt/aptdemod.h @@ -188,6 +188,8 @@ public: APTDemod(DeviceAPI *deviceAPI); virtual ~APTDemod(); virtual void destroy() { delete this; } + virtual void setDeviceAPI(DeviceAPI *deviceAPI); + virtual DeviceAPI *getDeviceAPI() { return m_deviceAPI; } using BasebandSampleSink::feed; virtual void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool po); diff --git a/plugins/channelrx/demodapt/aptdemodgui.h b/plugins/channelrx/demodapt/aptdemodgui.h index ecd76ef13..1cb07b223 100644 --- a/plugins/channelrx/demodapt/aptdemodgui.h +++ b/plugins/channelrx/demodapt/aptdemodgui.h @@ -83,6 +83,7 @@ public: virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } virtual bool getHidden() const { return m_settings.m_hidden; } + virtual ChannelMarker& getChannelMarker() { return m_channelMarker; } public slots: void channelMarkerChangedByCursor(); diff --git a/plugins/channelrx/demodatv/atvdemod.cpp b/plugins/channelrx/demodatv/atvdemod.cpp index 7770a6348..0eeb224db 100644 --- a/plugins/channelrx/demodatv/atvdemod.cpp +++ b/plugins/channelrx/demodatv/atvdemod.cpp @@ -75,6 +75,18 @@ ATVDemod::~ATVDemod() delete m_basebandSink; } +void ATVDemod::setDeviceAPI(DeviceAPI *deviceAPI) +{ + if (deviceAPI != m_deviceAPI) + { + m_deviceAPI->removeChannelSinkAPI(this); + m_deviceAPI->removeChannelSink(this); + m_deviceAPI = deviceAPI; + m_deviceAPI->addChannelSink(this); + m_deviceAPI->addChannelSinkAPI(this); + } +} + void ATVDemod::start() { qDebug("ATVDemod::start"); diff --git a/plugins/channelrx/demodatv/atvdemod.h b/plugins/channelrx/demodatv/atvdemod.h index e8e246c0c..f70342a17 100644 --- a/plugins/channelrx/demodatv/atvdemod.h +++ b/plugins/channelrx/demodatv/atvdemod.h @@ -63,6 +63,8 @@ public: ATVDemod(DeviceAPI *deviceAPI); virtual ~ATVDemod(); virtual void destroy() { delete this; } + virtual void setDeviceAPI(DeviceAPI *deviceAPI); + virtual DeviceAPI *getDeviceAPI() { return m_deviceAPI; } using BasebandSampleSink::feed; virtual void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool po); diff --git a/plugins/channelrx/demodatv/atvdemodgui.h b/plugins/channelrx/demodatv/atvdemodgui.h index e66415e89..123bf011e 100644 --- a/plugins/channelrx/demodatv/atvdemodgui.h +++ b/plugins/channelrx/demodatv/atvdemodgui.h @@ -58,6 +58,7 @@ public: virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } virtual bool getHidden() const { return m_settings.m_hidden; } + virtual ChannelMarker& getChannelMarker() { return m_channelMarker; } public slots: void channelMarkerChangedByCursor(); diff --git a/plugins/channelrx/demodbfm/bfmdemod.cpp b/plugins/channelrx/demodbfm/bfmdemod.cpp index 2afb6de93..97617df1d 100644 --- a/plugins/channelrx/demodbfm/bfmdemod.cpp +++ b/plugins/channelrx/demodbfm/bfmdemod.cpp @@ -96,6 +96,18 @@ BFMDemod::~BFMDemod() delete m_thread; } +void BFMDemod::setDeviceAPI(DeviceAPI *deviceAPI) +{ + if (deviceAPI != m_deviceAPI) + { + m_deviceAPI->removeChannelSinkAPI(this); + m_deviceAPI->removeChannelSink(this); + m_deviceAPI = deviceAPI; + m_deviceAPI->addChannelSink(this); + m_deviceAPI->addChannelSinkAPI(this); + } +} + uint32_t BFMDemod::getNumberOfDeviceStreams() const { return m_deviceAPI->getNbSourceStreams(); diff --git a/plugins/channelrx/demodbfm/bfmdemod.h b/plugins/channelrx/demodbfm/bfmdemod.h index 0d6b654af..49bbe0ac9 100644 --- a/plugins/channelrx/demodbfm/bfmdemod.h +++ b/plugins/channelrx/demodbfm/bfmdemod.h @@ -70,6 +70,8 @@ public: BFMDemod(DeviceAPI *deviceAPI); virtual ~BFMDemod(); virtual void destroy() { delete this; } + virtual void setDeviceAPI(DeviceAPI *deviceAPI); + virtual DeviceAPI *getDeviceAPI() { return m_deviceAPI; } SpectrumVis *getSpectrumVis() { return &m_spectrumVis; } void setBasebandMessageQueueToGUI(MessageQueue *messageQueue) { m_basebandSink->setMessageQueueToGUI(messageQueue); } diff --git a/plugins/channelrx/demodbfm/bfmdemodgui.h b/plugins/channelrx/demodbfm/bfmdemodgui.h index 79892b3a8..e89dabe31 100644 --- a/plugins/channelrx/demodbfm/bfmdemodgui.h +++ b/plugins/channelrx/demodbfm/bfmdemodgui.h @@ -56,6 +56,7 @@ public: virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } virtual bool getHidden() const { return m_settings.m_hidden; } + virtual ChannelMarker& getChannelMarker() { return m_channelMarker; } public slots: void channelMarkerChangedByCursor(); diff --git a/plugins/channelrx/demodchirpchat/chirpchatdemod.cpp b/plugins/channelrx/demodchirpchat/chirpchatdemod.cpp index 17d261310..a0f2b0ef8 100644 --- a/plugins/channelrx/demodchirpchat/chirpchatdemod.cpp +++ b/plugins/channelrx/demodchirpchat/chirpchatdemod.cpp @@ -97,6 +97,18 @@ ChirpChatDemod::~ChirpChatDemod() delete m_thread; } +void ChirpChatDemod::setDeviceAPI(DeviceAPI *deviceAPI) +{ + if (deviceAPI != m_deviceAPI) + { + m_deviceAPI->removeChannelSinkAPI(this); + m_deviceAPI->removeChannelSink(this); + m_deviceAPI = deviceAPI; + m_deviceAPI->addChannelSink(this); + m_deviceAPI->addChannelSinkAPI(this); + } +} + uint32_t ChirpChatDemod::getNumberOfDeviceStreams() const { return m_deviceAPI->getNbSourceStreams(); diff --git a/plugins/channelrx/demodchirpchat/chirpchatdemod.h b/plugins/channelrx/demodchirpchat/chirpchatdemod.h index 618782e9d..d9dec0a35 100644 --- a/plugins/channelrx/demodchirpchat/chirpchatdemod.h +++ b/plugins/channelrx/demodchirpchat/chirpchatdemod.h @@ -202,6 +202,8 @@ public: ChirpChatDemod(DeviceAPI* deviceAPI); virtual ~ChirpChatDemod(); virtual void destroy() { delete this; } + virtual void setDeviceAPI(DeviceAPI *deviceAPI); + virtual DeviceAPI *getDeviceAPI() { return m_deviceAPI; } SpectrumVis *getSpectrumVis() { return &m_spectrumVis; } using BasebandSampleSink::feed; diff --git a/plugins/channelrx/demodchirpchat/chirpchatdemodgui.h b/plugins/channelrx/demodchirpchat/chirpchatdemodgui.h index ea5a547cc..123c613d8 100644 --- a/plugins/channelrx/demodchirpchat/chirpchatdemodgui.h +++ b/plugins/channelrx/demodchirpchat/chirpchatdemodgui.h @@ -54,6 +54,7 @@ public: virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } virtual bool getHidden() const { return m_settings.m_hidden; } + virtual ChannelMarker& getChannelMarker() { return m_channelMarker; } private slots: void channelMarkerChangedByCursor(); diff --git a/plugins/channelrx/demoddab/dabdemod.cpp b/plugins/channelrx/demoddab/dabdemod.cpp index e42c47928..7319bc582 100644 --- a/plugins/channelrx/demoddab/dabdemod.cpp +++ b/plugins/channelrx/demoddab/dabdemod.cpp @@ -108,6 +108,18 @@ DABDemod::~DABDemod() delete m_basebandSink; } +void DABDemod::setDeviceAPI(DeviceAPI *deviceAPI) +{ + if (deviceAPI != m_deviceAPI) + { + m_deviceAPI->removeChannelSinkAPI(this); + m_deviceAPI->removeChannelSink(this); + m_deviceAPI = deviceAPI; + m_deviceAPI->addChannelSink(this); + m_deviceAPI->addChannelSinkAPI(this); + } +} + uint32_t DABDemod::getNumberOfDeviceStreams() const { return m_deviceAPI->getNbSourceStreams(); diff --git a/plugins/channelrx/demoddab/dabdemod.h b/plugins/channelrx/demoddab/dabdemod.h index a2423a7b9..a177a3872 100644 --- a/plugins/channelrx/demoddab/dabdemod.h +++ b/plugins/channelrx/demoddab/dabdemod.h @@ -313,6 +313,8 @@ public: DABDemod(DeviceAPI *deviceAPI); virtual ~DABDemod(); virtual void destroy() { delete this; } + virtual void setDeviceAPI(DeviceAPI *deviceAPI); + virtual DeviceAPI *getDeviceAPI() { return m_deviceAPI; } using BasebandSampleSink::feed; virtual void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool po); diff --git a/plugins/channelrx/demoddab/dabdemodgui.h b/plugins/channelrx/demoddab/dabdemodgui.h index 2ce282c2c..9ed800f16 100644 --- a/plugins/channelrx/demoddab/dabdemodgui.h +++ b/plugins/channelrx/demoddab/dabdemodgui.h @@ -61,6 +61,7 @@ public: virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } virtual bool getHidden() const { return m_settings.m_hidden; } + virtual ChannelMarker& getChannelMarker() { return m_channelMarker; } public slots: void channelMarkerChangedByCursor(); diff --git a/plugins/channelrx/demoddatv/datvdemod.cpp b/plugins/channelrx/demoddatv/datvdemod.cpp index 988508501..e50a04956 100644 --- a/plugins/channelrx/demoddatv/datvdemod.cpp +++ b/plugins/channelrx/demoddatv/datvdemod.cpp @@ -88,6 +88,18 @@ DATVDemod::~DATVDemod() m_basebandSink->deleteLater(); } +void DATVDemod::setDeviceAPI(DeviceAPI *deviceAPI) +{ + if (deviceAPI != m_deviceAPI) + { + m_deviceAPI->removeChannelSinkAPI(this); + m_deviceAPI->removeChannelSink(this); + m_deviceAPI = deviceAPI; + m_deviceAPI->addChannelSink(this); + m_deviceAPI->addChannelSinkAPI(this); + } +} + void DATVDemod::feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool firstOfBurst) { (void) firstOfBurst; diff --git a/plugins/channelrx/demoddatv/datvdemod.h b/plugins/channelrx/demoddatv/datvdemod.h index 78befcd87..bd90cf38b 100644 --- a/plugins/channelrx/demoddatv/datvdemod.h +++ b/plugins/channelrx/demoddatv/datvdemod.h @@ -43,8 +43,10 @@ public: DATVDemod(DeviceAPI *); virtual ~DATVDemod(); - virtual void destroy() { delete this; } + virtual void setDeviceAPI(DeviceAPI *deviceAPI); + virtual DeviceAPI *getDeviceAPI() { return m_deviceAPI; } + virtual void getIdentifier(QString& id) { id = objectName(); } virtual QString getIdentifier() const { return objectName(); } virtual void getTitle(QString& title) { title = objectName(); } diff --git a/plugins/channelrx/demoddatv/datvdemodgui.cpp b/plugins/channelrx/demoddatv/datvdemodgui.cpp index 83ffed036..73515445c 100644 --- a/plugins/channelrx/demoddatv/datvdemodgui.cpp +++ b/plugins/channelrx/demoddatv/datvdemodgui.cpp @@ -97,7 +97,7 @@ bool DATVDemodGUI::handleMessage(const Message& message) { DATVDemod::MsgConfigureDATVDemod& cfg = (DATVDemod::MsgConfigureDATVDemod&) message; m_settings = cfg.getSettings(); - m_objChannelMarker.updateSettings(static_cast(m_settings.m_channelMarker)); + m_channelMarker.updateSettings(static_cast(m_settings.m_channelMarker)); displaySettings(); return true; } @@ -132,14 +132,14 @@ void DATVDemodGUI::handleInputMessages() void DATVDemodGUI::channelMarkerChangedByCursor() { - ui->deltaFrequency->setValue(m_objChannelMarker.getCenterFrequency()); - m_settings.m_centerFrequency = m_objChannelMarker.getCenterFrequency(); + ui->deltaFrequency->setValue(m_channelMarker.getCenterFrequency()); + m_settings.m_centerFrequency = m_channelMarker.getCenterFrequency(); applySettings(); } void DATVDemodGUI::channelMarkerHighlightedByCursor() { - getRollupContents()->setHighlighted(m_objChannelMarker.getHighlighted()); + getRollupContents()->setHighlighted(m_channelMarker.getHighlighted()); } @@ -156,7 +156,7 @@ void DATVDemodGUI::onMenuDialogCalled(const QPoint &p) { if (m_contextMenuType == ContextMenuChannelSettings) { - BasicChannelSettingsDialog dialog(&m_objChannelMarker, this); + BasicChannelSettingsDialog dialog(&m_channelMarker, this); dialog.setUseReverseAPI(m_settings.m_useReverseAPI); dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); @@ -165,8 +165,8 @@ void DATVDemodGUI::onMenuDialogCalled(const QPoint &p) dialog.move(p); dialog.exec(); - m_settings.m_rgbColor = m_objChannelMarker.getColor().rgb(); - m_settings.m_title = m_objChannelMarker.getTitle(); + m_settings.m_rgbColor = m_channelMarker.getColor().rgb(); + m_settings.m_title = m_channelMarker.getTitle(); m_settings.m_useReverseAPI = dialog.useReverseAPI(); m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); @@ -174,7 +174,7 @@ void DATVDemodGUI::onMenuDialogCalled(const QPoint &p) m_settings.m_reverseAPIChannelIndex = dialog.getReverseAPIChannelIndex(); setWindowTitle(m_settings.m_title); - setTitle(m_objChannelMarker.getTitle()); + setTitle(m_channelMarker.getTitle()); setTitleColor(m_settings.m_rgbColor); applySettings(); @@ -188,8 +188,8 @@ void DATVDemodGUI::onMenuDialogCalled(const QPoint &p) dialog.exec(); m_settings.m_streamIndex = dialog.getSelectedStreamIndex(); - m_objChannelMarker.clearStreamIndexes(); - m_objChannelMarker.addStreamIndex(m_settings.m_streamIndex); + m_channelMarker.clearStreamIndexes(); + m_channelMarker.addStreamIndex(m_settings.m_streamIndex); displayStreamIndex(); applySettings(); } @@ -202,7 +202,7 @@ DATVDemodGUI::DATVDemodGUI(PluginAPI* objPluginAPI, DeviceUISet *deviceUISet, Ba ui(new Ui::DATVDemodGUI), m_objPluginAPI(objPluginAPI), m_deviceUISet(deviceUISet), - m_objChannelMarker(this), + m_channelMarker(this), m_deviceCenterFrequency(0), m_basebandSampleRate(1), m_blnBasicSettingsShown(false), @@ -241,7 +241,7 @@ DATVDemodGUI::DATVDemodGUI(PluginAPI* objPluginAPI, DeviceUISet *deviceUISet, Ba connect(m_datvDemod->getUDPStream(), &DATVUDPStream::fifoData, this, &DATVDemodGUI::on_StreamDataAvailable); } - m_settings.setChannelMarker(&m_objChannelMarker); + m_settings.setChannelMarker(&m_channelMarker); m_settings.setRollupState(&m_rollupState); connect(ui->screenTV_2, &DATVideoRender::onMetaDataChanged, this, &DATVDemodGUI::on_StreamMetaDataChanged); @@ -264,18 +264,18 @@ DATVDemodGUI::DATVDemodGUI(PluginAPI* objPluginAPI, DeviceUISet *deviceUISet, Ba ui->rfBandwidth->setColorMapper(ColorMapper(ColorMapper::GrayYellow)); ui->rfBandwidth->setValueRange(true, 8, 0, 50000000); - m_objChannelMarker.blockSignals(true); - m_objChannelMarker.setColor(Qt::magenta); - m_objChannelMarker.setBandwidth(6000000); - m_objChannelMarker.setCenterFrequency(0); - m_objChannelMarker.setTitle("DATV Demodulator"); - m_objChannelMarker.blockSignals(false); - m_objChannelMarker.setVisible(true); + m_channelMarker.blockSignals(true); + m_channelMarker.setColor(Qt::magenta); + m_channelMarker.setBandwidth(6000000); + m_channelMarker.setCenterFrequency(0); + m_channelMarker.setTitle("DATV Demodulator"); + m_channelMarker.blockSignals(false); + m_channelMarker.setVisible(true); - connect(&m_objChannelMarker, SIGNAL(changedByCursor()), this, SLOT(channelMarkerChangedByCursor())); - connect(&m_objChannelMarker, SIGNAL(highlightedByCursor()), this, SLOT(channelMarkerHighlightedByCursor())); + connect(&m_channelMarker, SIGNAL(changedByCursor()), this, SLOT(channelMarkerChangedByCursor())); + connect(&m_channelMarker, SIGNAL(highlightedByCursor()), this, SLOT(channelMarkerHighlightedByCursor())); - m_deviceUISet->addChannelMarker(&m_objChannelMarker); + m_deviceUISet->addChannelMarker(&m_channelMarker); // QPixmap pixmapTarget = QPixmap(":/film.png"); // pixmapTarget = pixmapTarget.scaled(16, 16, Qt::KeepAspectRatio, Qt::SmoothTransformation); @@ -312,18 +312,18 @@ void DATVDemodGUI::blockApplySettings(bool blnBlock) void DATVDemodGUI::displaySettings() { - m_objChannelMarker.blockSignals(true); - m_objChannelMarker.setCenterFrequency(m_settings.m_centerFrequency); - m_objChannelMarker.setColor(m_settings.m_rgbColor); - m_objChannelMarker.setTitle(m_settings.m_title); - m_objChannelMarker.blockSignals(false); - m_objChannelMarker.setBandwidth(m_settings.m_rfBandwidth); + m_channelMarker.blockSignals(true); + m_channelMarker.setCenterFrequency(m_settings.m_centerFrequency); + m_channelMarker.setColor(m_settings.m_rgbColor); + m_channelMarker.setTitle(m_settings.m_title); + m_channelMarker.blockSignals(false); + m_channelMarker.setBandwidth(m_settings.m_rfBandwidth); blockApplySettings(true); setTitleColor(m_settings.m_rgbColor); - setWindowTitle(m_objChannelMarker.getTitle()); - setTitle(m_objChannelMarker.getTitle()); + setWindowTitle(m_channelMarker.getTitle()); + setTitle(m_channelMarker.getTitle()); ui->deltaFrequency->setValue(m_settings.m_centerFrequency); ui->chkAllowDrift->setChecked(m_settings.m_allowDrift); @@ -475,7 +475,7 @@ void DATVDemodGUI::applySettings(bool force) { qDebug("DATVDemodGUI::applySettings"); - setTitleColor(m_objChannelMarker.getColor()); + setTitleColor(m_channelMarker.getColor()); QString msg = tr("DATVDemodGUI::applySettings: force: %1").arg(force ? "true" : "false"); m_settings.debug(msg); @@ -488,14 +488,14 @@ void DATVDemodGUI::applySettings(bool force) void DATVDemodGUI::leaveEvent(QEvent*) { blockApplySettings(true); - m_objChannelMarker.setHighlighted(false); + m_channelMarker.setHighlighted(false); blockApplySettings(false); } void DATVDemodGUI::enterEvent(QEvent*) { blockApplySettings(true); - m_objChannelMarker.setHighlighted(true); + m_channelMarker.setHighlighted(true); blockApplySettings(false); } @@ -790,16 +790,16 @@ void DATVDemodGUI::on_StreamDataAvailable(int intBytes, int intPercent, qint64 i void DATVDemodGUI::on_deltaFrequency_changed(qint64 value) { - m_objChannelMarker.setCenterFrequency(value); - m_settings.m_centerFrequency = m_objChannelMarker.getCenterFrequency(); + m_channelMarker.setCenterFrequency(value); + m_settings.m_centerFrequency = m_channelMarker.getCenterFrequency(); updateAbsoluteCenterFrequency(); applySettings(); } void DATVDemodGUI::on_rfBandwidth_changed(qint64 value) { - m_objChannelMarker.setBandwidth(value); - m_settings.m_rfBandwidth = m_objChannelMarker.getBandwidth(); + m_channelMarker.setBandwidth(value); + m_settings.m_rfBandwidth = m_channelMarker.getBandwidth(); applySettings(); } diff --git a/plugins/channelrx/demoddatv/datvdemodgui.h b/plugins/channelrx/demoddatv/datvdemodgui.h index a3028f206..1b2511983 100644 --- a/plugins/channelrx/demoddatv/datvdemodgui.h +++ b/plugins/channelrx/demoddatv/datvdemodgui.h @@ -58,6 +58,7 @@ public: virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } virtual bool getHidden() const { return m_settings.m_hidden; } + virtual ChannelMarker& getChannelMarker() { return m_channelMarker; } virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } @@ -109,7 +110,7 @@ private: PluginAPI* m_objPluginAPI; DeviceUISet* m_deviceUISet; - ChannelMarker m_objChannelMarker; + ChannelMarker m_channelMarker; RollupState m_rollupState; DATVDemod* m_datvDemod; MessageQueue m_inputMessageQueue; diff --git a/plugins/channelrx/demoddsd/dsddemod.cpp b/plugins/channelrx/demoddsd/dsddemod.cpp index 051c6fc66..6ea650bdd 100644 --- a/plugins/channelrx/demoddsd/dsddemod.cpp +++ b/plugins/channelrx/demoddsd/dsddemod.cpp @@ -98,6 +98,18 @@ DSDDemod::~DSDDemod() delete m_thread; } +void DSDDemod::setDeviceAPI(DeviceAPI *deviceAPI) +{ + if (deviceAPI != m_deviceAPI) + { + m_deviceAPI->removeChannelSinkAPI(this); + m_deviceAPI->removeChannelSink(this); + m_deviceAPI = deviceAPI; + m_deviceAPI->addChannelSink(this); + m_deviceAPI->addChannelSinkAPI(this); + } +} + uint32_t DSDDemod::getNumberOfDeviceStreams() const { return m_deviceAPI->getNbSourceStreams(); diff --git a/plugins/channelrx/demoddsd/dsddemod.h b/plugins/channelrx/demoddsd/dsddemod.h index e402cad24..620138826 100644 --- a/plugins/channelrx/demoddsd/dsddemod.h +++ b/plugins/channelrx/demoddsd/dsddemod.h @@ -65,6 +65,8 @@ public: DSDDemod(DeviceAPI *deviceAPI); virtual ~DSDDemod(); virtual void destroy() { delete this; } + virtual void setDeviceAPI(DeviceAPI *deviceAPI); + virtual DeviceAPI *getDeviceAPI() { return m_deviceAPI; } using BasebandSampleSink::feed; virtual void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool po); diff --git a/plugins/channelrx/demoddsd/dsddemodgui.h b/plugins/channelrx/demoddsd/dsddemodgui.h index 55453875f..618a853fc 100644 --- a/plugins/channelrx/demoddsd/dsddemodgui.h +++ b/plugins/channelrx/demoddsd/dsddemodgui.h @@ -60,6 +60,7 @@ public: virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } virtual bool getHidden() const { return m_settings.m_hidden; } + virtual ChannelMarker& getChannelMarker() { return m_channelMarker; } public slots: void channelMarkerChangedByCursor(); diff --git a/plugins/channelrx/demodfreedv/freedvdemod.cpp b/plugins/channelrx/demodfreedv/freedvdemod.cpp index b5ff705e5..dfa55ed6d 100644 --- a/plugins/channelrx/demodfreedv/freedvdemod.cpp +++ b/plugins/channelrx/demodfreedv/freedvdemod.cpp @@ -89,6 +89,18 @@ FreeDVDemod::~FreeDVDemod() delete m_thread; } +void FreeDVDemod::setDeviceAPI(DeviceAPI *deviceAPI) +{ + if (deviceAPI != m_deviceAPI) + { + m_deviceAPI->removeChannelSinkAPI(this); + m_deviceAPI->removeChannelSink(this); + m_deviceAPI = deviceAPI; + m_deviceAPI->addChannelSink(this); + m_deviceAPI->addChannelSinkAPI(this); + } +} + uint32_t FreeDVDemod::getNumberOfDeviceStreams() const { return m_deviceAPI->getNbSourceStreams(); diff --git a/plugins/channelrx/demodfreedv/freedvdemod.h b/plugins/channelrx/demodfreedv/freedvdemod.h index 8404998d8..078a840c8 100644 --- a/plugins/channelrx/demodfreedv/freedvdemod.h +++ b/plugins/channelrx/demodfreedv/freedvdemod.h @@ -77,6 +77,8 @@ public: FreeDVDemod(DeviceAPI *deviceAPI); virtual ~FreeDVDemod(); virtual void destroy() { delete this; } + virtual void setDeviceAPI(DeviceAPI *deviceAPI); + virtual DeviceAPI *getDeviceAPI() { return m_deviceAPI; } SpectrumVis *getSpectrumVis() { return &m_spectrumVis; } using BasebandSampleSink::feed; diff --git a/plugins/channelrx/demodfreedv/freedvdemodgui.h b/plugins/channelrx/demodfreedv/freedvdemodgui.h index c68bfaa15..4edb7d8fd 100644 --- a/plugins/channelrx/demodfreedv/freedvdemodgui.h +++ b/plugins/channelrx/demodfreedv/freedvdemodgui.h @@ -58,6 +58,7 @@ public: virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } virtual bool getHidden() const { return m_settings.m_hidden; } + virtual ChannelMarker& getChannelMarker() { return m_channelMarker; } public slots: void channelMarkerChangedByCursor(); diff --git a/plugins/channelrx/demodfreedv/freedvdemodgui.ui b/plugins/channelrx/demodfreedv/freedvdemodgui.ui index 4c1ab1c80..a9f8f818c 100644 --- a/plugins/channelrx/demodfreedv/freedvdemodgui.ui +++ b/plugins/channelrx/demodfreedv/freedvdemodgui.ui @@ -6,12 +6,12 @@ 0 0 - 442 - 523 + 412 + 478
- + 0 0 @@ -36,7 +36,7 @@ 0 0 - 441 + 410 171 @@ -661,12 +661,24 @@ - 10 + 0 170 - 218 + 410 284 + + + 0 + 0 + + + + + 410 + 0 + + Channel Spectrum @@ -688,6 +700,12 @@
+ + + 0 + 0 + + 200 @@ -715,12 +733,23 @@
gui/rollupcontents.h
1 + + ButtonSwitch + QToolButton +
gui/buttonswitch.h
+
LevelMeterSignalDB QWidget
gui/levelmeter.h
1
+ + ValueDialZ + QWidget +
gui/valuedialz.h
+ 1 +
GLSpectrum QWidget @@ -733,17 +762,6 @@
gui/glspectrumgui.h
1
- - ValueDialZ - QWidget -
gui/valuedialz.h
- 1 -
- - ButtonSwitch - QToolButton -
gui/buttonswitch.h
-
LevelMeterVU QWidget diff --git a/plugins/channelrx/demodnfm/nfmdemod.cpp b/plugins/channelrx/demodnfm/nfmdemod.cpp index 1239c0e36..b532c7fe7 100644 --- a/plugins/channelrx/demodnfm/nfmdemod.cpp +++ b/plugins/channelrx/demodnfm/nfmdemod.cpp @@ -102,6 +102,18 @@ NFMDemod::~NFMDemod() delete m_thread; } +void NFMDemod::setDeviceAPI(DeviceAPI *deviceAPI) +{ + if (deviceAPI != m_deviceAPI) + { + m_deviceAPI->removeChannelSinkAPI(this); + m_deviceAPI->removeChannelSink(this); + m_deviceAPI = deviceAPI; + m_deviceAPI->addChannelSink(this); + m_deviceAPI->addChannelSinkAPI(this); + } +} + uint32_t NFMDemod::getNumberOfDeviceStreams() const { return m_deviceAPI->getNbSourceStreams(); diff --git a/plugins/channelrx/demodnfm/nfmdemod.h b/plugins/channelrx/demodnfm/nfmdemod.h index 6581b053f..546df2274 100644 --- a/plugins/channelrx/demodnfm/nfmdemod.h +++ b/plugins/channelrx/demodnfm/nfmdemod.h @@ -64,6 +64,8 @@ public: NFMDemod(DeviceAPI *deviceAPI); virtual ~NFMDemod(); virtual void destroy() { delete this; } + virtual void setDeviceAPI(DeviceAPI *deviceAPI); + virtual DeviceAPI *getDeviceAPI() { return m_deviceAPI; } using BasebandSampleSink::feed; virtual void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool po); diff --git a/plugins/channelrx/demodnfm/nfmdemodgui.h b/plugins/channelrx/demodnfm/nfmdemodgui.h index 6ae22810b..e1681c039 100644 --- a/plugins/channelrx/demodnfm/nfmdemodgui.h +++ b/plugins/channelrx/demodnfm/nfmdemodgui.h @@ -38,6 +38,7 @@ public: virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } virtual bool getHidden() const { return m_settings.m_hidden; } + virtual ChannelMarker& getChannelMarker() { return m_channelMarker; } public slots: void channelMarkerChangedByCursor(); diff --git a/plugins/channelrx/demodpacket/packetdemod.cpp b/plugins/channelrx/demodpacket/packetdemod.cpp index 94886d91f..c7dfd2038 100644 --- a/plugins/channelrx/demodpacket/packetdemod.cpp +++ b/plugins/channelrx/demodpacket/packetdemod.cpp @@ -99,6 +99,18 @@ PacketDemod::~PacketDemod() delete m_basebandSink; } +void PacketDemod::setDeviceAPI(DeviceAPI *deviceAPI) +{ + if (deviceAPI != m_deviceAPI) + { + m_deviceAPI->removeChannelSinkAPI(this); + m_deviceAPI->removeChannelSink(this); + m_deviceAPI = deviceAPI; + m_deviceAPI->addChannelSink(this); + m_deviceAPI->addChannelSinkAPI(this); + } +} + uint32_t PacketDemod::getNumberOfDeviceStreams() const { return m_deviceAPI->getNbSourceStreams(); diff --git a/plugins/channelrx/demodpacket/packetdemod.h b/plugins/channelrx/demodpacket/packetdemod.h index 861ece372..296f1503a 100644 --- a/plugins/channelrx/demodpacket/packetdemod.h +++ b/plugins/channelrx/demodpacket/packetdemod.h @@ -67,6 +67,8 @@ public: PacketDemod(DeviceAPI *deviceAPI); virtual ~PacketDemod(); virtual void destroy() { delete this; } + virtual void setDeviceAPI(DeviceAPI *deviceAPI); + virtual DeviceAPI *getDeviceAPI() { return m_deviceAPI; } using BasebandSampleSink::feed; virtual void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool po); diff --git a/plugins/channelrx/demodpacket/packetdemodgui.h b/plugins/channelrx/demodpacket/packetdemodgui.h index e7ed265ed..80f860986 100644 --- a/plugins/channelrx/demodpacket/packetdemodgui.h +++ b/plugins/channelrx/demodpacket/packetdemodgui.h @@ -66,6 +66,7 @@ public: virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } virtual bool getHidden() const { return m_settings.m_hidden; } + virtual ChannelMarker& getChannelMarker() { return m_channelMarker; } public slots: void channelMarkerChangedByCursor(); diff --git a/plugins/channelrx/demodpager/pagerdemod.cpp b/plugins/channelrx/demodpager/pagerdemod.cpp index 8f7552375..eaa12a793 100644 --- a/plugins/channelrx/demodpager/pagerdemod.cpp +++ b/plugins/channelrx/demodpager/pagerdemod.cpp @@ -92,6 +92,18 @@ PagerDemod::~PagerDemod() delete m_basebandSink; } +void PagerDemod::setDeviceAPI(DeviceAPI *deviceAPI) +{ + if (deviceAPI != m_deviceAPI) + { + m_deviceAPI->removeChannelSinkAPI(this); + m_deviceAPI->removeChannelSink(this); + m_deviceAPI = deviceAPI; + m_deviceAPI->addChannelSink(this); + m_deviceAPI->addChannelSinkAPI(this); + } +} + uint32_t PagerDemod::getNumberOfDeviceStreams() const { return m_deviceAPI->getNbSourceStreams(); diff --git a/plugins/channelrx/demodpager/pagerdemod.h b/plugins/channelrx/demodpager/pagerdemod.h index 3561bf0a4..a283cc5d0 100644 --- a/plugins/channelrx/demodpager/pagerdemod.h +++ b/plugins/channelrx/demodpager/pagerdemod.h @@ -122,6 +122,8 @@ public: PagerDemod(DeviceAPI *deviceAPI); virtual ~PagerDemod(); virtual void destroy() { delete this; } + virtual void setDeviceAPI(DeviceAPI *deviceAPI); + virtual DeviceAPI *getDeviceAPI() { return m_deviceAPI; } using BasebandSampleSink::feed; virtual void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool po); diff --git a/plugins/channelrx/demodpager/pagerdemodgui.h b/plugins/channelrx/demodpager/pagerdemodgui.h index 5e9c3b718..b4b3b82e4 100644 --- a/plugins/channelrx/demodpager/pagerdemodgui.h +++ b/plugins/channelrx/demodpager/pagerdemodgui.h @@ -60,6 +60,7 @@ public: virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } virtual bool getHidden() const { return m_settings.m_hidden; } + virtual ChannelMarker& getChannelMarker() { return m_channelMarker; } public slots: void channelMarkerChangedByCursor(); diff --git a/plugins/channelrx/demodradiosonde/radiosondedemod.cpp b/plugins/channelrx/demodradiosonde/radiosondedemod.cpp index 6964c3d2b..86944c1ea 100644 --- a/plugins/channelrx/demodradiosonde/radiosondedemod.cpp +++ b/plugins/channelrx/demodradiosonde/radiosondedemod.cpp @@ -96,6 +96,18 @@ RadiosondeDemod::~RadiosondeDemod() delete m_basebandSink; } +void RadiosondeDemod::setDeviceAPI(DeviceAPI *deviceAPI) +{ + if (deviceAPI != m_deviceAPI) + { + m_deviceAPI->removeChannelSinkAPI(this); + m_deviceAPI->removeChannelSink(this); + m_deviceAPI = deviceAPI; + m_deviceAPI->addChannelSink(this); + m_deviceAPI->addChannelSinkAPI(this); + } +} + uint32_t RadiosondeDemod::getNumberOfDeviceStreams() const { return m_deviceAPI->getNbSourceStreams(); diff --git a/plugins/channelrx/demodradiosonde/radiosondedemod.h b/plugins/channelrx/demodradiosonde/radiosondedemod.h index 5703eac62..c103794aa 100644 --- a/plugins/channelrx/demodradiosonde/radiosondedemod.h +++ b/plugins/channelrx/demodradiosonde/radiosondedemod.h @@ -102,6 +102,8 @@ public: RadiosondeDemod(DeviceAPI *deviceAPI); virtual ~RadiosondeDemod(); virtual void destroy() { delete this; } + virtual void setDeviceAPI(DeviceAPI *deviceAPI); + virtual DeviceAPI *getDeviceAPI() { return m_deviceAPI; } using BasebandSampleSink::feed; virtual void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool po); diff --git a/plugins/channelrx/demodradiosonde/radiosondedemodgui.h b/plugins/channelrx/demodradiosonde/radiosondedemodgui.h index 0f64739b0..1958c2503 100644 --- a/plugins/channelrx/demodradiosonde/radiosondedemodgui.h +++ b/plugins/channelrx/demodradiosonde/radiosondedemodgui.h @@ -61,6 +61,7 @@ public: virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } virtual bool getHidden() const { return m_settings.m_hidden; } + virtual ChannelMarker& getChannelMarker() { return m_channelMarker; } public slots: void channelMarkerChangedByCursor(); diff --git a/plugins/channelrx/demodssb/ssbdemod.cpp b/plugins/channelrx/demodssb/ssbdemod.cpp index a3ebb7910..3cd877eb0 100644 --- a/plugins/channelrx/demodssb/ssbdemod.cpp +++ b/plugins/channelrx/demodssb/ssbdemod.cpp @@ -96,6 +96,18 @@ SSBDemod::~SSBDemod() delete m_thread; } +void SSBDemod::setDeviceAPI(DeviceAPI *deviceAPI) +{ + if (deviceAPI != m_deviceAPI) + { + m_deviceAPI->removeChannelSinkAPI(this); + m_deviceAPI->removeChannelSink(this); + m_deviceAPI = deviceAPI; + m_deviceAPI->addChannelSink(this); + m_deviceAPI->addChannelSinkAPI(this); + } +} + uint32_t SSBDemod::getNumberOfDeviceStreams() const { return m_deviceAPI->getNbSourceStreams(); diff --git a/plugins/channelrx/demodssb/ssbdemod.h b/plugins/channelrx/demodssb/ssbdemod.h index 3345cbb05..119dc700d 100644 --- a/plugins/channelrx/demodssb/ssbdemod.h +++ b/plugins/channelrx/demodssb/ssbdemod.h @@ -66,6 +66,8 @@ public: SSBDemod(DeviceAPI *deviceAPI); virtual ~SSBDemod(); virtual void destroy() { delete this; } + virtual void setDeviceAPI(DeviceAPI *deviceAPI); + virtual DeviceAPI *getDeviceAPI() { return m_deviceAPI; } SpectrumVis *getSpectrumVis() { return &m_spectrumVis; } using BasebandSampleSink::feed; diff --git a/plugins/channelrx/demodssb/ssbdemodgui.h b/plugins/channelrx/demodssb/ssbdemodgui.h index f5da7c3ed..3134732f5 100644 --- a/plugins/channelrx/demodssb/ssbdemodgui.h +++ b/plugins/channelrx/demodssb/ssbdemodgui.h @@ -41,6 +41,7 @@ public: virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } virtual bool getHidden() const { return m_settings.m_hidden; } + virtual ChannelMarker& getChannelMarker() { return m_channelMarker; } public slots: void channelMarkerChangedByCursor(); diff --git a/plugins/channelrx/demodssb/ssbdemodgui.ui b/plugins/channelrx/demodssb/ssbdemodgui.ui index 1bc6325e8..6896c617b 100644 --- a/plugins/channelrx/demodssb/ssbdemodgui.ui +++ b/plugins/channelrx/demodssb/ssbdemodgui.ui @@ -969,17 +969,25 @@
- + + + + 0 + 0 + + + + + 0 + 30 + + + - - ButtonSwitch - QToolButton -
gui/buttonswitch.h
-
RollupContents QWidget @@ -987,10 +995,9 @@ 1 - ValueDialZ - QWidget -
gui/valuedialz.h
- 1 + ButtonSwitch + QToolButton +
gui/buttonswitch.h
LevelMeterSignalDB @@ -998,6 +1005,12 @@
gui/levelmeter.h
1
+ + ValueDialZ + QWidget +
gui/valuedialz.h
+ 1 +
GLSpectrum QWidget diff --git a/plugins/channelrx/demodvor/vordemod.cpp b/plugins/channelrx/demodvor/vordemod.cpp index efb88b475..f09204d93 100644 --- a/plugins/channelrx/demodvor/vordemod.cpp +++ b/plugins/channelrx/demodvor/vordemod.cpp @@ -96,6 +96,18 @@ VORDemod::~VORDemod() delete m_basebandSink; } +void VORDemod::setDeviceAPI(DeviceAPI *deviceAPI) +{ + if (deviceAPI != m_deviceAPI) + { + m_deviceAPI->removeChannelSinkAPI(this); + m_deviceAPI->removeChannelSink(this); + m_deviceAPI = deviceAPI; + m_deviceAPI->addChannelSink(this); + m_deviceAPI->addChannelSinkAPI(this); + } +} + uint32_t VORDemod::getNumberOfDeviceStreams() const { return m_deviceAPI->getNbSourceStreams(); diff --git a/plugins/channelrx/demodvor/vordemod.h b/plugins/channelrx/demodvor/vordemod.h index 8ffd638da..ef16083d6 100644 --- a/plugins/channelrx/demodvor/vordemod.h +++ b/plugins/channelrx/demodvor/vordemod.h @@ -65,6 +65,8 @@ public: VORDemod(DeviceAPI *deviceAPI); virtual ~VORDemod(); virtual void destroy() { delete this; } + virtual void setDeviceAPI(DeviceAPI *deviceAPI); + virtual DeviceAPI *getDeviceAPI() { return m_deviceAPI; } using BasebandSampleSink::feed; virtual void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool po); diff --git a/plugins/channelrx/demodvor/vordemodgui.h b/plugins/channelrx/demodvor/vordemodgui.h index 875cfdf9c..f368a7b54 100644 --- a/plugins/channelrx/demodvor/vordemodgui.h +++ b/plugins/channelrx/demodvor/vordemodgui.h @@ -219,6 +219,7 @@ public: virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } virtual bool getHidden() const { return m_settings.m_hidden; } + virtual ChannelMarker& getChannelMarker() { return m_channelMarker; } void selectVOR(VORGUI *vorGUI, bool selected); diff --git a/plugins/channelrx/demodvorsc/vordemodsc.cpp b/plugins/channelrx/demodvorsc/vordemodsc.cpp index d4d3ea223..fb30218d9 100644 --- a/plugins/channelrx/demodvorsc/vordemodsc.cpp +++ b/plugins/channelrx/demodvorsc/vordemodsc.cpp @@ -98,6 +98,18 @@ VORDemodSC::~VORDemodSC() delete m_basebandSink; } +void VORDemodSC::setDeviceAPI(DeviceAPI *deviceAPI) +{ + if (deviceAPI != m_deviceAPI) + { + m_deviceAPI->removeChannelSinkAPI(this); + m_deviceAPI->removeChannelSink(this); + m_deviceAPI = deviceAPI; + m_deviceAPI->addChannelSink(this); + m_deviceAPI->addChannelSinkAPI(this); + } +} + uint32_t VORDemodSC::getNumberOfDeviceStreams() const { return m_deviceAPI->getNbSourceStreams(); diff --git a/plugins/channelrx/demodvorsc/vordemodsc.h b/plugins/channelrx/demodvorsc/vordemodsc.h index 5bca0a6df..3741eb873 100644 --- a/plugins/channelrx/demodvorsc/vordemodsc.h +++ b/plugins/channelrx/demodvorsc/vordemodsc.h @@ -65,6 +65,8 @@ public: VORDemodSC(DeviceAPI *deviceAPI); virtual ~VORDemodSC(); virtual void destroy() { delete this; } + virtual void setDeviceAPI(DeviceAPI *deviceAPI); + virtual DeviceAPI *getDeviceAPI() { return m_deviceAPI; } using BasebandSampleSink::feed; virtual void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool po); diff --git a/plugins/channelrx/demodvorsc/vordemodscgui.h b/plugins/channelrx/demodvorsc/vordemodscgui.h index a1534a077..557a2775d 100644 --- a/plugins/channelrx/demodvorsc/vordemodscgui.h +++ b/plugins/channelrx/demodvorsc/vordemodscgui.h @@ -56,6 +56,7 @@ public: virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } virtual bool getHidden() const { return m_settings.m_hidden; } + virtual ChannelMarker& getChannelMarker() { return m_channelMarker; } public slots: void channelMarkerChangedByCursor(); diff --git a/plugins/channelrx/demodwfm/wfmdemod.cpp b/plugins/channelrx/demodwfm/wfmdemod.cpp index dfda76957..aab213164 100644 --- a/plugins/channelrx/demodwfm/wfmdemod.cpp +++ b/plugins/channelrx/demodwfm/wfmdemod.cpp @@ -99,6 +99,18 @@ WFMDemod::~WFMDemod() delete m_thread; } +void WFMDemod::setDeviceAPI(DeviceAPI *deviceAPI) +{ + if (deviceAPI != m_deviceAPI) + { + m_deviceAPI->removeChannelSinkAPI(this); + m_deviceAPI->removeChannelSink(this); + m_deviceAPI = deviceAPI; + m_deviceAPI->addChannelSink(this); + m_deviceAPI->addChannelSinkAPI(this); + } +} + uint32_t WFMDemod::getNumberOfDeviceStreams() const { return m_deviceAPI->getNbSourceStreams(); diff --git a/plugins/channelrx/demodwfm/wfmdemod.h b/plugins/channelrx/demodwfm/wfmdemod.h index beef2586b..9002f6932 100644 --- a/plugins/channelrx/demodwfm/wfmdemod.h +++ b/plugins/channelrx/demodwfm/wfmdemod.h @@ -63,6 +63,8 @@ public: WFMDemod(DeviceAPI *deviceAPI); virtual ~WFMDemod(); virtual void destroy() { delete this; } + virtual void setDeviceAPI(DeviceAPI *deviceAPI); + virtual DeviceAPI *getDeviceAPI() { return m_deviceAPI; } using BasebandSampleSink::feed; virtual void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool po); diff --git a/plugins/channelrx/demodwfm/wfmdemodgui.h b/plugins/channelrx/demodwfm/wfmdemodgui.h index 3c83d13d8..772b4d6f0 100644 --- a/plugins/channelrx/demodwfm/wfmdemodgui.h +++ b/plugins/channelrx/demodwfm/wfmdemodgui.h @@ -36,6 +36,7 @@ public: virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } virtual bool getHidden() const { return m_settings.m_hidden; } + virtual ChannelMarker& getChannelMarker() { return m_channelMarker; } public slots: void channelMarkerChangedByCursor(); diff --git a/plugins/channelrx/filesink/filesink.cpp b/plugins/channelrx/filesink/filesink.cpp index f9491275b..96b492628 100644 --- a/plugins/channelrx/filesink/filesink.cpp +++ b/plugins/channelrx/filesink/filesink.cpp @@ -101,6 +101,18 @@ FileSink::~FileSink() delete m_basebandSink; } +void FileSink::setDeviceAPI(DeviceAPI *deviceAPI) +{ + if (deviceAPI != m_deviceAPI) + { + m_deviceAPI->removeChannelSinkAPI(this); + m_deviceAPI->removeChannelSink(this); + m_deviceAPI = deviceAPI; + m_deviceAPI->addChannelSink(this); + m_deviceAPI->addChannelSinkAPI(this); + } +} + void FileSink::setMessageQueueToGUI(MessageQueue* queue) { ChannelAPI::setMessageQueueToGUI(queue); diff --git a/plugins/channelrx/filesink/filesink.h b/plugins/channelrx/filesink/filesink.h index 679d2a04a..a2b97d9a7 100644 --- a/plugins/channelrx/filesink/filesink.h +++ b/plugins/channelrx/filesink/filesink.h @@ -84,6 +84,8 @@ public: FileSink(DeviceAPI *deviceAPI); virtual ~FileSink(); virtual void destroy() { delete this; } + virtual void setDeviceAPI(DeviceAPI *deviceAPI); + virtual DeviceAPI *getDeviceAPI() { return m_deviceAPI; } using BasebandSampleSink::feed; virtual void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool po); diff --git a/plugins/channelrx/filesink/filesinkgui.h b/plugins/channelrx/filesink/filesinkgui.h index 10a8da955..d16d37e0f 100644 --- a/plugins/channelrx/filesink/filesinkgui.h +++ b/plugins/channelrx/filesink/filesinkgui.h @@ -57,6 +57,7 @@ public: virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } virtual bool getHidden() const { return m_settings.m_hidden; } + virtual ChannelMarker& getChannelMarker() { return m_channelMarker; } public slots: void channelMarkerChangedByCursor(); diff --git a/plugins/channelrx/freqtracker/freqtracker.cpp b/plugins/channelrx/freqtracker/freqtracker.cpp index 934e9f146..dfc6b753f 100644 --- a/plugins/channelrx/freqtracker/freqtracker.cpp +++ b/plugins/channelrx/freqtracker/freqtracker.cpp @@ -101,6 +101,18 @@ FreqTracker::~FreqTracker() delete m_thread; } +void FreqTracker::setDeviceAPI(DeviceAPI *deviceAPI) +{ + if (deviceAPI != m_deviceAPI) + { + m_deviceAPI->removeChannelSinkAPI(this); + m_deviceAPI->removeChannelSink(this); + m_deviceAPI = deviceAPI; + m_deviceAPI->addChannelSink(this); + m_deviceAPI->addChannelSinkAPI(this); + } +} + uint32_t FreqTracker::getNumberOfDeviceStreams() const { return m_deviceAPI->getNbSourceStreams(); diff --git a/plugins/channelrx/freqtracker/freqtracker.h b/plugins/channelrx/freqtracker/freqtracker.h index e77c60dd5..628cd76cb 100644 --- a/plugins/channelrx/freqtracker/freqtracker.h +++ b/plugins/channelrx/freqtracker/freqtracker.h @@ -63,6 +63,8 @@ public: FreqTracker(DeviceAPI *deviceAPI); virtual ~FreqTracker(); virtual void destroy() { delete this; } + virtual void setDeviceAPI(DeviceAPI *deviceAPI); + virtual DeviceAPI *getDeviceAPI() { return m_deviceAPI; } using BasebandSampleSink::feed; virtual void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool po); diff --git a/plugins/channelrx/freqtracker/freqtrackergui.h b/plugins/channelrx/freqtracker/freqtrackergui.h index eb3cac976..702b61c25 100644 --- a/plugins/channelrx/freqtracker/freqtrackergui.h +++ b/plugins/channelrx/freqtracker/freqtrackergui.h @@ -57,6 +57,7 @@ public: virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } virtual bool getHidden() const { return m_settings.m_hidden; } + virtual ChannelMarker& getChannelMarker() { return m_channelMarker; } public slots: void channelMarkerChangedByCursor(); diff --git a/plugins/channelrx/localsink/localsink.cpp b/plugins/channelrx/localsink/localsink.cpp index abfd2f096..758f9e1fa 100644 --- a/plugins/channelrx/localsink/localsink.cpp +++ b/plugins/channelrx/localsink/localsink.cpp @@ -93,6 +93,18 @@ LocalSink::~LocalSink() delete m_thread; } +void LocalSink::setDeviceAPI(DeviceAPI *deviceAPI) +{ + if (deviceAPI != m_deviceAPI) + { + m_deviceAPI->removeChannelSinkAPI(this); + m_deviceAPI->removeChannelSink(this); + m_deviceAPI = deviceAPI; + m_deviceAPI->addChannelSink(this); + m_deviceAPI->addChannelSinkAPI(this); + } +} + uint32_t LocalSink::getNumberOfDeviceStreams() const { return m_deviceAPI->getNbSourceStreams(); diff --git a/plugins/channelrx/localsink/localsink.h b/plugins/channelrx/localsink/localsink.h index 9dce4752a..d25a2972a 100644 --- a/plugins/channelrx/localsink/localsink.h +++ b/plugins/channelrx/localsink/localsink.h @@ -64,6 +64,8 @@ public: LocalSink(DeviceAPI *deviceAPI); virtual ~LocalSink(); virtual void destroy() { delete this; } + virtual void setDeviceAPI(DeviceAPI *deviceAPI); + virtual DeviceAPI *getDeviceAPI() { return m_deviceAPI; } using BasebandSampleSink::feed; virtual void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool po); diff --git a/plugins/channelrx/localsink/localsinkgui.h b/plugins/channelrx/localsink/localsinkgui.h index a0f8c2b1c..55a7344cd 100644 --- a/plugins/channelrx/localsink/localsinkgui.h +++ b/plugins/channelrx/localsink/localsinkgui.h @@ -56,6 +56,7 @@ public: virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } virtual bool getHidden() const { return m_settings.m_hidden; } + virtual ChannelMarker& getChannelMarker() { return m_channelMarker; } private: Ui::LocalSinkGUI* ui; diff --git a/plugins/channelrx/noisefigure/noisefigure.cpp b/plugins/channelrx/noisefigure/noisefigure.cpp index 0953a028c..ff5f3160d 100644 --- a/plugins/channelrx/noisefigure/noisefigure.cpp +++ b/plugins/channelrx/noisefigure/noisefigure.cpp @@ -110,6 +110,18 @@ NoiseFigure::~NoiseFigure() delete m_basebandSink; } +void NoiseFigure::setDeviceAPI(DeviceAPI *deviceAPI) +{ + if (deviceAPI != m_deviceAPI) + { + m_deviceAPI->removeChannelSinkAPI(this); + m_deviceAPI->removeChannelSink(this); + m_deviceAPI = deviceAPI; + m_deviceAPI->addChannelSink(this); + m_deviceAPI->addChannelSinkAPI(this); + } +} + uint32_t NoiseFigure::getNumberOfDeviceStreams() const { return m_deviceAPI->getNbSourceStreams(); diff --git a/plugins/channelrx/noisefigure/noisefigure.h b/plugins/channelrx/noisefigure/noisefigure.h index 65cbb3c41..f64437436 100644 --- a/plugins/channelrx/noisefigure/noisefigure.h +++ b/plugins/channelrx/noisefigure/noisefigure.h @@ -172,6 +172,8 @@ public: NoiseFigure(DeviceAPI *deviceAPI); virtual ~NoiseFigure(); virtual void destroy() { delete this; } + virtual void setDeviceAPI(DeviceAPI *deviceAPI); + virtual DeviceAPI *getDeviceAPI() { return m_deviceAPI; } using BasebandSampleSink::feed; virtual void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool po); diff --git a/plugins/channelrx/noisefigure/noisefiguregui.h b/plugins/channelrx/noisefigure/noisefiguregui.h index c04a7e12e..249697592 100644 --- a/plugins/channelrx/noisefigure/noisefiguregui.h +++ b/plugins/channelrx/noisefigure/noisefiguregui.h @@ -63,6 +63,7 @@ public: virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } virtual bool getHidden() const { return m_settings.m_hidden; } + virtual ChannelMarker& getChannelMarker() { return m_channelMarker; } public slots: void channelMarkerChangedByCursor(); diff --git a/plugins/channelrx/radioastronomy/radioastronomy.cpp b/plugins/channelrx/radioastronomy/radioastronomy.cpp index cf06ca0dd..98c8bf794 100644 --- a/plugins/channelrx/radioastronomy/radioastronomy.cpp +++ b/plugins/channelrx/radioastronomy/radioastronomy.cpp @@ -154,6 +154,18 @@ RadioAstronomy::~RadioAstronomy() delete m_worker; } +void RadioAstronomy::setDeviceAPI(DeviceAPI *deviceAPI) +{ + if (deviceAPI != m_deviceAPI) + { + m_deviceAPI->removeChannelSinkAPI(this); + m_deviceAPI->removeChannelSink(this); + m_deviceAPI = deviceAPI; + m_deviceAPI->addChannelSink(this); + m_deviceAPI->addChannelSinkAPI(this); + } +} + uint32_t RadioAstronomy::getNumberOfDeviceStreams() const { return m_deviceAPI->getNbSourceStreams(); diff --git a/plugins/channelrx/radioastronomy/radioastronomy.h b/plugins/channelrx/radioastronomy/radioastronomy.h index 59b3a1420..aac4fc559 100644 --- a/plugins/channelrx/radioastronomy/radioastronomy.h +++ b/plugins/channelrx/radioastronomy/radioastronomy.h @@ -346,6 +346,8 @@ public: RadioAstronomy(DeviceAPI *deviceAPI); virtual ~RadioAstronomy(); virtual void destroy() { delete this; } + virtual void setDeviceAPI(DeviceAPI *deviceAPI); + virtual DeviceAPI *getDeviceAPI() { return m_deviceAPI; } using BasebandSampleSink::feed; virtual void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool po); diff --git a/plugins/channelrx/radioastronomy/radioastronomygui.h b/plugins/channelrx/radioastronomy/radioastronomygui.h index d82e265d4..2ae11b487 100644 --- a/plugins/channelrx/radioastronomy/radioastronomygui.h +++ b/plugins/channelrx/radioastronomy/radioastronomygui.h @@ -206,6 +206,7 @@ public: virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } virtual bool getHidden() const { return m_settings.m_hidden; } + virtual ChannelMarker& getChannelMarker() { return m_channelMarker; } public slots: void channelMarkerChangedByCursor(); diff --git a/plugins/channelrx/radioclock/radioclock.cpp b/plugins/channelrx/radioclock/radioclock.cpp index 14b9437ef..bf0859890 100644 --- a/plugins/channelrx/radioclock/radioclock.cpp +++ b/plugins/channelrx/radioclock/radioclock.cpp @@ -99,6 +99,18 @@ RadioClock::~RadioClock() delete m_basebandSink; } +void RadioClock::setDeviceAPI(DeviceAPI *deviceAPI) +{ + if (deviceAPI != m_deviceAPI) + { + m_deviceAPI->removeChannelSinkAPI(this); + m_deviceAPI->removeChannelSink(this); + m_deviceAPI = deviceAPI; + m_deviceAPI->addChannelSink(this); + m_deviceAPI->addChannelSinkAPI(this); + } +} + uint32_t RadioClock::getNumberOfDeviceStreams() const { return m_deviceAPI->getNbSourceStreams(); diff --git a/plugins/channelrx/radioclock/radioclock.h b/plugins/channelrx/radioclock/radioclock.h index 2a8714b78..af0558895 100644 --- a/plugins/channelrx/radioclock/radioclock.h +++ b/plugins/channelrx/radioclock/radioclock.h @@ -112,6 +112,8 @@ public: RadioClock(DeviceAPI *deviceAPI); virtual ~RadioClock(); virtual void destroy() { delete this; } + virtual void setDeviceAPI(DeviceAPI *deviceAPI); + virtual DeviceAPI *getDeviceAPI() { return m_deviceAPI; } using BasebandSampleSink::feed; virtual void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool po); diff --git a/plugins/channelrx/radioclock/radioclockgui.h b/plugins/channelrx/radioclock/radioclockgui.h index 74259e465..73b04177a 100644 --- a/plugins/channelrx/radioclock/radioclockgui.h +++ b/plugins/channelrx/radioclock/radioclockgui.h @@ -58,6 +58,7 @@ public: virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } virtual bool getHidden() const { return m_settings.m_hidden; } + virtual ChannelMarker& getChannelMarker() { return m_channelMarker; } public slots: void channelMarkerChangedByCursor(); diff --git a/plugins/channelrx/remotesink/remotesink.cpp b/plugins/channelrx/remotesink/remotesink.cpp index 00e6ef750..bbba6c7f4 100644 --- a/plugins/channelrx/remotesink/remotesink.cpp +++ b/plugins/channelrx/remotesink/remotesink.cpp @@ -99,6 +99,18 @@ RemoteSink::~RemoteSink() delete m_basebandSink; } +void RemoteSink::setDeviceAPI(DeviceAPI *deviceAPI) +{ + if (deviceAPI != m_deviceAPI) + { + m_deviceAPI->removeChannelSinkAPI(this); + m_deviceAPI->removeChannelSink(this); + m_deviceAPI = deviceAPI; + m_deviceAPI->addChannelSink(this); + m_deviceAPI->addChannelSinkAPI(this); + } +} + uint32_t RemoteSink::getNumberOfDeviceStreams() const { return m_deviceAPI->getNbSourceStreams(); diff --git a/plugins/channelrx/remotesink/remotesink.h b/plugins/channelrx/remotesink/remotesink.h index 0764449da..476ed6677 100644 --- a/plugins/channelrx/remotesink/remotesink.h +++ b/plugins/channelrx/remotesink/remotesink.h @@ -65,6 +65,8 @@ public: RemoteSink(DeviceAPI *deviceAPI); virtual ~RemoteSink(); virtual void destroy() { delete this; } + virtual void setDeviceAPI(DeviceAPI *deviceAPI); + virtual DeviceAPI *getDeviceAPI() { return m_deviceAPI; } using BasebandSampleSink::feed; virtual void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool po); diff --git a/plugins/channelrx/remotesink/remotesinkgui.h b/plugins/channelrx/remotesink/remotesinkgui.h index 84332a6fe..60354c528 100644 --- a/plugins/channelrx/remotesink/remotesinkgui.h +++ b/plugins/channelrx/remotesink/remotesinkgui.h @@ -56,6 +56,7 @@ public: virtual QColor getTitleColor() const { return m_settings.m_rgbColor; } virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } virtual bool getHidden() const { return m_settings.m_hidden; } + virtual ChannelMarker& getChannelMarker() { return m_channelMarker; } private: Ui::RemoteSinkGUI* ui; diff --git a/plugins/channelrx/sigmffilesink/sigmffilesink.cpp b/plugins/channelrx/sigmffilesink/sigmffilesink.cpp index ffe624604..a2879bead 100644 --- a/plugins/channelrx/sigmffilesink/sigmffilesink.cpp +++ b/plugins/channelrx/sigmffilesink/sigmffilesink.cpp @@ -101,6 +101,18 @@ SigMFFileSink::~SigMFFileSink() delete m_basebandSink; } +void SigMFFileSink::setDeviceAPI(DeviceAPI *deviceAPI) +{ + if (deviceAPI != m_deviceAPI) + { + m_deviceAPI->removeChannelSinkAPI(this); + m_deviceAPI->removeChannelSink(this); + m_deviceAPI = deviceAPI; + m_deviceAPI->addChannelSink(this); + m_deviceAPI->addChannelSinkAPI(this); + } +} + void SigMFFileSink::setMessageQueueToGUI(MessageQueue* queue) { ChannelAPI::setMessageQueueToGUI(queue); diff --git a/plugins/channelrx/sigmffilesink/sigmffilesink.h b/plugins/channelrx/sigmffilesink/sigmffilesink.h index 3d8b4e82c..4923c9fc4 100644 --- a/plugins/channelrx/sigmffilesink/sigmffilesink.h +++ b/plugins/channelrx/sigmffilesink/sigmffilesink.h @@ -84,6 +84,8 @@ public: SigMFFileSink(DeviceAPI *deviceAPI); virtual ~SigMFFileSink(); virtual void destroy() { delete this; } + virtual void setDeviceAPI(DeviceAPI *deviceAPI); + virtual DeviceAPI *getDeviceAPI() { return m_deviceAPI; } using BasebandSampleSink::feed; virtual void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool po); diff --git a/plugins/channelrx/sigmffilesink/sigmffilesinkgui.h b/plugins/channelrx/sigmffilesink/sigmffilesinkgui.h index 2813e6f44..e97e4d006 100644 --- a/plugins/channelrx/sigmffilesink/sigmffilesinkgui.h +++ b/plugins/channelrx/sigmffilesink/sigmffilesinkgui.h @@ -57,6 +57,7 @@ public: virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } virtual bool getHidden() const { return m_settings.m_hidden; } + virtual ChannelMarker& getChannelMarker() { return m_channelMarker; } public slots: void channelMarkerChangedByCursor(); diff --git a/plugins/channelrx/udpsink/udpsink.cpp b/plugins/channelrx/udpsink/udpsink.cpp index 4db88391c..81a90f9c3 100644 --- a/plugins/channelrx/udpsink/udpsink.cpp +++ b/plugins/channelrx/udpsink/udpsink.cpp @@ -92,6 +92,18 @@ UDPSink::~UDPSink() delete m_thread; } +void UDPSink::setDeviceAPI(DeviceAPI *deviceAPI) +{ + if (deviceAPI != m_deviceAPI) + { + m_deviceAPI->removeChannelSinkAPI(this); + m_deviceAPI->removeChannelSink(this); + m_deviceAPI = deviceAPI; + m_deviceAPI->addChannelSink(this); + m_deviceAPI->addChannelSinkAPI(this); + } +} + uint32_t UDPSink::getNumberOfDeviceStreams() const { return m_deviceAPI->getNbSourceStreams(); diff --git a/plugins/channelrx/udpsink/udpsink.h b/plugins/channelrx/udpsink/udpsink.h index c70f6bd43..6a5dac3db 100644 --- a/plugins/channelrx/udpsink/udpsink.h +++ b/plugins/channelrx/udpsink/udpsink.h @@ -63,6 +63,8 @@ public: UDPSink(DeviceAPI *deviceAPI); virtual ~UDPSink(); virtual void destroy() { delete this; } + virtual void setDeviceAPI(DeviceAPI *deviceAPI); + virtual DeviceAPI *getDeviceAPI() { return m_deviceAPI; } SpectrumVis *getSpectrumVis() { return &m_spectrumVis; } void enableSpectrum(bool enable); diff --git a/plugins/channelrx/udpsink/udpsinkgui.h b/plugins/channelrx/udpsink/udpsinkgui.h index cce4e24be..fe476b533 100644 --- a/plugins/channelrx/udpsink/udpsinkgui.h +++ b/plugins/channelrx/udpsink/udpsinkgui.h @@ -57,6 +57,7 @@ public: virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } virtual bool getHidden() const { return m_settings.m_hidden; } + virtual ChannelMarker& getChannelMarker() { return m_channelMarker; } public slots: void channelMarkerChangedByCursor(); diff --git a/plugins/channeltx/filesource/filesource.cpp b/plugins/channeltx/filesource/filesource.cpp index 2bb39a9b0..fcce597f2 100644 --- a/plugins/channeltx/filesource/filesource.cpp +++ b/plugins/channeltx/filesource/filesource.cpp @@ -92,6 +92,18 @@ FileSource::~FileSource() delete m_thread; } +void FileSource::setDeviceAPI(DeviceAPI *deviceAPI) +{ + if (deviceAPI != m_deviceAPI) + { + m_deviceAPI->removeChannelSourceAPI(this); + m_deviceAPI->removeChannelSource(this); + m_deviceAPI = deviceAPI; + m_deviceAPI->addChannelSource(this); + m_deviceAPI->addChannelSinkAPI(this); + } +} + void FileSource::start() { qDebug("FileSource::start"); diff --git a/plugins/channeltx/filesource/filesource.h b/plugins/channeltx/filesource/filesource.h index 9b7db6307..6a7de9be0 100644 --- a/plugins/channeltx/filesource/filesource.h +++ b/plugins/channeltx/filesource/filesource.h @@ -147,6 +147,8 @@ public: FileSource(DeviceAPI *deviceAPI); virtual ~FileSource(); virtual void destroy() { delete this; } + virtual void setDeviceAPI(DeviceAPI *deviceAPI); + virtual DeviceAPI *getDeviceAPI() { return m_deviceAPI; } virtual void start(); virtual void stop(); diff --git a/plugins/channeltx/filesource/filesourcegui.h b/plugins/channeltx/filesource/filesourcegui.h index 7f5c227c7..8eec2f10f 100644 --- a/plugins/channeltx/filesource/filesourcegui.h +++ b/plugins/channeltx/filesource/filesourcegui.h @@ -53,6 +53,7 @@ public: virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } virtual bool getHidden() const { return m_settings.m_hidden; } + virtual ChannelMarker& getChannelMarker() { return m_channelMarker; } public slots: void channelMarkerChangedByCursor(); diff --git a/plugins/channeltx/localsource/localsource.cpp b/plugins/channeltx/localsource/localsource.cpp index 7e1bef605..05c3d795f 100644 --- a/plugins/channeltx/localsource/localsource.cpp +++ b/plugins/channeltx/localsource/localsource.cpp @@ -84,6 +84,18 @@ LocalSource::~LocalSource() delete m_thread; } +void LocalSource::setDeviceAPI(DeviceAPI *deviceAPI) +{ + if (deviceAPI != m_deviceAPI) + { + m_deviceAPI->removeChannelSourceAPI(this); + m_deviceAPI->removeChannelSource(this); + m_deviceAPI = deviceAPI; + m_deviceAPI->addChannelSource(this); + m_deviceAPI->addChannelSinkAPI(this); + } +} + void LocalSource::start() { qDebug("LocalSource::start"); diff --git a/plugins/channeltx/localsource/localsource.h b/plugins/channeltx/localsource/localsource.h index 889ebc125..37963a533 100644 --- a/plugins/channeltx/localsource/localsource.h +++ b/plugins/channeltx/localsource/localsource.h @@ -63,6 +63,8 @@ public: LocalSource(DeviceAPI *deviceAPI); virtual ~LocalSource(); virtual void destroy() { delete this; } + virtual void setDeviceAPI(DeviceAPI *deviceAPI); + virtual DeviceAPI *getDeviceAPI() { return m_deviceAPI; } virtual void start(); virtual void stop(); diff --git a/plugins/channeltx/localsource/localsourcegui.h b/plugins/channeltx/localsource/localsourcegui.h index 3dc9b8908..52f358eed 100644 --- a/plugins/channeltx/localsource/localsourcegui.h +++ b/plugins/channeltx/localsource/localsourcegui.h @@ -56,6 +56,7 @@ public: virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } virtual bool getHidden() const { return m_settings.m_hidden; } + virtual ChannelMarker& getChannelMarker() { return m_channelMarker; } private: Ui::LocalSourceGUI* ui; diff --git a/plugins/channeltx/mod802.15.4/ieee_802_15_4_mod.cpp b/plugins/channeltx/mod802.15.4/ieee_802_15_4_mod.cpp index 029063a44..592e7f94e 100644 --- a/plugins/channeltx/mod802.15.4/ieee_802_15_4_mod.cpp +++ b/plugins/channeltx/mod802.15.4/ieee_802_15_4_mod.cpp @@ -97,6 +97,18 @@ IEEE_802_15_4_Mod::~IEEE_802_15_4_Mod() delete m_thread; } +void IEEE_802_15_4_Mod::setDeviceAPI(DeviceAPI *deviceAPI) +{ + if (deviceAPI != m_deviceAPI) + { + m_deviceAPI->removeChannelSourceAPI(this); + m_deviceAPI->removeChannelSource(this); + m_deviceAPI = deviceAPI; + m_deviceAPI->addChannelSource(this); + m_deviceAPI->addChannelSinkAPI(this); + } +} + void IEEE_802_15_4_Mod::start() { qDebug("IEEE_802_15_4_Mod::start"); diff --git a/plugins/channeltx/mod802.15.4/ieee_802_15_4_mod.h b/plugins/channeltx/mod802.15.4/ieee_802_15_4_mod.h index 8068e7f67..6dd3bd28a 100644 --- a/plugins/channeltx/mod802.15.4/ieee_802_15_4_mod.h +++ b/plugins/channeltx/mod802.15.4/ieee_802_15_4_mod.h @@ -91,6 +91,8 @@ public: IEEE_802_15_4_Mod(DeviceAPI *deviceAPI); ~IEEE_802_15_4_Mod(); virtual void destroy() { delete this; } + virtual void setDeviceAPI(DeviceAPI *deviceAPI); + virtual DeviceAPI *getDeviceAPI() { return m_deviceAPI; } virtual void start(); virtual void stop(); diff --git a/plugins/channeltx/mod802.15.4/ieee_802_15_4_modgui.h b/plugins/channeltx/mod802.15.4/ieee_802_15_4_modgui.h index 2071794f8..b8f7ac261 100644 --- a/plugins/channeltx/mod802.15.4/ieee_802_15_4_modgui.h +++ b/plugins/channeltx/mod802.15.4/ieee_802_15_4_modgui.h @@ -62,6 +62,7 @@ public: virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } virtual bool getHidden() const { return m_settings.m_hidden; } + virtual ChannelMarker& getChannelMarker() { return m_channelMarker; } public slots: void channelMarkerChangedByCursor(); diff --git a/plugins/channeltx/mod802.15.4/ieee_802_15_4_modgui.ui b/plugins/channeltx/mod802.15.4/ieee_802_15_4_modgui.ui index 6e565edc6..472cb688b 100644 --- a/plugins/channeltx/mod802.15.4/ieee_802_15_4_modgui.ui +++ b/plugins/channeltx/mod802.15.4/ieee_802_15_4_modgui.ui @@ -6,19 +6,19 @@ 0 0 - 605 + 622 937
- + 0 0 - 0 + 600 0 @@ -40,15 +40,15 @@
- 2 - 2 + 0 + 0 600 151 - 600 + 598 0 @@ -586,14 +586,14 @@
- + 0 0 - 600 + 598 0 @@ -634,9 +634,15 @@ 284
+ + + 0 + 0 + + - 600 + 598 0 @@ -661,6 +667,12 @@
+ + + 0 + 0 + + 200 @@ -691,7 +703,7 @@ - 600 + 598 0 @@ -716,6 +728,12 @@ + + + 0 + 0 + + 200 @@ -743,29 +761,17 @@
gui/rollupcontents.h
1 + + ButtonSwitch + QToolButton +
gui/buttonswitch.h
+
ValueDialZ QWidget
gui/valuedialz.h
1
- - GLScope - QWidget -
gui/glscope.h
- 1 -
- - GLScopeGUI - QWidget -
gui/glscopegui.h
- 1 -
- - ButtonSwitch - QToolButton -
gui/buttonswitch.h
-
GLSpectrum QWidget @@ -784,6 +790,18 @@
gui/levelmeter.h
1
+ + GLScope + QWidget +
gui/glscope.h
+ 1 +
+ + GLScopeGUI + QWidget +
gui/glscopegui.h
+ 1 +
deltaFrequency diff --git a/plugins/channeltx/modais/aismod.cpp b/plugins/channeltx/modais/aismod.cpp index 40eccd9a4..2de16546e 100644 --- a/plugins/channeltx/modais/aismod.cpp +++ b/plugins/channeltx/modais/aismod.cpp @@ -99,6 +99,18 @@ AISMod::~AISMod() delete m_thread; } +void AISMod::setDeviceAPI(DeviceAPI *deviceAPI) +{ + if (deviceAPI != m_deviceAPI) + { + m_deviceAPI->removeChannelSourceAPI(this); + m_deviceAPI->removeChannelSource(this); + m_deviceAPI = deviceAPI; + m_deviceAPI->addChannelSource(this); + m_deviceAPI->addChannelSinkAPI(this); + } +} + void AISMod::start() { qDebug("AISMod::start"); diff --git a/plugins/channeltx/modais/aismod.h b/plugins/channeltx/modais/aismod.h index 1f610be16..3492ec4dc 100644 --- a/plugins/channeltx/modais/aismod.h +++ b/plugins/channeltx/modais/aismod.h @@ -152,6 +152,8 @@ public: AISMod(DeviceAPI *deviceAPI); virtual ~AISMod(); virtual void destroy() { delete this; } + virtual void setDeviceAPI(DeviceAPI *deviceAPI); + virtual DeviceAPI *getDeviceAPI() { return m_deviceAPI; } virtual void start(); virtual void stop(); diff --git a/plugins/channeltx/modais/aismodgui.h b/plugins/channeltx/modais/aismodgui.h index 502d5eb0b..c327f5cbb 100644 --- a/plugins/channeltx/modais/aismodgui.h +++ b/plugins/channeltx/modais/aismodgui.h @@ -57,6 +57,7 @@ public: virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } virtual bool getHidden() const { return m_settings.m_hidden; } + virtual ChannelMarker& getChannelMarker() { return m_channelMarker; } public slots: void channelMarkerChangedByCursor(); diff --git a/plugins/channeltx/modam/ammod.cpp b/plugins/channeltx/modam/ammod.cpp index e50093b66..c1742d396 100644 --- a/plugins/channeltx/modam/ammod.cpp +++ b/plugins/channeltx/modam/ammod.cpp @@ -97,6 +97,18 @@ AMMod::~AMMod() delete m_thread; } +void AMMod::setDeviceAPI(DeviceAPI *deviceAPI) +{ + if (deviceAPI != m_deviceAPI) + { + m_deviceAPI->removeChannelSourceAPI(this); + m_deviceAPI->removeChannelSource(this); + m_deviceAPI = deviceAPI; + m_deviceAPI->addChannelSource(this); + m_deviceAPI->addChannelSinkAPI(this); + } +} + uint32_t AMMod::getNumberOfDeviceStreams() const { return m_deviceAPI->getNbSinkStreams(); diff --git a/plugins/channeltx/modam/ammod.h b/plugins/channeltx/modam/ammod.h index 46001df0b..66658c438 100644 --- a/plugins/channeltx/modam/ammod.h +++ b/plugins/channeltx/modam/ammod.h @@ -174,6 +174,8 @@ public: AMMod(DeviceAPI *deviceAPI); virtual ~AMMod(); virtual void destroy() { delete this; } + virtual void setDeviceAPI(DeviceAPI *deviceAPI); + virtual DeviceAPI *getDeviceAPI() { return m_deviceAPI; } virtual void start(); virtual void stop(); diff --git a/plugins/channeltx/modam/ammodgui.h b/plugins/channeltx/modam/ammodgui.h index c9ec32652..7e4c9595f 100644 --- a/plugins/channeltx/modam/ammodgui.h +++ b/plugins/channeltx/modam/ammodgui.h @@ -56,6 +56,7 @@ public: virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } virtual bool getHidden() const { return m_settings.m_hidden; } + virtual ChannelMarker& getChannelMarker() { return m_channelMarker; } public slots: void channelMarkerChangedByCursor(); diff --git a/plugins/channeltx/modam/ammodgui.ui b/plugins/channeltx/modam/ammodgui.ui index 0e57ea350..b1f136cf0 100644 --- a/plugins/channeltx/modam/ammodgui.ui +++ b/plugins/channeltx/modam/ammodgui.ui @@ -6,19 +6,19 @@ 0 0 - 342 - 363 + 360 + 347 - + 0 0 - 0 + 360 0 @@ -37,15 +37,21 @@ - 10 - 10 - 320 + 0 + 0 + 358 341 + + + 0 + 0 + + - 280 + 358 0 @@ -730,29 +736,29 @@
gui/rollupcontents.h
1 + + ButtonSwitch + QToolButton +
gui/buttonswitch.h
+
+ + ValueDialZ + QWidget +
gui/valuedialz.h
+ 1 +
LevelMeterVU QWidget
gui/levelmeter.h
1
- - ButtonSwitch - QToolButton -
gui/buttonswitch.h
-
CWKeyerGUI QWidget
gui/cwkeyergui.h
1
- - ValueDialZ - QWidget -
gui/valuedialz.h
- 1 -
diff --git a/plugins/channeltx/modatv/atvmod.cpp b/plugins/channeltx/modatv/atvmod.cpp index c39f1723c..0eb8dad6d 100644 --- a/plugins/channeltx/modatv/atvmod.cpp +++ b/plugins/channeltx/modatv/atvmod.cpp @@ -94,6 +94,18 @@ ATVMod::~ATVMod() delete m_thread; } +void ATVMod::setDeviceAPI(DeviceAPI *deviceAPI) +{ + if (deviceAPI != m_deviceAPI) + { + m_deviceAPI->removeChannelSourceAPI(this); + m_deviceAPI->removeChannelSource(this); + m_deviceAPI = deviceAPI; + m_deviceAPI->addChannelSource(this); + m_deviceAPI->addChannelSinkAPI(this); + } +} + uint32_t ATVMod::getNumberOfDeviceStreams() const { return m_deviceAPI->getNbSinkStreams(); diff --git a/plugins/channeltx/modatv/atvmod.h b/plugins/channeltx/modatv/atvmod.h index 4c2cc4daf..185e11fd0 100644 --- a/plugins/channeltx/modatv/atvmod.h +++ b/plugins/channeltx/modatv/atvmod.h @@ -246,6 +246,8 @@ public: ATVMod(DeviceAPI *deviceAPI); virtual ~ATVMod(); virtual void destroy() { delete this; } + virtual void setDeviceAPI(DeviceAPI *deviceAPI); + virtual DeviceAPI *getDeviceAPI() { return m_deviceAPI; } virtual void start(); virtual void stop(); diff --git a/plugins/channeltx/modatv/atvmodgui.h b/plugins/channeltx/modatv/atvmodgui.h index b6fbd5af3..a146ff4b4 100644 --- a/plugins/channeltx/modatv/atvmodgui.h +++ b/plugins/channeltx/modatv/atvmodgui.h @@ -55,6 +55,7 @@ public: virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } virtual bool getHidden() const { return m_settings.m_hidden; } + virtual ChannelMarker& getChannelMarker() { return m_channelMarker; } public slots: void channelMarkerChangedByCursor(); diff --git a/plugins/channeltx/modchirpchat/chirpchatmod.cpp b/plugins/channeltx/modchirpchat/chirpchatmod.cpp index 4ed11d89b..c2d3412fa 100644 --- a/plugins/channeltx/modchirpchat/chirpchatmod.cpp +++ b/plugins/channeltx/modchirpchat/chirpchatmod.cpp @@ -93,6 +93,18 @@ ChirpChatMod::~ChirpChatMod() delete m_thread; } +void ChirpChatMod::setDeviceAPI(DeviceAPI *deviceAPI) +{ + if (deviceAPI != m_deviceAPI) + { + m_deviceAPI->removeChannelSourceAPI(this); + m_deviceAPI->removeChannelSource(this); + m_deviceAPI = deviceAPI; + m_deviceAPI->addChannelSource(this); + m_deviceAPI->addChannelSinkAPI(this); + } +} + void ChirpChatMod::start() { qDebug("ChirpChatMod::start"); diff --git a/plugins/channeltx/modchirpchat/chirpchatmod.h b/plugins/channeltx/modchirpchat/chirpchatmod.h index 09c0c0b22..cfbc82d89 100644 --- a/plugins/channeltx/modchirpchat/chirpchatmod.h +++ b/plugins/channeltx/modchirpchat/chirpchatmod.h @@ -89,6 +89,8 @@ public: ChirpChatMod(DeviceAPI *deviceAPI); virtual ~ChirpChatMod(); virtual void destroy() { delete this; } + virtual void setDeviceAPI(DeviceAPI *deviceAPI); + virtual DeviceAPI *getDeviceAPI() { return m_deviceAPI; } virtual void start(); virtual void stop(); diff --git a/plugins/channeltx/modchirpchat/chirpchatmodgui.h b/plugins/channeltx/modchirpchat/chirpchatmodgui.h index ce012587b..bfb017afb 100644 --- a/plugins/channeltx/modchirpchat/chirpchatmodgui.h +++ b/plugins/channeltx/modchirpchat/chirpchatmodgui.h @@ -54,6 +54,7 @@ public: virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } virtual bool getHidden() const { return m_settings.m_hidden; } + virtual ChannelMarker& getChannelMarker() { return m_channelMarker; } public slots: void channelMarkerChangedByCursor(); diff --git a/plugins/channeltx/moddatv/datvmod.cpp b/plugins/channeltx/moddatv/datvmod.cpp index 796ebd9b9..83b429850 100644 --- a/plugins/channeltx/moddatv/datvmod.cpp +++ b/plugins/channeltx/moddatv/datvmod.cpp @@ -97,6 +97,18 @@ DATVMod::~DATVMod() delete m_thread; } +void DATVMod::setDeviceAPI(DeviceAPI *deviceAPI) +{ + if (deviceAPI != m_deviceAPI) + { + m_deviceAPI->removeChannelSourceAPI(this); + m_deviceAPI->removeChannelSource(this); + m_deviceAPI = deviceAPI; + m_deviceAPI->addChannelSource(this); + m_deviceAPI->addChannelSinkAPI(this); + } +} + uint32_t DATVMod::getNumberOfDeviceStreams() const { return m_deviceAPI->getNbSinkStreams(); diff --git a/plugins/channeltx/moddatv/datvmod.h b/plugins/channeltx/moddatv/datvmod.h index 0cd939f2e..c4d1a3764 100644 --- a/plugins/channeltx/moddatv/datvmod.h +++ b/plugins/channeltx/moddatv/datvmod.h @@ -202,6 +202,8 @@ public: DATVMod(DeviceAPI *deviceAPI); virtual ~DATVMod(); virtual void destroy() { delete this; } + virtual void setDeviceAPI(DeviceAPI *deviceAPI); + virtual DeviceAPI *getDeviceAPI() { return m_deviceAPI; } virtual void start(); virtual void stop(); diff --git a/plugins/channeltx/moddatv/datvmodgui.h b/plugins/channeltx/moddatv/datvmodgui.h index f0aba1bb8..4982fa555 100644 --- a/plugins/channeltx/moddatv/datvmodgui.h +++ b/plugins/channeltx/moddatv/datvmodgui.h @@ -56,6 +56,7 @@ public: virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } virtual bool getHidden() const { return m_settings.m_hidden; } + virtual ChannelMarker& getChannelMarker() { return m_channelMarker; } public slots: void channelMarkerChangedByCursor(); diff --git a/plugins/channeltx/modfreedv/freedvmod.cpp b/plugins/channeltx/modfreedv/freedvmod.cpp index bee8aa146..9667d4e8b 100644 --- a/plugins/channeltx/modfreedv/freedvmod.cpp +++ b/plugins/channeltx/modfreedv/freedvmod.cpp @@ -97,6 +97,18 @@ FreeDVMod::~FreeDVMod() delete m_thread; } +void FreeDVMod::setDeviceAPI(DeviceAPI *deviceAPI) +{ + if (deviceAPI != m_deviceAPI) + { + m_deviceAPI->removeChannelSourceAPI(this); + m_deviceAPI->removeChannelSource(this); + m_deviceAPI = deviceAPI; + m_deviceAPI->addChannelSource(this); + m_deviceAPI->addChannelSinkAPI(this); + } +} + void FreeDVMod::start() { qDebug("FreeDVMod::start"); diff --git a/plugins/channeltx/modfreedv/freedvmod.h b/plugins/channeltx/modfreedv/freedvmod.h index 49f2cc6d8..41660950f 100644 --- a/plugins/channeltx/modfreedv/freedvmod.h +++ b/plugins/channeltx/modfreedv/freedvmod.h @@ -178,6 +178,8 @@ public: FreeDVMod(DeviceAPI *deviceAPI); virtual ~FreeDVMod(); virtual void destroy() { delete this; } + virtual void setDeviceAPI(DeviceAPI *deviceAPI); + virtual DeviceAPI *getDeviceAPI() { return m_deviceAPI; } virtual void start(); virtual void stop(); diff --git a/plugins/channeltx/modfreedv/freedvmodgui.h b/plugins/channeltx/modfreedv/freedvmodgui.h index ad79495de..0275e17ef 100644 --- a/plugins/channeltx/modfreedv/freedvmodgui.h +++ b/plugins/channeltx/modfreedv/freedvmodgui.h @@ -57,6 +57,7 @@ public: virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } virtual bool getHidden() const { return m_settings.m_hidden; } + virtual ChannelMarker& getChannelMarker() { return m_channelMarker; } public slots: void channelMarkerChangedByCursor(); diff --git a/plugins/channeltx/modnfm/nfmmod.cpp b/plugins/channeltx/modnfm/nfmmod.cpp index 0f5457afc..faaf386da 100644 --- a/plugins/channeltx/modnfm/nfmmod.cpp +++ b/plugins/channeltx/modnfm/nfmmod.cpp @@ -98,6 +98,18 @@ NFMMod::~NFMMod() delete m_thread; } +void NFMMod::setDeviceAPI(DeviceAPI *deviceAPI) +{ + if (deviceAPI != m_deviceAPI) + { + m_deviceAPI->removeChannelSourceAPI(this); + m_deviceAPI->removeChannelSource(this); + m_deviceAPI = deviceAPI; + m_deviceAPI->addChannelSource(this); + m_deviceAPI->addChannelSinkAPI(this); + } +} + void NFMMod::start() { qDebug("NFMMod::start"); diff --git a/plugins/channeltx/modnfm/nfmmod.h b/plugins/channeltx/modnfm/nfmmod.h index 121b1a9a1..90a4ce786 100644 --- a/plugins/channeltx/modnfm/nfmmod.h +++ b/plugins/channeltx/modnfm/nfmmod.h @@ -174,6 +174,8 @@ public: NFMMod(DeviceAPI *deviceAPI); virtual ~NFMMod(); virtual void destroy() { delete this; } + virtual void setDeviceAPI(DeviceAPI *deviceAPI); + virtual DeviceAPI *getDeviceAPI() { return m_deviceAPI; } virtual void start(); virtual void stop(); diff --git a/plugins/channeltx/modnfm/nfmmodgui.h b/plugins/channeltx/modnfm/nfmmodgui.h index 2343874b4..e4dbd25a0 100644 --- a/plugins/channeltx/modnfm/nfmmodgui.h +++ b/plugins/channeltx/modnfm/nfmmodgui.h @@ -56,6 +56,7 @@ public: virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } virtual bool getHidden() const { return m_settings.m_hidden; } + virtual ChannelMarker& getChannelMarker() { return m_channelMarker; } public slots: void channelMarkerChangedByCursor(); diff --git a/plugins/channeltx/modnfm/nfmmodgui.ui b/plugins/channeltx/modnfm/nfmmodgui.ui index 925309b4d..ec293e1c7 100644 --- a/plugins/channeltx/modnfm/nfmmodgui.ui +++ b/plugins/channeltx/modnfm/nfmmodgui.ui @@ -6,19 +6,19 @@ 0 0 - 363 - 300 + 360 + 278 - + 0 0 - 0 + 360 0 @@ -37,15 +37,15 @@ - 2 - 2 - 341 + 0 + 0 + 358 271 - 280 + 358 0 diff --git a/plugins/channeltx/modpacket/packetmod.cpp b/plugins/channeltx/modpacket/packetmod.cpp index c08f28d3c..d3f086bba 100644 --- a/plugins/channeltx/modpacket/packetmod.cpp +++ b/plugins/channeltx/modpacket/packetmod.cpp @@ -103,6 +103,18 @@ PacketMod::~PacketMod() delete m_thread; } +void PacketMod::setDeviceAPI(DeviceAPI *deviceAPI) +{ + if (deviceAPI != m_deviceAPI) + { + m_deviceAPI->removeChannelSourceAPI(this); + m_deviceAPI->removeChannelSource(this); + m_deviceAPI = deviceAPI; + m_deviceAPI->addChannelSource(this); + m_deviceAPI->addChannelSinkAPI(this); + } +} + void PacketMod::start() { qDebug("PacketMod::start"); diff --git a/plugins/channeltx/modpacket/packetmod.h b/plugins/channeltx/modpacket/packetmod.h index 7a9bd95d6..ebb7ebf78 100644 --- a/plugins/channeltx/modpacket/packetmod.h +++ b/plugins/channeltx/modpacket/packetmod.h @@ -142,6 +142,8 @@ public: PacketMod(DeviceAPI *deviceAPI); virtual ~PacketMod(); virtual void destroy() { delete this; } + virtual void setDeviceAPI(DeviceAPI *deviceAPI); + virtual DeviceAPI *getDeviceAPI() { return m_deviceAPI; } virtual void start(); virtual void stop(); diff --git a/plugins/channeltx/modpacket/packetmodgui.h b/plugins/channeltx/modpacket/packetmodgui.h index 4c5fba36d..644cbdea6 100644 --- a/plugins/channeltx/modpacket/packetmodgui.h +++ b/plugins/channeltx/modpacket/packetmodgui.h @@ -56,6 +56,7 @@ public: virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } virtual bool getHidden() const { return m_settings.m_hidden; } + virtual ChannelMarker& getChannelMarker() { return m_channelMarker; } public slots: void channelMarkerChangedByCursor(); diff --git a/plugins/channeltx/modssb/ssbmod.cpp b/plugins/channeltx/modssb/ssbmod.cpp index 44ad84076..51860e1fe 100644 --- a/plugins/channeltx/modssb/ssbmod.cpp +++ b/plugins/channeltx/modssb/ssbmod.cpp @@ -99,6 +99,18 @@ SSBMod::~SSBMod() delete m_thread; } +void SSBMod::setDeviceAPI(DeviceAPI *deviceAPI) +{ + if (deviceAPI != m_deviceAPI) + { + m_deviceAPI->removeChannelSourceAPI(this); + m_deviceAPI->removeChannelSource(this); + m_deviceAPI = deviceAPI; + m_deviceAPI->addChannelSource(this); + m_deviceAPI->addChannelSinkAPI(this); + } +} + void SSBMod::start() { qDebug("SSBMod::start"); diff --git a/plugins/channeltx/modssb/ssbmod.h b/plugins/channeltx/modssb/ssbmod.h index 77afa532f..6603f9ac0 100644 --- a/plugins/channeltx/modssb/ssbmod.h +++ b/plugins/channeltx/modssb/ssbmod.h @@ -176,6 +176,8 @@ public: SSBMod(DeviceAPI *deviceAPI); virtual ~SSBMod(); virtual void destroy() { delete this; } + virtual void setDeviceAPI(DeviceAPI *deviceAPI); + virtual DeviceAPI *getDeviceAPI() { return m_deviceAPI; } virtual void start(); virtual void stop(); diff --git a/plugins/channeltx/modssb/ssbmodgui.h b/plugins/channeltx/modssb/ssbmodgui.h index 74e95c05a..5a132e8e6 100644 --- a/plugins/channeltx/modssb/ssbmodgui.h +++ b/plugins/channeltx/modssb/ssbmodgui.h @@ -57,6 +57,7 @@ public: virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } virtual bool getHidden() const { return m_settings.m_hidden; } + virtual ChannelMarker& getChannelMarker() { return m_channelMarker; } public slots: void channelMarkerChangedByCursor(); diff --git a/plugins/channeltx/modssb/ssbmodgui.ui b/plugins/channeltx/modssb/ssbmodgui.ui index 0465fac0a..06b6eb17f 100644 --- a/plugins/channeltx/modssb/ssbmodgui.ui +++ b/plugins/channeltx/modssb/ssbmodgui.ui @@ -6,19 +6,19 @@ 0 0 - 430 - 643 + 414 + 331 - + 0 0 - 430 + 414 0 @@ -36,13 +36,13 @@ 0 0 - 430 + 412 331 - 385 + 412 0 @@ -503,6 +503,7 @@ + Liberation Sans 8 @@ -524,6 +525,7 @@ + Liberation Sans 8 @@ -536,6 +538,7 @@ + Liberation Sans 8 @@ -557,6 +560,7 @@ + Liberation Sans 8 @@ -572,6 +576,7 @@ + Liberation Sans 8 @@ -593,6 +598,7 @@ + Liberation Sans 8 @@ -1202,6 +1208,12 @@ 284 + + + 0 + 0 + + Channel Spectrum @@ -1250,6 +1262,17 @@
gui/rollupcontents.h
1 + + ButtonSwitch + QToolButton +
gui/buttonswitch.h
+
+ + ValueDialZ + QWidget +
gui/valuedialz.h
+ 1 +
GLSpectrum QWidget @@ -1262,34 +1285,23 @@
gui/glspectrumgui.h
1
+ + TickedSlider + QSlider +
gui/tickedslider.h
+
LevelMeterVU QWidget
gui/levelmeter.h
1
- - ButtonSwitch - QToolButton -
gui/buttonswitch.h
-
CWKeyerGUI QWidget
gui/cwkeyergui.h
1
- - ValueDialZ - QWidget -
gui/valuedialz.h
- 1 -
- - TickedSlider - QSlider -
gui/tickedslider.h
-
diff --git a/plugins/channeltx/modwfm/wfmmod.cpp b/plugins/channeltx/modwfm/wfmmod.cpp index ae83b93a3..c0859f4f5 100644 --- a/plugins/channeltx/modwfm/wfmmod.cpp +++ b/plugins/channeltx/modwfm/wfmmod.cpp @@ -96,6 +96,18 @@ WFMMod::~WFMMod() delete m_thread; } +void WFMMod::setDeviceAPI(DeviceAPI *deviceAPI) +{ + if (deviceAPI != m_deviceAPI) + { + m_deviceAPI->removeChannelSourceAPI(this); + m_deviceAPI->removeChannelSource(this); + m_deviceAPI = deviceAPI; + m_deviceAPI->addChannelSource(this); + m_deviceAPI->addChannelSinkAPI(this); + } +} + void WFMMod::start() { qDebug("WFMMod::start"); diff --git a/plugins/channeltx/modwfm/wfmmod.h b/plugins/channeltx/modwfm/wfmmod.h index 0f05972bb..625164ef9 100644 --- a/plugins/channeltx/modwfm/wfmmod.h +++ b/plugins/channeltx/modwfm/wfmmod.h @@ -174,6 +174,8 @@ public: WFMMod(DeviceAPI *deviceAPI); virtual ~WFMMod(); virtual void destroy() { delete this; } + virtual void setDeviceAPI(DeviceAPI *deviceAPI); + virtual DeviceAPI *getDeviceAPI() { return m_deviceAPI; } virtual void start(); virtual void stop(); diff --git a/plugins/channeltx/modwfm/wfmmodgui.h b/plugins/channeltx/modwfm/wfmmodgui.h index 3c14d722e..26ef490c7 100644 --- a/plugins/channeltx/modwfm/wfmmodgui.h +++ b/plugins/channeltx/modwfm/wfmmodgui.h @@ -54,6 +54,7 @@ public: virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } virtual bool getHidden() const { return m_settings.m_hidden; } + virtual ChannelMarker& getChannelMarker() { return m_channelMarker; } public slots: void channelMarkerChangedByCursor(); diff --git a/plugins/channeltx/remotesource/remotesource.cpp b/plugins/channeltx/remotesource/remotesource.cpp index 89af2e0db..5458f7294 100644 --- a/plugins/channeltx/remotesource/remotesource.cpp +++ b/plugins/channeltx/remotesource/remotesource.cpp @@ -88,6 +88,18 @@ RemoteSource::~RemoteSource() delete m_thread; } +void RemoteSource::setDeviceAPI(DeviceAPI *deviceAPI) +{ + if (deviceAPI != m_deviceAPI) + { + m_deviceAPI->removeChannelSourceAPI(this); + m_deviceAPI->removeChannelSource(this); + m_deviceAPI = deviceAPI; + m_deviceAPI->addChannelSource(this); + m_deviceAPI->addChannelSinkAPI(this); + } +} + void RemoteSource::start() { qDebug("RemoteSource::start"); diff --git a/plugins/channeltx/remotesource/remotesource.h b/plugins/channeltx/remotesource/remotesource.h index ec9f27a11..6b3f831f5 100644 --- a/plugins/channeltx/remotesource/remotesource.h +++ b/plugins/channeltx/remotesource/remotesource.h @@ -155,8 +155,9 @@ public: RemoteSource(DeviceAPI *deviceAPI); virtual ~RemoteSource(); - virtual void destroy() { delete this; } + virtual void setDeviceAPI(DeviceAPI *deviceAPI); + virtual DeviceAPI *getDeviceAPI() { return m_deviceAPI; } virtual void start(); virtual void stop(); diff --git a/plugins/channeltx/remotesource/remotesourcegui.h b/plugins/channeltx/remotesource/remotesourcegui.h index 6a095e0ee..6f8cd729e 100644 --- a/plugins/channeltx/remotesource/remotesourcegui.h +++ b/plugins/channeltx/remotesource/remotesourcegui.h @@ -55,6 +55,7 @@ public: virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } virtual bool getHidden() const { return m_settings.m_hidden; } + virtual ChannelMarker& getChannelMarker() { return m_channelMarker; } public slots: void channelMarkerChangedByCursor(); diff --git a/plugins/channeltx/udpsource/udpsource.cpp b/plugins/channeltx/udpsource/udpsource.cpp index 68f9e9b91..f9a96d86f 100644 --- a/plugins/channeltx/udpsource/udpsource.cpp +++ b/plugins/channeltx/udpsource/udpsource.cpp @@ -82,6 +82,18 @@ UDPSource::~UDPSource() delete m_thread; } +void UDPSource::setDeviceAPI(DeviceAPI *deviceAPI) +{ + if (deviceAPI != m_deviceAPI) + { + m_deviceAPI->removeChannelSourceAPI(this); + m_deviceAPI->removeChannelSource(this); + m_deviceAPI = deviceAPI; + m_deviceAPI->addChannelSource(this); + m_deviceAPI->addChannelSinkAPI(this); + } +} + void UDPSource::start() { qDebug("UDPSource::start"); diff --git a/plugins/channeltx/udpsource/udpsource.h b/plugins/channeltx/udpsource/udpsource.h index 4d9cb6297..4a4fe6caf 100644 --- a/plugins/channeltx/udpsource/udpsource.h +++ b/plugins/channeltx/udpsource/udpsource.h @@ -95,6 +95,8 @@ public: UDPSource(DeviceAPI *deviceAPI); virtual ~UDPSource(); virtual void destroy() { delete this; } + virtual void setDeviceAPI(DeviceAPI *deviceAPI); + virtual DeviceAPI *getDeviceAPI() { return m_deviceAPI; } virtual void start(); virtual void stop(); diff --git a/plugins/channeltx/udpsource/udpsourcegui.h b/plugins/channeltx/udpsource/udpsourcegui.h index f9cfdecf0..aa8afa1d8 100644 --- a/plugins/channeltx/udpsource/udpsourcegui.h +++ b/plugins/channeltx/udpsource/udpsourcegui.h @@ -57,6 +57,7 @@ public: virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } virtual bool getHidden() const { return m_settings.m_hidden; } + virtual ChannelMarker& getChannelMarker() { return m_channelMarker; } public slots: void channelMarkerChangedByCursor(); diff --git a/plugins/channeltx/udpsource/udpsourcegui.ui b/plugins/channeltx/udpsource/udpsourcegui.ui index ee3d3a970..dcc1dc6d8 100644 --- a/plugins/channeltx/udpsource/udpsourcegui.ui +++ b/plugins/channeltx/udpsource/udpsourcegui.ui @@ -6,10 +6,16 @@ 0 0 - 395 - 403 + 414 + 400 + + + 0 + 0 + + 395 @@ -18,7 +24,7 @@ - 400 + 414 16777215 @@ -39,13 +45,13 @@ 2 2 - 390 + 412 221 - 390 + 412 0 @@ -190,6 +196,15 @@ + + + + 26 + 26 + 26 + + + @@ -210,6 +225,15 @@ + + + + 26 + 26 + 26 + + + @@ -230,11 +254,21 @@ + + + + 26 + 26 + 26 + + + + Liberation Sans 8 @@ -1003,6 +1037,12 @@ 156 + + + 0 + 0 + + Channel Spectrum @@ -1024,6 +1064,12 @@ + + + 0 + 0 + + Liberation Mono @@ -1045,6 +1091,17 @@
gui/rollupcontents.h
1 + + ButtonSwitch + QToolButton +
gui/buttonswitch.h
+
+ + ValueDialZ + QWidget +
gui/valuedialz.h
+ 1 +
GLSpectrum QWidget @@ -1057,23 +1114,12 @@
gui/glspectrumgui.h
1
- - ValueDialZ - QWidget -
gui/valuedialz.h
- 1 -
LevelMeterVU QWidget
gui/levelmeter.h
1
- - ButtonSwitch - QToolButton -
gui/buttonswitch.h
-
diff --git a/plugins/samplesink/testsink/testsinkgui.ui b/plugins/samplesink/testsink/testsinkgui.ui index 545630c60..267f0a318 100644 --- a/plugins/samplesink/testsink/testsinkgui.ui +++ b/plugins/samplesink/testsink/testsinkgui.ui @@ -7,7 +7,7 @@ 0 0 360 - 320 + 350
@@ -293,9 +293,15 @@ + + + 0 + 0 + + - 200 + 354 200 @@ -308,24 +314,37 @@ - + + + + 0 + 0 + + + + + 354 + 30 + + +
+ + ButtonSwitch + QToolButton +
gui/buttonswitch.h
+
ValueDial QWidget
gui/valuedial.h
1
- - ButtonSwitch - QToolButton -
gui/buttonswitch.h
-
GLSpectrum QWidget diff --git a/sdrbase/channel/channelapi.cpp b/sdrbase/channel/channelapi.cpp index 557359d4a..861507547 100644 --- a/sdrbase/channel/channelapi.cpp +++ b/sdrbase/channel/channelapi.cpp @@ -28,7 +28,6 @@ ChannelAPI::ChannelAPI(const QString& uri, StreamType streamType) : m_uri(uri), m_indexInDeviceSet(-1), m_deviceSetIndex(0), - m_deviceAPI(0), m_uid(UidCalculator::getNewObjectId()) { connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages())); diff --git a/sdrbase/channel/channelapi.h b/sdrbase/channel/channelapi.h index dd284db0a..b7168d1b8 100644 --- a/sdrbase/channel/channelapi.h +++ b/sdrbase/channel/channelapi.h @@ -52,6 +52,8 @@ public: ChannelAPI(const QString& name, StreamType streamType); virtual ~ChannelAPI() {} virtual void destroy() = 0; + virtual void setDeviceAPI(DeviceAPI*) = 0; + virtual DeviceAPI *getDeviceAPI() = 0; virtual void getIdentifier(QString& id) = 0; virtual QString getIdentifier() const = 0; @@ -129,8 +131,6 @@ public: int getDeviceSetIndex() const { return m_deviceSetIndex; } void setDeviceSetIndex(int deviceSetIndex) { m_deviceSetIndex = deviceSetIndex; } - DeviceAPI *getDeviceAPI() { return m_deviceAPI; } - void setDeviceAPI(DeviceAPI *deviceAPI) { m_deviceAPI = deviceAPI; } uint64_t getUID() const { return m_uid; } // MIMO support @@ -167,7 +167,6 @@ private: int m_indexInDeviceSet; int m_deviceSetIndex; - DeviceAPI *m_deviceAPI; uint64_t m_uid; signals: diff --git a/sdrgui/CMakeLists.txt b/sdrgui/CMakeLists.txt index 95842d5aa..d83e53ec2 100644 --- a/sdrgui/CMakeLists.txt +++ b/sdrgui/CMakeLists.txt @@ -27,6 +27,7 @@ set(sdrgui_SOURCES gui/cwkeyergui.cpp gui/datetimedelegate.cpp gui/decimaldelegate.cpp + gui/devicesetselectiondialog.cpp gui/devicesetpresetsdialog.cpp gui/devicestreamselectiondialog.cpp gui/deviceuserargsdialog.cpp @@ -124,6 +125,7 @@ set(sdrgui_HEADERS gui/cwkeyergui.h gui/datetimedelegate.h gui/decimaldelegate.h + gui/devicesetselectiondialog.h gui/devicesetpresetsdialog.h gui/devicestreamselectiondialog.h gui/deviceuserargsdialog.h diff --git a/sdrgui/channel/channelgui.cpp b/sdrgui/channel/channelgui.cpp index 89f008f96..1ad76e619 100644 --- a/sdrgui/channel/channelgui.cpp +++ b/sdrgui/channel/channelgui.cpp @@ -28,6 +28,7 @@ #include "mainwindow.h" #include "gui/workspaceselectiondialog.h" +#include "gui/devicesetselectiondialog.h" #include "gui/rollupcontents.h" #include "channelgui.h" @@ -97,6 +98,12 @@ ChannelGUI::ChannelGUI(QWidget *parent) : m_duplicateButton->setIcon(m_duplicateIcon); m_duplicateButton->setToolTip("Duplicate channel"); + m_moveToDeviceButton = new QPushButton(); + m_moveToDeviceButton->setFixedSize(20, 20); + QIcon moveRoundIcon(":/exit_round.png"); + m_moveToDeviceButton->setIcon(moveRoundIcon); + m_moveToDeviceButton->setToolTip("Move to another device"); + m_statusFrequency = new QLabel(); // QFont font = m_statusFrequency->font(); // font.setPointSize(8); @@ -142,6 +149,7 @@ ChannelGUI::ChannelGUI(QWidget *parent) : m_bottomLayout = new QHBoxLayout(); m_bottomLayout->setContentsMargins(0, 0, 0, 0); m_bottomLayout->addWidget(m_duplicateButton); + m_bottomLayout->addWidget(m_moveToDeviceButton); m_bottomLayout->addWidget(m_statusFrequency); m_bottomLayout->addWidget(m_statusLabel); m_sizeGripBottomRight = new QSizeGrip(this); @@ -165,6 +173,7 @@ ChannelGUI::ChannelGUI(QWidget *parent) : connect(m_hideButton, SIGNAL(clicked()), this, SLOT(hide())); connect(m_closeButton, SIGNAL(clicked()), this, SLOT(close())); connect(m_duplicateButton, SIGNAL(clicked()), this, SLOT(duplicateChannel())); + connect(m_moveToDeviceButton, SIGNAL(clicked()), this, SLOT(openMoveToDeviceSetDialog())); connect( m_rollupContents, @@ -185,6 +194,8 @@ ChannelGUI::~ChannelGUI() delete m_layouts; delete m_statusLabel; delete m_statusFrequency; + delete m_moveToDeviceButton; + delete m_duplicateButton; delete m_closeButton; delete m_hideButton; delete m_shrinkButton; @@ -280,10 +291,19 @@ void ChannelGUI::onWidgetRolled(QWidget *widget, bool show) void ChannelGUI::duplicateChannel() { - qDebug("ChannelGUI::duplicateChannel"); emit duplicateChannelEmitted(); } +void ChannelGUI::openMoveToDeviceSetDialog() +{ + DeviceSetSelectionDialog dialog(MainWindow::getInstance()->getDeviceUISets(), m_deviceSetIndex, this); + dialog.exec(); + + if (dialog.hasChanged() && (dialog.getSelectedIndex() != m_deviceSetIndex)) { + emit moveToDeviceSet(dialog.getSelectedIndex()); + } +} + void ChannelGUI::shrinkWindow() { qDebug("ChannelGUI::shrinkWindow"); diff --git a/sdrgui/channel/channelgui.h b/sdrgui/channel/channelgui.h index 7ab78dc6d..28a8b7434 100644 --- a/sdrgui/channel/channelgui.h +++ b/sdrgui/channel/channelgui.h @@ -31,6 +31,7 @@ class QVBoxLayout; class QHBoxLayout; class QSizeGrip; class RollupContents; +class ChannelMarker; class SDRGUI_API ChannelGUI : public QMdiSubWindow { @@ -66,6 +67,7 @@ public: virtual QColor getTitleColor() const = 0; virtual void zetHidden(bool hidden) = 0; virtual bool getHidden() const = 0; + virtual ChannelMarker& getChannelMarker() = 0; virtual MessageQueue* getInputMessageQueue() = 0; @@ -114,6 +116,7 @@ private: QPushButton *m_hideButton; QPushButton *m_closeButton; QPushButton *m_duplicateButton; + QPushButton *m_moveToDeviceButton; QLabel *m_statusFrequency; QLabel *m_statusLabel; QVBoxLayout *m_layouts; @@ -132,12 +135,14 @@ private slots: void openMoveToWorkspaceDialog(); void onWidgetRolled(QWidget *widget, bool show); void duplicateChannel(); + void openMoveToDeviceSetDialog(); signals: void closing(); void moveToWorkspace(int workspaceIndex); void forceShrink(); void duplicateChannelEmitted(); + void moveToDeviceSet(int deviceSetIndex); }; #endif // SDRGUI_CHANNEL_CHANNELGUI_H_ diff --git a/sdrgui/device/deviceuiset.cpp b/sdrgui/device/deviceuiset.cpp index 90a8dd5f9..1adb6df0f 100644 --- a/sdrgui/device/deviceuiset.cpp +++ b/sdrgui/device/deviceuiset.cpp @@ -97,6 +97,11 @@ void DeviceUISet::addChannelMarker(ChannelMarker* channelMarker) m_spectrum->addChannelMarker(channelMarker); } +void DeviceUISet::removeChannelMarker(ChannelMarker* channelMarker) +{ + m_spectrum->removeChannelMarker(channelMarker); +} + void DeviceUISet::registerRxChannelInstance(ChannelAPI *channelAPI, ChannelGUI* channelGUI) { m_channelInstanceRegistrations.append(ChannelInstanceRegistration(channelAPI, channelGUI, 0)); @@ -136,6 +141,20 @@ void DeviceUISet::registerChannelInstance(ChannelAPI *channelAPI, ChannelGUI* ch ); } +void DeviceUISet::unregisterChannelInstanceAt(int channelIndex) +{ + if ((channelIndex >= 0) && (channelIndex < m_channelInstanceRegistrations.count())) + { + m_channelInstanceRegistrations.removeAt(channelIndex); + m_deviceSet->removeChannelInstanceAt(channelIndex); + + // Renumerate + for (int i = 0; i < m_channelInstanceRegistrations.count(); i++) { + m_channelInstanceRegistrations.at(i).m_gui->setIndex(i); + } + } +} + void DeviceUISet::freeChannels() { for(int i = 0; i < m_channelInstanceRegistrations.count(); i++) @@ -331,6 +350,12 @@ void DeviceUISet::loadRxChannelSettings(const Preset *preset, PluginAPI *pluginA this, [=](){ MainWindow::getInstance()->channelDuplicate(rxChannelGUI); } ); + QObject::connect( + rxChannelGUI, + &ChannelGUI::moveToDeviceSet, + this, + [=](int dsIndexDest){ MainWindow::getInstance()->channelMoveToDeviceSet(rxChannelGUI, dsIndexDest); } + ); } } } @@ -451,6 +476,12 @@ void DeviceUISet::loadTxChannelSettings(const Preset *preset, PluginAPI *pluginA this, [=](){ MainWindow::getInstance()->channelDuplicate(txChannelGUI); } ); + QObject::connect( + txChannelGUI, + &ChannelGUI::moveToDeviceSet, + this, + [=](int dsIndexDest){ MainWindow::getInstance()->channelMoveToDeviceSet(txChannelGUI, dsIndexDest); } + ); } } } @@ -573,6 +604,12 @@ void DeviceUISet::loadMIMOChannelSettings(const Preset *preset, PluginAPI *plugi this, [=](){ MainWindow::getInstance()->channelDuplicate(mimoChannelGUI); } ); + QObject::connect( + mimoChannelGUI, + &ChannelGUI::moveToDeviceSet, + this, + [=](int dsIndexDest){ MainWindow::getInstance()->channelMoveToDeviceSet(mimoChannelGUI, dsIndexDest); } + ); } } } diff --git a/sdrgui/device/deviceuiset.h b/sdrgui/device/deviceuiset.h index 397944c25..f7572f790 100644 --- a/sdrgui/device/deviceuiset.h +++ b/sdrgui/device/deviceuiset.h @@ -73,9 +73,10 @@ public: void setIndex(int deviceSetIndex); int getIndex() const { return m_deviceSetIndex; } - GLSpectrum *getSpectrum() { return m_spectrum; } //!< Direct spectrum getter + GLSpectrum *getSpectrum() { return m_spectrum; } //!< Direct spectrum getter void setSpectrumScalingFactor(float scalef); - void addChannelMarker(ChannelMarker* channelMarker); //!< Add channel marker to spectrum + void addChannelMarker(ChannelMarker* channelMarker); //!< Add channel marker to spectrum + void removeChannelMarker(ChannelMarker* channelMarker); //!< Remove channel marker from spectrum int getNumberOfChannels() const { return m_channelInstanceRegistrations.size(); } void freeChannels(); @@ -94,6 +95,7 @@ public: void registerRxChannelInstance(ChannelAPI *channelAPI, ChannelGUI* channelGUI); void registerTxChannelInstance(ChannelAPI *channelAPI, ChannelGUI* channelGUI); void registerChannelInstance(ChannelAPI *channelAPI, ChannelGUI* channelGUI); + void unregisterChannelInstanceAt(int channelIndex); // These are the number of channel types available for selection void setNumberOfAvailableRxChannels(int number) { m_nbAvailableRxChannels = number; } diff --git a/sdrgui/gui/devicesetselectiondialog.cpp b/sdrgui/gui/devicesetselectiondialog.cpp new file mode 100644 index 000000000..dc836cfbf --- /dev/null +++ b/sdrgui/gui/devicesetselectiondialog.cpp @@ -0,0 +1,65 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2022 F4EXB // +// written by Edouard Griffiths // +// // +// This program 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 as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program 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 V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#include "device/deviceuiset.h" +#include "device/devicegui.h" +#include "device/deviceapi.h" +#include "devicesetselectiondialog.h" + +#include "ui_workspaceselectiondialog.h" + +DeviceSetSelectionDialog::DeviceSetSelectionDialog(std::vector& deviceUIs, int channelDeviceSetIndex, QWidget *parent) : + QDialog(parent), + ui(new Ui::WorkspaceSelectionDialog), + m_deviceUIs(deviceUIs), + m_channelDeviceSetIndex(channelDeviceSetIndex), + m_hasChanged(false) +{ + ui->setupUi(this); + setWindowTitle("Device"); // Change window title + + DeviceUISet *originDeviceUISet = deviceUIs[channelDeviceSetIndex]; + int originDeviceType = (int) originDeviceUISet->m_deviceGUI->getDeviceType(); + + for (int i = 0; i < (int) m_deviceUIs.size(); i++) + { + DeviceUISet *deviceUISet = m_deviceUIs[i]; + + if ((int) deviceUISet->m_deviceGUI->getDeviceType() == originDeviceType) + { + ui->workspaceList->addItem(tr("%1:%2 %3") + .arg(getDeviceTypeChar(originDeviceType)) + .arg(i) + .arg(deviceUISet->m_deviceAPI->getSamplingDeviceDisplayName().split(" ")[0]) + ); + m_deviceSetIndexes.push_back(i); + } + } +} + +DeviceSetSelectionDialog::~DeviceSetSelectionDialog() +{ + delete ui; +} + +void DeviceSetSelectionDialog::accept() +{ + m_selectedDeviceSetIndex = m_deviceSetIndexes[ui->workspaceList->currentRow()]; + m_hasChanged = true; + QDialog::accept(); +} diff --git a/sdrgui/gui/devicesetselectiondialog.h b/sdrgui/gui/devicesetselectiondialog.h new file mode 100644 index 000000000..b59fd1fee --- /dev/null +++ b/sdrgui/gui/devicesetselectiondialog.h @@ -0,0 +1,73 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2022 F4EXB // +// written by Edouard Griffiths // +// // +// This program 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 as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program 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 V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef SDRGUI_GUI_DEVICESETSELECTIONDIALOG_H_ +#define SDRGUI_GUI_DEVICESETSELECTIONDIALOG_H_ + +#include + +#include "export.h" + +namespace Ui { + class WorkspaceSelectionDialog; +} + +class DeviceUISet; + +class SDRGUI_API DeviceSetSelectionDialog : public QDialog +{ + Q_OBJECT +public: + explicit DeviceSetSelectionDialog(std::vector& deviceUIs, int channelDeviceSetIndex, QWidget *parent = nullptr); + ~DeviceSetSelectionDialog(); + + bool hasChanged() const { return m_hasChanged; } + int getSelectedIndex() const { return m_selectedDeviceSetIndex; } + +private: + Ui::WorkspaceSelectionDialog *ui; + std::vector& m_deviceUIs; + std::vector m_deviceSetIndexes; + int m_channelDeviceSetIndex; + int m_selectedDeviceSetIndex; + bool m_hasChanged; + + QString getDeviceTypeChar(int deviceType) + { + switch(deviceType) + { + case 0: + return "R"; + break; + case 1: + return "T"; + break; + case 2: + return "M"; + break; + default: + return "X"; + break; + } + } + +private slots: + void accept(); +}; + +#endif // SDRGUI_GUI_WORKSPACESELECTIONDIALOG_H_ diff --git a/sdrgui/mainwindow.cpp b/sdrgui/mainwindow.cpp index 44aa30a21..99d0495e2 100644 --- a/sdrgui/mainwindow.cpp +++ b/sdrgui/mainwindow.cpp @@ -1759,14 +1759,14 @@ void MainWindow::addWorkspace() m_workspaces.back(), &Workspace::addTxDevice, this, - [=](Workspace *inWorkspace, int deviceIndex) { this->sampleSourceAdd(inWorkspace, inWorkspace, deviceIndex); } + [=](Workspace *inWorkspace, int deviceIndex) { this->sampleSinkAdd(inWorkspace, inWorkspace, deviceIndex); } ); QObject::connect( m_workspaces.back(), &Workspace::addMIMODevice, this, - [=](Workspace *inWorkspace, int deviceIndex) { this->sampleSourceAdd(inWorkspace, inWorkspace, deviceIndex); } + [=](Workspace *inWorkspace, int deviceIndex) { this->sampleMIMOAdd(inWorkspace, inWorkspace, deviceIndex); } ); QObject::connect( @@ -2119,22 +2119,64 @@ void MainWindow::sampleMIMOChange(int deviceSetIndex, int newDeviceIndex, Worksp } } -void MainWindow::channelDuplicate(ChannelGUI *sourceChannelGUI) +void MainWindow::channelMoveToDeviceSet(ChannelGUI *gui, int dsIndexDestination) { - int deviceSetIndex = sourceChannelGUI->getDeviceSetIndex(); - int channelIndex = sourceChannelGUI->getIndex(); + int deviceSetIndex = gui->getDeviceSetIndex(); + int channelIndex = gui->getIndex(); + qDebug("MainWindow::channelMoveToDeviceSet: %s at %d:%d to %d", + qPrintable(gui->getTitle()), deviceSetIndex, channelIndex, dsIndexDestination); - qDebug("MainWindow::channelDuplicate: %s at %d:%d in workspace %d", - qPrintable(sourceChannelGUI->getTitle()), deviceSetIndex, channelIndex, sourceChannelGUI->getWorkspaceIndex()); - - if (deviceSetIndex < (int) m_deviceUIs.size()) + if ((deviceSetIndex < (int) m_deviceUIs.size()) && (dsIndexDestination < (int) m_deviceUIs.size())) { DeviceUISet *deviceUI = m_deviceUIs[deviceSetIndex]; - ChannelAPI *sourceChannelAPI = deviceUI->getChannelAt(channelIndex); - DeviceAPI *deviceAPI = deviceUI->m_deviceAPI; - ChannelGUI *gui = nullptr; + DeviceUISet *destDeviceUI = m_deviceUIs[dsIndexDestination]; + ChannelAPI *channelAPI = deviceUI->getChannelAt(channelIndex); + deviceUI->unregisterChannelInstanceAt(channelIndex); - if (deviceUI->m_deviceSourceEngine) // source device => Rx channels + if (deviceUI->m_deviceSourceEngine) // source devices + { + destDeviceUI->registerRxChannelInstance(channelAPI, gui); + } + else if (deviceUI->m_deviceSinkEngine) // sink devices + { + destDeviceUI->registerTxChannelInstance(channelAPI, gui); + } + else if (deviceUI->m_deviceMIMOEngine) // MIMO devices + { + destDeviceUI->registerChannelInstance(channelAPI, gui); + } + + gui->setIndex(channelAPI->getIndexInDeviceSet()); + gui->setDeviceSetIndex(dsIndexDestination); + DeviceAPI *destDeviceAPI = destDeviceUI->m_deviceAPI; + gui->setToolTip(destDeviceAPI->getSamplingDeviceDisplayName()); + channelAPI->setDeviceAPI(destDeviceAPI); + deviceUI->removeChannelMarker(&gui->getChannelMarker()); + destDeviceUI->addChannelMarker(&gui->getChannelMarker()); + } +} + +void MainWindow::channelDuplicate(ChannelGUI *sourceChannelGUI) +{ + channelDuplicateToDeviceSet(sourceChannelGUI, sourceChannelGUI->getDeviceSetIndex()); // Duplicate in same device set +} + +void MainWindow::channelDuplicateToDeviceSet(ChannelGUI *sourceChannelGUI, int dsIndexDestination) +{ + int dsIndexSource = sourceChannelGUI->getDeviceSetIndex(); + int sourceChannelIndex = sourceChannelGUI->getIndex(); + + qDebug("MainWindow::channelDuplicateToDeviceSet: %s at %d:%d to %d in workspace %d", + qPrintable(sourceChannelGUI->getTitle()), dsIndexSource, sourceChannelIndex, dsIndexDestination, sourceChannelGUI->getWorkspaceIndex()); + + if ((dsIndexSource < (int) m_deviceUIs.size()) && (dsIndexDestination < (int) m_deviceUIs.size())) + { + DeviceUISet *sourceDeviceUI = m_deviceUIs[dsIndexSource]; + ChannelAPI *sourceChannelAPI = sourceDeviceUI->getChannelAt(sourceChannelIndex); + ChannelGUI *destChannelGUI = nullptr; + DeviceUISet *destDeviceUI = m_deviceUIs[dsIndexDestination]; + + if (destDeviceUI->m_deviceSourceEngine) // source device => Rx channels { PluginAPI::ChannelRegistrations *channelRegistrations = m_pluginManager->getRxChannelRegistrations(); PluginInterface *pluginInterface = nullptr; @@ -2152,16 +2194,16 @@ void MainWindow::channelDuplicate(ChannelGUI *sourceChannelGUI) { ChannelAPI *channelAPI; BasebandSampleSink *rxChannel; - pluginInterface->createRxChannel(deviceUI->m_deviceAPI, &rxChannel, &channelAPI); - gui = pluginInterface->createRxChannelGUI(deviceUI, rxChannel); - deviceUI->registerRxChannelInstance(channelAPI, gui); - gui->setDeviceType(ChannelGUI::DeviceRx); - gui->setIndex(channelAPI->getIndexInDeviceSet()); + pluginInterface->createRxChannel(destDeviceUI->m_deviceAPI, &rxChannel, &channelAPI); + destChannelGUI = pluginInterface->createRxChannelGUI(destDeviceUI, rxChannel); + destDeviceUI->registerRxChannelInstance(channelAPI, destChannelGUI); + destChannelGUI->setDeviceType(ChannelGUI::DeviceRx); + destChannelGUI->setIndex(channelAPI->getIndexInDeviceSet()); QByteArray b = sourceChannelGUI->serialize(); - gui->deserialize(b); + destChannelGUI->deserialize(b); } } - else if (deviceUI->m_deviceSinkEngine) // sink device => Tx channels + else if (destDeviceUI->m_deviceSinkEngine) // sink device => Tx channels { PluginAPI::ChannelRegistrations *channelRegistrations = m_pluginManager->getTxChannelRegistrations(); // Available channel plugins PluginInterface *pluginInterface = nullptr; @@ -2179,16 +2221,16 @@ void MainWindow::channelDuplicate(ChannelGUI *sourceChannelGUI) { ChannelAPI *channelAPI; BasebandSampleSource *txChannel; - pluginInterface->createTxChannel(deviceUI->m_deviceAPI, &txChannel, &channelAPI); - gui = pluginInterface->createTxChannelGUI(deviceUI, txChannel); - deviceUI->registerTxChannelInstance(channelAPI, gui); - gui->setDeviceType(ChannelGUI::DeviceTx); - gui->setIndex(channelAPI->getIndexInDeviceSet()); + pluginInterface->createTxChannel(destDeviceUI->m_deviceAPI, &txChannel, &channelAPI); + destChannelGUI = pluginInterface->createTxChannelGUI(destDeviceUI, txChannel); + destDeviceUI->registerTxChannelInstance(channelAPI, destChannelGUI); + destChannelGUI->setDeviceType(ChannelGUI::DeviceTx); + destChannelGUI->setIndex(channelAPI->getIndexInDeviceSet()); QByteArray b = sourceChannelGUI->serialize(); - gui->deserialize(b); + destChannelGUI->deserialize(b); } } - else if (deviceUI->m_deviceMIMOEngine) // MIMO device => Any type of channel is possible + else if (destDeviceUI->m_deviceMIMOEngine) // MIMO device => Any type of channel is possible { PluginAPI::ChannelRegistrations *rxChannelRegistrations = m_pluginManager->getRxChannelRegistrations(); PluginAPI::ChannelRegistrations *txChannelRegistrations = m_pluginManager->getTxChannelRegistrations(); @@ -2208,13 +2250,13 @@ void MainWindow::channelDuplicate(ChannelGUI *sourceChannelGUI) { ChannelAPI *channelAPI; BasebandSampleSink *rxChannel; - pluginInterface->createRxChannel(deviceUI->m_deviceAPI, &rxChannel, &channelAPI); - gui = pluginInterface->createRxChannelGUI(deviceUI, rxChannel); - deviceUI->registerRxChannelInstance(channelAPI, gui); - gui->setDeviceType(ChannelGUI::DeviceMIMO); - gui->setIndex(channelAPI->getIndexInDeviceSet()); + pluginInterface->createRxChannel(destDeviceUI->m_deviceAPI, &rxChannel, &channelAPI); + destChannelGUI = pluginInterface->createRxChannelGUI(destDeviceUI, rxChannel); + destDeviceUI->registerRxChannelInstance(channelAPI, destChannelGUI); + destChannelGUI->setDeviceType(ChannelGUI::DeviceMIMO); + destChannelGUI->setIndex(channelAPI->getIndexInDeviceSet()); QByteArray b = sourceChannelGUI->serialize(); - gui->deserialize(b); + destChannelGUI->deserialize(b); } else { @@ -2231,13 +2273,13 @@ void MainWindow::channelDuplicate(ChannelGUI *sourceChannelGUI) { ChannelAPI *channelAPI; BasebandSampleSource *txChannel; - pluginInterface->createTxChannel(deviceUI->m_deviceAPI, &txChannel, &channelAPI); - gui = pluginInterface->createTxChannelGUI(deviceUI, txChannel); - deviceUI->registerTxChannelInstance(channelAPI, gui); - gui->setDeviceType(ChannelGUI::DeviceMIMO); - gui->setIndex(channelAPI->getIndexInDeviceSet()); + pluginInterface->createTxChannel(destDeviceUI->m_deviceAPI, &txChannel, &channelAPI); + destChannelGUI = pluginInterface->createTxChannelGUI(destDeviceUI, txChannel); + destDeviceUI->registerTxChannelInstance(channelAPI, destChannelGUI); + destChannelGUI->setDeviceType(ChannelGUI::DeviceMIMO); + destChannelGUI->setIndex(channelAPI->getIndexInDeviceSet()); QByteArray b = sourceChannelGUI->serialize(); - gui->deserialize(b); + destChannelGUI->deserialize(b); } else { @@ -2254,42 +2296,49 @@ void MainWindow::channelDuplicate(ChannelGUI *sourceChannelGUI) { ChannelAPI *channelAPI; MIMOChannel *mimoChannel; - pluginInterface->createMIMOChannel(deviceUI->m_deviceAPI, &mimoChannel, &channelAPI); - gui = pluginInterface->createMIMOChannelGUI(deviceUI, mimoChannel); - deviceUI->registerChannelInstance(channelAPI, gui); - gui->setDeviceType(ChannelGUI::DeviceMIMO); - gui->setIndex(channelAPI->getIndexInDeviceSet()); + pluginInterface->createMIMOChannel(destDeviceUI->m_deviceAPI, &mimoChannel, &channelAPI); + destChannelGUI = pluginInterface->createMIMOChannelGUI(destDeviceUI, mimoChannel); + destDeviceUI->registerChannelInstance(channelAPI, destChannelGUI); + destChannelGUI->setDeviceType(ChannelGUI::DeviceMIMO); + destChannelGUI->setIndex(channelAPI->getIndexInDeviceSet()); QByteArray b = sourceChannelGUI->serialize(); - gui->deserialize(b); + destChannelGUI->deserialize(b); } } } } + DeviceAPI *destDeviceAPI = destDeviceUI->m_deviceAPI; int workspaceIndex = sourceChannelGUI->getWorkspaceIndex(); Workspace *workspace = workspaceIndex < m_workspaces.size() ? m_workspaces[sourceChannelGUI->getWorkspaceIndex()] : nullptr; - if (gui && workspace) + if (destChannelGUI && workspace) { QObject::connect( - gui, + destChannelGUI, &ChannelGUI::moveToWorkspace, this, - [=](int wsIndexDest){ this->channelMove(gui, wsIndexDest); } + [=](int wsIndexDest){ this->channelMove(destChannelGUI, wsIndexDest); } ); QObject::connect( - gui, + destChannelGUI, &ChannelGUI::duplicateChannelEmitted, this, - [=](){ this->channelDuplicate(gui); } + [=](){ this->channelDuplicate(destChannelGUI); } + ); + QObject::connect( + destChannelGUI, + &ChannelGUI::moveToDeviceSet, + this, + [=](int dsIndexDest){ this->channelMoveToDeviceSet(destChannelGUI, dsIndexDest); } ); - gui->setDeviceSetIndex(deviceSetIndex); - gui->setToolTip(deviceAPI->getSamplingDeviceDisplayName()); - gui->setWorkspaceIndex(workspace->getIndex()); + destChannelGUI->setDeviceSetIndex(dsIndexDestination); + destChannelGUI->setToolTip(destDeviceAPI->getSamplingDeviceDisplayName()); + destChannelGUI->setWorkspaceIndex(workspace->getIndex()); qDebug("MainWindow::channelDuplicate: adding %s to workspace #%d", - qPrintable(gui->getTitle()), workspace->getIndex()); - workspace->addToMdiArea((QMdiSubWindow*) gui); + qPrintable(destChannelGUI->getTitle()), workspace->getIndex()); + workspace->addToMdiArea((QMdiSubWindow*) destChannelGUI); } } } @@ -2385,6 +2434,12 @@ void MainWindow::channelAddClicked(Workspace *workspace, int deviceSetIndex, int this, [=](){ this->channelDuplicate(gui); } ); + QObject::connect( + gui, + &ChannelGUI::moveToDeviceSet, + this, + [=](int dsIndexDest){ this->channelMoveToDeviceSet(gui, dsIndexDest); } + ); gui->setDeviceSetIndex(deviceSetIndex); gui->setToolTip(deviceAPI->getSamplingDeviceDisplayName()); diff --git a/sdrgui/mainwindow.h b/sdrgui/mainwindow.h index adf825bc3..e49fa6d9c 100644 --- a/sdrgui/mainwindow.h +++ b/sdrgui/mainwindow.h @@ -81,8 +81,9 @@ public: int getNumberOfWorkspaces() const { return m_workspaces.size(); } public slots: - void channelMove(ChannelGUI *gui, int wsIndexDestnation); + void channelMove(ChannelGUI *gui, int wsIndexDestination); void channelDuplicate(ChannelGUI *gui); + void channelMoveToDeviceSet(ChannelGUI *gui, int dsIndexDestination); private: enum { @@ -146,6 +147,7 @@ private: void removeFeatureSet(unsigned int featureSetIndex); void removeAllFeatureSets(); void deleteChannel(int deviceSetIndex, int channelIndex); + void channelDuplicateToDeviceSet(ChannelGUI *sourceChannelGUI, int dsIndexDestination); void sampleDeviceChange(int deviceType, int deviceSetIndex, int newDeviceIndex, Workspace *workspace); void sampleSourceChange(int deviceSetIndex, int newDeviceIndex, Workspace *workspace); void sampleSinkChange(int deviceSetIndex, int newDeviceIndex, Workspace *workspace); diff --git a/sdrgui/resources/exit_round.png b/sdrgui/resources/exit_round.png new file mode 100644 index 0000000000000000000000000000000000000000..ee6c185f8d3c322e4aaba8bdfd36222dde492104 GIT binary patch literal 8704 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 Kym!Ij+W!EWmbQQZ literal 0 HcmV?d00001 diff --git a/sdrgui/resources/res.qrc b/sdrgui/resources/res.qrc index 4b39bd761..81fc74a52 100644 --- a/sdrgui/resources/res.qrc +++ b/sdrgui/resources/res.qrc @@ -134,6 +134,7 @@ hide.png channels.png channels_add.png + exit_round.png LiberationMono-Regular.ttf LiberationSans-Regular.ttf From 40e34bdebd00c13ce61c648728888fc3e37e94a4 Mon Sep 17 00:00:00 2001 From: f4exb Date: Sun, 17 Apr 2022 01:31:50 +0200 Subject: [PATCH 031/115] Massive UI revamping (v7): implemented device common settings from top bar and added documentation on device windows top and bottom bars --- doc/img/BeamsteeringCWMod_plugin.png | Bin 13932 -> 10159 bytes doc/img/BeamsteeringCWMod_plugin.xcf | Bin 42258 -> 30301 bytes doc/img/DeviceWindow.png | Bin 0 -> 30881 bytes doc/img/DeviceWindow.xcf | Bin 0 -> 81246 bytes doc/img/DeviceWindow_bottom.png | Bin 0 -> 3276 bytes doc/img/DeviceWindow_bottom.xcf | Bin 0 -> 14194 bytes doc/img/DeviceWindow_top.png | Bin 0 -> 11528 bytes doc/img/DeviceWindow_top.xcf | Bin 0 -> 47954 bytes doc/img/MainWindow_presets.png | Bin 7161 -> 6865 bytes doc/img/MainWindow_presets.xcf | Bin 24207 -> 26513 bytes doc/img/MainWindow_presets_view.png | Bin 59405 -> 56312 bytes doc/img/MainWindow_presets_view.xcf | Bin 176482 -> 158712 bytes plugins/channelrx/demodadsb/readme.md | 2 + .../bladerf2mimo/bladerf2mimogui.cpp | 33 +-- plugins/samplemimo/bladerf2mimo/readme.md | 2 + .../samplemimo/limesdrmimo/limesdrmimogui.cpp | 33 +-- plugins/samplemimo/limesdrmimo/readme.md | 2 + plugins/samplemimo/metismiso/metismisogui.cpp | 33 +-- plugins/samplemimo/metismiso/readme.md | 2 + .../plutosdrmimo/plutosdrmimogui.cpp | 33 +-- plugins/samplemimo/plutosdrmimo/readme.md | 2 + plugins/samplemimo/testmi/readme.md | 2 + plugins/samplemimo/testmi/testmigui.cpp | 33 +-- plugins/samplemimo/testmi/testmisettings.h | 3 +- plugins/samplemimo/testmosync/readme.md | 2 + .../samplemimo/testmosync/testmosyncgui.cpp | 26 ++ plugins/samplemimo/testmosync/testmosyncgui.h | 1 + .../testmosync/testmosyncsettings.cpp | 21 ++ .../testmosync/testmosyncsettings.h | 4 + plugins/samplemimo/xtrxmimo/readme.md | 2 + plugins/samplemimo/xtrxmimo/xtrxmimogui.cpp | 33 +-- .../samplesink/audiooutput/audiooutputgui.cpp | 34 +-- plugins/samplesink/audiooutput/readme.md | 2 + .../bladerf1output/bladerf1outputgui.cpp | 33 +-- plugins/samplesink/bladerf1output/readme.md | 2 + .../bladerf2output/bladerf2outputgui.cpp | 33 +-- plugins/samplesink/bladerf2output/readme.md | 2 + .../samplesink/fileoutput/fileoutputgui.cpp | 26 ++ plugins/samplesink/fileoutput/fileoutputgui.h | 1 + plugins/samplesink/fileoutput/readme.md | 2 + .../hackrfoutput/hackrfoutputgui.cpp | 33 +-- plugins/samplesink/hackrfoutput/readme.md | 4 +- .../limesdroutput/limesdroutputgui.cpp | 34 +-- plugins/samplesink/limesdroutput/readme.md | 2 + .../samplesink/localoutput/localoutputgui.cpp | 33 +-- plugins/samplesink/localoutput/readme.md | 2 + .../plutosdroutput/plutosdroutputgui.cpp | 33 +-- plugins/samplesink/plutosdroutput/readme.md | 2 + plugins/samplesink/remoteoutput/readme.md | 2 + .../remoteoutput/remoteoutputgui.cpp | 33 +-- plugins/samplesink/soapysdroutput/readme.md | 2 + .../soapysdroutput/soapysdroutputgui.cpp | 33 +-- plugins/samplesink/testsink/readme.md | 4 +- plugins/samplesink/testsink/testsinkgui.cpp | 26 ++ plugins/samplesink/testsink/testsinkgui.h | 1 + .../samplesink/testsink/testsinksettings.cpp | 22 +- .../samplesink/testsink/testsinksettings.h | 4 + plugins/samplesink/usrpoutput/readme.md | 4 +- .../samplesink/usrpoutput/usrpoutputgui.cpp | 34 +-- plugins/samplesink/xtrxoutput/readme.md | 3 + .../samplesink/xtrxoutput/xtrxoutputgui.cpp | 33 +-- plugins/samplesource/airspy/airspygui.cpp | 33 +-- plugins/samplesource/airspyhf/airspyhfgui.cpp | 33 +-- plugins/samplesource/airspyhf/readme.md | 2 + .../samplesource/audioinput/audioinputgui.cpp | 34 +-- plugins/samplesource/audioinput/readme.md | 2 + .../bladerf1input/bladerf1inputgui.cpp | 33 +-- plugins/samplesource/bladerf1input/readme.md | 2 + .../bladerf2input/bladerf2inputgui.cpp | 33 +-- plugins/samplesource/bladerf2input/readme.md | 2 + plugins/samplesource/fcdpro/fcdprogui.cpp | 33 +-- plugins/samplesource/fcdpro/readme.md | 2 + .../samplesource/fcdproplus/fcdproplusgui.cpp | 33 +-- plugins/samplesource/fcdproplus/readme.md | 4 +- .../samplesource/fileinput/fileinputgui.cpp | 33 +-- plugins/samplesource/fileinput/readme.md | 2 + .../hackrfinput/hackrfinputgui.cpp | 33 +-- plugins/samplesource/hackrfinput/readme.md | 4 +- plugins/samplesource/kiwisdr/kiwisdrgui.cpp | 33 +-- plugins/samplesource/kiwisdr/readme.md | 2 + .../limesdrinput/limesdrinputgui.cpp | 33 +-- plugins/samplesource/limesdrinput/readme.md | 4 +- .../samplesource/localinput/localinputgui.cpp | 33 +-- plugins/samplesource/localinput/readme.md | 2 + plugins/samplesource/perseus/perseusgui.cpp | 33 +-- plugins/samplesource/perseus/readme.md | 2 + .../plutosdrinput/plutosdrinputgui.cpp | 33 +-- plugins/samplesource/plutosdrinput/readme.md | 2 + plugins/samplesource/remoteinput/readme.md | 2 + .../remoteinput/remoteinputgui.cpp | 33 +-- plugins/samplesource/rtlsdr/readme.md | 2 + plugins/samplesource/rtlsdr/rtlsdrgui.cpp | 34 +-- plugins/samplesource/sdrplay/readme.md | 24 +- plugins/samplesource/sdrplay/sdrplaygui.cpp | 33 +-- plugins/samplesource/sdrplayv3/readme.md | 2 + .../samplesource/sdrplayv3/sdrplayv3gui.cpp | 33 +-- plugins/samplesource/sigmffileinput/readme.md | 2 + .../sigmffileinput/sigmffileinputgui.cpp | 33 +-- plugins/samplesource/soapysdrinput/readme.md | 2 + .../soapysdrinput/soapysdrinputgui.cpp | 33 +-- .../samplesource/testsource/testsourcegui.cpp | 33 +-- plugins/samplesource/usrpinput/readme.md | 7 +- .../samplesource/usrpinput/usrpinputgui.cpp | 33 +-- plugins/samplesource/xtrxinput/readme.md | 2 + .../samplesource/xtrxinput/xtrxinputgui.cpp | 33 +-- sdrgui/channel/channelgui.cpp | 2 +- sdrgui/device/devicegui.cpp | 15 ++ sdrgui/device/devicegui.h | 5 +- sdrgui/device/readme.md | 225 ++++++++++++++++++ 109 files changed, 1201 insertions(+), 630 deletions(-) create mode 100644 doc/img/DeviceWindow.png create mode 100644 doc/img/DeviceWindow.xcf create mode 100644 doc/img/DeviceWindow_bottom.png create mode 100644 doc/img/DeviceWindow_bottom.xcf create mode 100644 doc/img/DeviceWindow_top.png create mode 100644 doc/img/DeviceWindow_top.xcf create mode 100644 sdrgui/device/readme.md diff --git a/doc/img/BeamsteeringCWMod_plugin.png b/doc/img/BeamsteeringCWMod_plugin.png index 0a0dead8ce364d11d170c9e8343d428033637f3b..56c30be239ec95ed38600bbe58ac5f70730c8ce6 100644 GIT binary patch literal 10159 zcmc&)byQUEn;$?xNok}lP`W!M1Sus%kZzFf4k@KWx|HrAq`PAX=?~aT*&tYhyDDBM9VGlxLKXbh{`;r_N|FwF>`~SeAI;mf;7Ljt_kmJ#3yOd`v!$(h?hOq?SLM;p zC6?85$cYcp4v%xP?HA~9Dx$L@jB$s6- zviFzT?)tS|?<&1tlmU`zUL=LcIra741f!fnjZ7==pA-FA>UPmH=08QVr)OoBJc^Jz z4x8-Cn{hN&gp6Bbv1EkLA8s4l`H5AbB|CIdkCB(+b}vNaf4zq+3~eOawX`t(sa`*w znCad`iTqU(&C}BQlvOEf$1%eB%;EHOKk=ith>e0>jv+bb3D}lLHqz?$5C|s0{Tm6A zltK!=M0b#pmqcHG@R$U`8n;Bt2Z7K)WF*8?TxNFXT{TrsmhTQHS^G;#X+n~8>6x>} zCzZyI3bLIlIX^QpDcdtxYgD`a2>!L8InpQL9cNTsB-c=)Rh{+4$1KOlTt7+UuhnuO zrT_{GN3%$e46>T>G`h~G1UigXgS ztD1q2rEZ3A#NOo4WdT2l@b(>-JNNd$*})DrwO!HR-Qn#qlAy`O&yMY`t2Td(ThDod z2WJwzWi2$tnaj(ITMto{0!H0z8Hy7Jj|KY|DEscJetd+&kY1TfkKGu|y!Ie##?@L_ zI%)J?so0n0=1|fIdQ%vDrDeHU8arJto=8cu{+m6>K~Yco#y~@)lWTj2;Pd#Kf*y_E zrecPMt3)E+vsIQaUcNLNa6*o_)`T>S;?yo`=ON9^MGuNwDw}EWB#Yi5D-9){vrVVh z>Wr^XJz*(^Jj2LZ&dAKnSA#APi8RZ`Gaqk_%YG4mGL20|Va*1ChGssX^Img;O%{m} zbKE9sTFl6EA!_oC>9sV{j1GR}Pze6bBobTIa5kOiiY%{y?$ASviWtEeDer>({aJ07 zC7P~}Wbp|IzK4Vu9B&MtUtVf|aE%Z;O<39gY*ibOBcF`?zQS0(%3}KKw1v|wE5FM= zubnvA$cA_3!K)tUdLzct)+9@@hHEc_I>|+aS(0b29?O<5EU+_@oS!N>@aHpuM9Hu5 zygDtjoaNp*m38(ssGgO1_fCJY$ur!HfKK5B14Brfko!ojy$Ko)g><9a$+zI(x}7UD zG_<3Bjv?M#?~PwU6uu6Z$7Xd-yYIZ3t}OE`(~M6YJ(G{?ENfgl5$9>s+i@as^XS&P z$KBW$R%(t4xrt@OE;buAxD_8KkXj~TIZVOQPil6J7Ofi&(HPW9I$+z&-6seH!gVE# zCs_W=SRNh%K19dEGl};iQ!1>@l#^IBA(yT_5Ia*m2*<_EEjlHozvDYW*(b)1t}c91 z(jIua_h6mlw#$+)`o^DMD1ZO{?N8>3sHx$dsr>RbS0U8}L_Jkuf(uH>KOmsYVN;2i z@e@R}+G^hAax=GVcGqI6)K@BuEGjaxr9idN<)9Vabt^xYl`L#(*9jcjQf>q*(J5@L z=kLXpqg%X_N-81^9-2l`UGFwHE>#wI5n)nCiK2TmxHXgUEiXW2WV*rxY+}24WgP#a zc_lsxNjLZ=L|#!&&agj$?RaxEs4I+o>3 z*}xKpwTqxKc$s49!s9nFMt6DadJf*(92OV449N?3QAPw_IqL0<+7a0BZA&q*yjPL$ zc#Qf8+0c zbVuFoctZ>%p|rgG+FHC0Eei{DZkxMaO8NBo`hic5Y|v7w*YCdDZ(nbU<}`W8V{)}V z6GpMyT&0l-pD&@HVUzh+S63S(b?WO4rtoD8FIrW{L`I6fd4mkWCg(N;snVaq7qigl zzOuhRWIrU-J*p_O>msk9fYaLAs#Y!5yp(~NT)Z*oH*DH5G!$`se5{`iU+s+vhYMZp zR2*+lMw~6U<0cPu&DGjNVNdG~n9!A%2qb(J9}A!r(X)w|S|eitL!th*rYWwdc((emC77q<88lNnHIIvV|1@P*6v*<&(L?{j; zlO>dFY;2#3KQ=TrvLz3!!r=y$8i!|R%>_es_Hc!*NvPsVSJ-sDGYhWJ@nfDj8x#mO z8K*ADR|!eUsI;`f+nXy;$dlO$lI!+Wc}1(+oG+i-$^6!b#ahQ(fDJH#>Kif6E|_VRqR)=k*jj|;ybar zYCf&4?Q%w`K!q=Fj)tRo-s5hjwMC+b54$B?mJ^T^RVZ4h!V6*t)(4W!KvxgAOEIoU z1f7spA*BcO95c`}cotlqH(wvY94`bD^`f$VJ6XBLj@QGimjB3D^K*;5ZPXx^lOm@Z zbSqm^^Im_s(DUfTh%Q6aj_%^(qT%*(yWxB}jo^3aljgN4hwVNGyg4WG%2;)mCFtRXu3@^fo@t#=K&%Atj6~8&M++Qlq z-bR0SL38)x#kEpZZP9hIxT1~y#-rT$dXRON0-YC01pz8gE5G&V_MmjnIW;C@8)0OH zmJ(BZ;p$*AZ&UTnlC6*|p~hA9{q{}eJ8|^CNRR8GtBm{2-16W>&o?;@&OryaTCZ+R z_bJ#kd;2rTi&_AmJO73Hz$}uhUS6?%ah2b3sy8TY>;n~x?$_F4FJvQ6lWmc??p#6d zaFYT>iRqPi_eB$5Itnjwa<{>t{A}@jpb)!z$jx>q)IPAv`Q(Bt>K(Q8;`PH75j$yhgKTonvLd=hIPGjy$fn? z76BcgbI_Omvj}`!cb0qiP{&?&syE1(VdR&x33VLH?tx2o&HJrLGjn?+k95v-%QicF$Ei_`Waz71y%ezd}%4%^R;GXtJl+9MZdS;{FY116^EIz z_Fprf1^5dTN|U^+-O|scr<8Wm9YXrlgcIkD60!iXrgKWLsGemwL7N;;Z)6{fK(6p0 zbWwxDg+pE+Jj$9-49UBiKYmxw$a79u?&PMFj~Fye*x=s^4{mN(lKgBx$rWkAU2}3+ z(AcU<^E#6LYrAtqhc3f=N$GLytnGZ~{e?N_MNc`XrJrqMG zCiE}ij<6FU6l7#l0VhTP8Gw+0!+Sv63Bj&$)do0CKr5#U`g3OvRI@hvY|%U2-`}79 zY}Rf&jcl+Qa(j7jaPSlG#d|u{e0Q@?;<<3q9k1ghlP>JJF`A1zJUr|l6f|04G6ZWH zd=gRq5DRNH(Il;Q`SS4e^qy{b+?-5eQSe5-R?hiTs0Pg_yy??Z*ZP27a&QD=iJxi- zr4)!;T{Uz&-H~bbx&b2WCur$vs#buSkpc!|YilcbebDZ(JrO1uLX1aAIq*C}a5>uO z^}*4KO^{mt)%;yM1BawLxe=#+$3sS9%=vm}cz$l$@}R)}>fa#hkDYk|4u|4OOrNV( zTYOPG5Cc4dx92P87Z-qW*h;RY@H+iOG;*w{vAd-IdK0!7k$b=%Bf zn?6;V6aD)2D|{22ZaFg|*s2N!_Vns>mlH6~j_n%&R6_3DbXB-ar=Eb=9*xMd9D>kG z^*b@TkNy200NSFwMODQ?VJ1ScIJTHqX7X~HE4gNT|1NEIXgGUKz2Jf{I(kcTxr#=g zz^X%IDe11w|@*PuJRNVE1RSo?F~M<$I*^G@%(tHnaEA+SG6@SDqR zXpwrwdQx1cdF4pHGTr6ruB@9Ie}u>_?~u?Lg&#VuF<`B7X@ZH`jc!Cj7wjmh#>U3{ zZ4YT)z4``ddb!CERql8_!`9D+Ih%IuP%^GyyysGXO0??r5!{wD9e^Zu4~g7`MnvEi zf39Y>%^S1eCLkba2V}mCdKSBQhJQW4-ybj&X%7#N?`MwVo}V@yXCt+tOw~*tBTU}# zNUZNoW))R1O(_+O!GSJ3|+qOhs9j6^l0sU(D2xm6Mas~sMTqG z5ZCP2^XufK7t|-6hUIwEyfGwh6l+xn7HJzU(XQVIsRI|fyMr1U8uUj&^(-tbka!M& z)0b)#cjl-EO zMc3AN;pQZi8uW~8plNmhc?G4vw7Kc33V#C{p0txw)yf@+7JUA+j-Ug4E4!y1zT0=vt>don2ouu) zoI8|CC~0g=rCQi+?KEhIdG4pHo7;U6XuEA@>yrn9BnILc9)k1J>2Dw#9bFJen2}sX z>aB@l7?4{fTL_Qq!*0Q|Is5Qt2BjO(;o8Xiv;rtIX@t_J`kTLIq&@7OeovPhg||CP zWk!K@vg)-x0BWiun24ctc6W7c?QmR8ht0U3w0qn8`ta>7$V5ahU3#hKmD4%wN#odP zzM--4QP1-TR_lcZeZ=i)b@MRbVfdra*m0hOvCnRh(#j9Lw|fc;!^ttu$2og}n`1Mz zJ(g9d=;2q9mTdSAeqWfaMZEJ$6jJ%aGciD?s9WWJ#=%h`Vf`8o-4I&bO5%5Xd_(nvx1!7gVNlPoAS6-?t z*Z!N+ob#G2pe8&(!0q4;Ca~%uj~A-tDy9nq4237bC;+aypYGW1JbWEFw-h<=3Q(Jx zoX3iBn4Z^pj{>kia+yoOWiKD`pKp69!9IJ*Jhm9 zRi5t6!^#Z0KQ+2>0iMykqw3^5XNz8GK2Bd{f-9-!HjpNCRp%K*lnPvd^Mk+lavzI~ z^Pyj>sGS|Vb(8v0S{g_;J9w(|NRy{K&aKhO#c~LXw4!3J_uZ`^5UhlRgpnyJazNjG zuHuElt)Tq`*{2h!S{0An-8w&BrjZ2-Is9lq#%-GakoseE!6nh+w`odmNW`u7Z-RzV z$9V%qK!kw4tNdqu2>=}Dh2pps*Jomlhg1x$Wj`|Xyjg^84)cnUrr$5 zLxj)T!VbVrQ$X$4)AB30y8uQwT?;vGjp-vm&34_KE#it=Sy`18I+)IHMV}x*OI4|H zr=>Ti8AuhVp1K2RRiGuls0OjO^Q`kkfT_m? z+cb`pFVyxM*Bp71wj^`c;d!57UxQi)qg1S$7Kl5pxhgfCsH0y+rZ5W$&F2Tbm7otX z&-H5DL)qtB<4M`=ojafD4}%u7sQRiX1;t5bHt<&nI-4Qo1!NINtKb0IFWDZb@?$7N zw2u4icg=O=%AoNvXLM?6SaWmpO3rZK3mO_9&=o_|q$bCQYJ;@^8*c$!pOI&FNkYny zxW4BF2V?Y)FBU0=fs*u0ePSV9iDWI%a`fn5YbWZg026>zHU0Bb?0Xs#yU*|IQRM9;;j}?q}h~U-%B?LuX6JSCPyI(r^4>ecHn> zFES7sJOfBx-Bu!R^u+YCuk=Ao{M>BUWfaV>7EL?MD*q^YM|!!sgoXj*8Xu2%<|4 zGMhS(eL}X&(|tHjq6ewkd{cNc{97<<6KI|mwUulB^=V2 zo}Ni_)`w0jV8L!*6;7^0tJxh+J?Yid9kK7b&)_XIcik-x#O&be=TV7zGrjYPO9W}% zqP+Ip+)zlHhd^)0;MQk+FleaN4*5`dP;9h5r;S-}oE;r!(Yk7qwdZ;JsiMSlYQ4cP zZIzmHi#+a&e#JCvqTE=KI;Ntc;;Y>)cL7kXHd|vdMIXP^YNau&Ci68sh8`eSYgTQO zNw-#CJU;SnT1~4w@*Mk6G%bmw;!@?a8esJ0$r?3h`SCdRJ^CdN1h(qZfYt~k9-BcI zR{Yr*Q>;K}(MJZ$nYr&KN${7Gu@GjLpv=|HtTNPtSq#!ef$_kDfLz@K79D_`^#iFC}4J*CDE<1{C2>NGj#kl z*&_j$(AL%l!UYg6=1XZ=*>)HVGo9p62(zu)@0SnKyO6tcFGP zOq|w79CRO^)S>AfozSdEQ=zM6*}fRAEl!A`Q)CB+cSTU|PszIe4InWc&A|rk8yMCH z!1u8~-uO_U1|6)iUCC2}Qh^r*zr#0~7}{7;ZPJ!dRK?HC-5iz9J2)7am~8i!?kNLC zBEl2d*w9e(@ZeCXEK!aYxK~;e%bvi1zL(jGA~(P3>oXoi57%$NO73(Wt+tNgjWoVt zjgKf?pEvO&rMTVU`ve7{qoPU<%p;+o{mGIcxIEbo0#4&psUGO+QLTRHhz(KNi!gf9J+r#A14oPE-1YrM6rkRb6!U37^{B#m)uXKQK_AJyFHi zY8kK~FaW5_+5s+Z-d6?<8lu}_jpFgPpn;X}s7qxWDk&76+hQ{329=O5MKn8ISF#KB z!OhEDU}FoOj49s>FUURkOig`ajLUG>AZF6&_$lU>r`^b(pP%orITGNsI}`QehlGX2 zjwDPd&CJIKE%Ks%;+3;5(}U3PcDs1{TRw0v`GZy!3Vm^MRf|Q-vxqBIygu87uO#2@ zyjqujgXYWQPH?O1h)m`lRd+K}Pnjg>$_{8YDHhtu=%{+Lmk_nb0TM(^ObqCd!NWO3 zqc*r@WgZuH7#J8Ev(+qMXN_TTO!o>KY~~M_h#4d*-2va0E{)7NUp8vqBGH27v6*6h zUx&ST&9-`U&2<;)=>-EI(=6s{%Env)64SnV)pO{rtNYWSZr%zW!qT(^-Yvkc1+aSr zdKCvCu# zJuo5cK;kcWAmoeLRPU1!RGguS$qF3q+>3+-M1N0zzXMk6Q#(E&K1|m86T{=CQuyov zgSJA%i>bAzKTB>A;AMxmv_aICyb*ULGsK zDM3+zFt#R@B~f}!Im?~*wE-!Wu(4r96Ynh{+i?;AWPWG18W(gh)M(8ppsPnqv;@FV{h+NJdz=QD&`}@m znFuGmJ$(E+-7*|wTu)T>~Ir#-tz|GNcPzeXQ#FTEYJL{5jM9294x7%~F4lsz?Y z(j z|N0dw>DoB4zafwl~UK0L-VC_N-0^o$z#C%*uLA_lxh}zXOLq@a`HeVliJA3uY}badBuZ zKv#S!H+*!mJ$Y~aKtjoR!hifAbUWD!0Mjy1GGM|R(iuXcW2f!qB}^&cvfTIuWcihia7Ef7NB7ndorj%z(2HqR{IM#K)`R+D7c4brM zC7jmP3~}qmyRHNl>kbyHn#>^;I{=(BKr-Y~`H6f^cW3R-5A?wGfV|Tk{#z>oAcu_8 zv;{P{Z&<1{u^~>*Etc3cTdC#oM3j{B!249MbI4y|-=|CWhz2HMOmecUm>AOS?JWe< zYN;#27{GTo5Or*%Soedy-@iFO7Hc^6x~erCnHj2AXG9rg7XhOSR$RT%mn9Q(aCoQ( z=7(UkiRE*C?y^3Z3frw-90azN-2Or%7-(Gsj$=hGO|)0jTAC&qv<~lzEl%Mu8Dw?X zc)wS(f)mH0b3d~FRAciL0A?r!AHENuX|}+itN=C-7_0(kmWGirbPX0?VKL2aRlm#4 zZ8i7Mrq@-8>&DS=8qumU(>^q$_#F1`(fte^6dah4mg}}6H(l)s5VL6Sn93*u3l0#! z$bo?B|xJ?c((bKb@0 z9Xl-IlssL*HA&;exu_S2#JA z*TaU!J|{MDLkU_3#bGjrwVcw@$Mw6cr~xF@-muXPneJqv1J(k^7e*vcqBi4Ts(Q%1 zx<1~57;hC@)sjmZQ;5UnD?Tid6X@?NUGu7uox~=378xjroJM6-pYS)=Gxu8x zhRu0I(6?__Gj%7|I(wrL?*n*)IQ5){U(SW#|Bn9tO+~d1P4(ZO7=p*b__A=ALZnGU&|IEY8 z%Ri5ke9*L<({-x$UVE*z>x9Zmi@Za`L4<*Uc_$|NO&$j3MLzg@1OX2GcFM~41rKog z5+dKgBlvgwwb=(eys;HkbAW+)i~juc0wyI53%m&LC?+KYzmA0efsRU$(R39C<`azA zw=asW^M^|=svl;adQY#9I>vKJs0)7)o3jdc<48;*kPGMZeDsi+Tuj2!pyf!wIzKOd zV?I#3h$VH<+D$nf!Z%Ap{makK_oC_d{r#(1-%r1M*Ur57qDD!0s!d!*hek|BhUOeT z_#zVppk~L(ien%U1^m=S6GR{)O$tC26lHt^-awQ5pXUf~$p7bg!0T94!T&xN^b`BP z4r?d+Hdu~5E+I*$2HAg*`&j#o3l{TogRSK(qoHZiebn5@vnZrn2Mf#;mGNuO5)E&A0XR;E<)`aE_22e5ZofJNCwr$5L4NMtRNFoo*zSz!0xm)AQ=+b=<OCLt9G6;>WjZ=;Xz3%fR4T?j_Y~uVg$wEEZh47bY;&v(ou+;9_HJpmH;U|CzS$uO z(r!yDc?dG-Vbspal7>Dt$^?+h7`F)fTl7YbK6SG&>w~}J!z9}F)s)MJTqEE}6eeB{ zmd-XXVIbPws>Mn7L`L4++^p{Iie>V9We|vGWE?Eiqd0Z{fYZPf4!Ud4KtlBJ@PK=V z6A%{mHZ(N!;k1|V=I)N)$;%5C8~fkU(a3Nr2b4>d%~I6dc#u|r$NiF6ca%Vu($;)B zoWSRyJx3xDVX^r-Z&GY?y3B1~0IDZZARUm-6D(Mit9-oN0QoSL$~o`j>?3gsHt32^|YFPf`fq0r4^C{|H13HbmjlHRoKp(sGNcaNj_il$c zb=KC{7&exYsWuR!*~|U#7O&LuR1O1kixQt7z1x{A2X{{VhzPnMJ~(SlUwq~=G3g;A zlk>T;Z91N9@8fYfxp{EF@i~PhJXq{U@bTHZI85DMuq{-HO8WD;V@V;!mWxY7Wr**jft zrb2<|oOZOz-iTjD^w0YFDE9g4iv*?Ozs~J1Q%vd@gI5~9_s&*6M`T-Xl@pJlPijKJ1p3zu1c zKWR`f)B(-FT)g=hVlrE6fz)arNZ@g`lbIt+ZPu*&f)Eytxo(v0-4iGT_%lP;t@3Q)N`C*S{-V4eHb14#O*q0}P z)aoApqCjSSJgZZoK8li$-E#KN)8qZ=*;%&B@v>V`Oco7#yP=tx*l-FPm*Wm9cY5US z-;Crk8Iq!+qTfgPyzW@v;jsQ08bX56(9kGWE(779g7|ocm=`lpreUCv6Vz5a>AJ{S zTw^tOJe*RwI2@7m>r+bt-K$^n6Zti1O#Trzkm0JmsneAvjEX#o7osv!!<@cU@s|7Z z@sEUF-$3Y)Q4#x<9YaR5?AA@WJe)I*NlaJx4;GhptE}Nm) zHjB&Bc^teB)zj2#R;_GooUac0q2^O-8);k@i_Mtv>Jgz4@0RLqwjZJd&Lq;gExot# zm$h4x&Mx<36Jq?t%Y?Pc@dUVd&|RS3k=M}~DdJOChjViUDEsR5^ieeY$$rs-@^A0HtV4Q_e+HH z^Bje(O~d+fExY3}{}mh#{n9G}tLxPXtY`cy>7@@SO(O8`1q zUsHuV?&5qnfDo@yFN=e1=K*Rd2S1CvM8$8^&{+**RfUQ5(CT>=L ze0gUocx*qt@VGjeUD1K2q~wwow_JXl;&vw0%K6uGcL~$A+&Xxu)m%8$T>r$ktP0^j zUNP8lTwJVz)pF;CiJqz0AD!1)_D;31n8}uqq2QwaK_j#Hb|9Nx1vy?_Yc;Q?_vxwY zdEGvKKm-f=Pkj4bJd{#t4myyvyHDAE+^ejg2P{meaw>wp0s@ zua7h}n?6Wwc{?8k6BbUEVuQlg(DKOs{r)QY1NNIgfzYSxf|NyUVj!e~1;ECepQ zms0w~(^JKp>h<98#re93*=Cm)4IU?-?AqHE-S*4;Kr*uZbG$y%($v&MKt!Zw$$Aav z8Aa7RJLC_^%Oi^-6c}kJ-CX^wudg5Z+Z{(oM~BMFS`;^E+E zMaKt~YWqO){C9~bwon>lS!>;)pB)F#Jm2v z49xqu>f6a1*Kq~;-}ZL@#sc5oS_l7W^{~6V*f@>A&F$$$N5>MCHzZA0uIQQVc&Wm` zFakaK+GfPtWc$7A#J1S!ASASw4MjTb6B8|MFZajDNK&Kbn#9Z7VSIe1+G-MV%AHK3 zxoMA&PIp&%fFV#ug#R_G#Y#@zoad-omT=V7jqHsfCSf@$!_dDENvx2`?_Y&WiNz52&?F=pw@Z_v_Ip~`cbau|)ELFq9VhIA_ z_wwoHRR#1ZvCm?rVShabrRO9JvuLc51(YI_#aQY;@$oc86ZH)Fb#pc#hrj*8W z$PZhagC++)Sy{Q?L<~|69n=i><>II8hX@$WX-YIVn;m!lq_UV)<-0lP`}xUI#(Uym z#9u#s#7Q9ufEgM{q#$Bt)#y}Gm&oIatI}!PUux#iPNsXPLX|ekKkxj3K~o4g^z!nu zWD?_dfHauYaWFONbQCffxhfUfcmm!JjoK@d(+TchgHSW($Zn?-=vcAX5Dgq09yfEZ zmY47R?Jm-Jz*I8U{r=UyGe#LF9?pF1z{ik?jK9-jEF}yN7x*$zUJQVE(Rlv5>qxx* zn^K&JHuNB90v*U0;?kL~pEzsi)RNpuJ39UXtoS!bt;VGS1zhKkSezVB~*J6rF? zE)xy(BVX^`*QiuWysN5PI<#6SoGZgsRw{WZ3q)(Bt>Xq5js2ugw;&Zwc-0Zt<=&Kp zx@1MMFGbUku#nKHm$6M=M``BFW(zVtx9#3!ksu_vJe7Typ*F9l+wS;8&)Mjz=I;Lf zn5nYXNY)so5hNe7NhJU2_4Ug6@-c+vVgLiJvxAe_j(Bs$)xRB+{&$_x;C6W|Bww10 zj67&$e_Btjk{Hl}UJe_3+XGdvg7E$Q=`n{De(8~n;W?1_76Tueju%74%gFiI;#tnO z_Zd2^4(H>Y-(v20d|v_(`5REI3wlrGUX!6;6U9p5Elv_Ep;@O2K^}+@$I#j`^)dTt z+B*WCpy*`mF_$CTr(VLwpL1%jKk<pyXx|b_0$pdBTOdB1r^7;l`JU;yMHLT!m zGcmCTHYaK=cFRxXlp!I5iQqi6IXoWhPa5^|V9l|;*Wvo3qaPk0uQl%)v9@SBjxKUmWyT7Af57&d`IBLdC>ndAyts#v`%eVxH|9sdjtWw$NZl0vK0h zG9>ja<7y@>)}*Q%N{@hHp^MR8V9@wl^RcMS-)C)yakEJDHR#vL`TAtQ8%F`By<`z@ zm?Um@)&*vtT$&#Z3dM?dgBrE&m)ZSYp%2S7MT+5!rpHz*(udc}G{3L4Dzsa7W-Cc% zoR17`q6jpLRHNfF;n!<^$ArhL%%=nKx_ohEytgw38_9F|cP3E*og4<$eeJbBJPN(y zFC&z?;5qM7ixBeCzj3N_Z-ue;p(`NLZ9Sk~?2vP0?y`g?e>kHWf&fNak0vMo!-XQX zO8qv0jCUvJ6TvZA%sV3}qb!Bydan-6+Ku~)S9rxEQoCKE)}+ho5ehGrBYWxTwSE}* z!y!TJURk+w1GHb=n9&)OlZZ>VMGL}|{FjiRNu`gi@^lQ%coc)EVZGM)qE!_^ z(|sReli+8UPP00?-xZFZWr%U+hN835r7s*vE>x$T*LRIVZbpRp7S~NT2q^9gDW4u| zN7dfst9;23jyQ1*qJnvNc;HN&bJE~VUEP8bKU}79=*JE;qsuR+gRdy6Hli%9iBmap zxV_b=@{Dp|O285%x#pbH3!p4%XEhr5poy5YPu^U0)>#|#OFSayVRLwP>8hP}k6v5~ z&2A(D!JH$I6rO|F+$Fywo=#p47)JSCcq$;?8~dVl{{2ECEpot>|0 z)uqJ65hk@R)vx^_{M(F`e1PhLKctFDQOSPck$7N<3~q7 z5e7?bWHE;P&N8>(N`X$EdN(Oo6#@}me#>w=Pa-~#v#!2Az;t;9h2;CLxD{(_>r%Dq zpxj(iuV0cGyx2-x2(!Y%!r=sbDRov0`i6!95Qspv$tXD&SMpSeih+g2V7^o;r{@jh zaqBgK)k0mKdae1x)q>64M}_;VLpLzcM(0DEqoX5E9-eBW!H?6%(s_T26exmF2qUEn z)@LgX8X6lbeW2dXhx0;-^jcV)wx6iu5rGz;ueF$KaWpk4}zS0}g*ryi3B+BiMTUhUnnrrpoNGTYq z&KWbEZS|=4rVgj&=jWG9WsmTFypyXomWmT68yg$@W-N{V7vkf?%gc+!YKp4W>`X>V z8sG!HUomCHF&;@1b97{Fb-!FI$n;(VQlD0%E_Foh&b0Y298vV+qpeon{NBdme$rH9SFZf0&RP5Z*WE1-H)^62`tB>#pE)L z`k0?TVgB4*?wd{I53>Y+RaM2pWYB*7>C-2ec=14x0|W#Fui@eMP5PHA4T7I)$H&KA zA8wey_BMeR_nMER^$?%Mq?L z>__8&IQd#yT8hPDjMN#7G9y62#B`dxZ90I6`&l5hTs*I1d^{#8DGBeGg4RJDu1tBb zm(XX(_r|YlDN|*TUiMND|PKydeYlcGd?G>$NgY1f+ zn5bx<(5TH>KNN|Ah;`!{ydgptAJ!^uAbH z(gFY+acJ%96BUlY>F$dr!DiI;eR7fXdA!6#r;vWF3tSUbb@eLaVH}w3oSfUE=3|%! zr~Qv81iZS(%dO#r0vTU}k(sO)IrpZ^1|M$E-(G5e6%;(Vx|$B&&H5VrtUEyc_w|L5 z&g3u4Od#RoOV&#OxyU?&hyNyaxbcGkp6Yt}w|xK`*iX=*>xC^8d&Nsz)~q2#n& z_+F;BCz^I~l^Dz|X%Zs4%aP_sTGgK=9yhVpiw*5JLuMuC*<+@vRfcasru5g+Qc;Bh zK-h)0+5US8Y_19D601W;Gbr=+z=g&ewMwyKZUIqI<^AaXxRQ~P(Pcb>cR$DgIRQU? z%Nlz-jLhTu`P1S`2t@6QoCPwO`A+-Oc~4%&Q|8Xc%fknA)u)%2d3v2eilwU2`!f>T zeYJF(p?N{(h`#y-#I) z;(;ne(WbhqtBZF<ar=Id=3G-_WzpoltK30GNE zGgl2iiPV};nXa_SRmQ89&2z1`9OPsNv}FOpas7(yc1`b3Y$Yh;_D2a*4gvzxMeprK z2u2@x3s?pv4pSXF z;}{x^G0yE3f+M>-ra`1}>jGj}g|upTl$1!%&64(ABr*a?Yzj`IZp{qT?8%y>$h-FU zkTsf$GWeEFB3#+HG~pvVW{f7(AUsKt2i>izH5!{O5m@*ME+2^pq*V?w0y0P68YHe@ zqmUIE1r#vEE-nstqKYX;M|3W31ihX13+9_!jk5`75l8NH?{SDqXmT<(aK)IGD6@3W zI#0V~@|wJ>_WsPyCZSSB>zXUFT;*vQH=E7-rRLMQVwrVxipJ_Ml#_WSDx4TaBWdr0~CdMFtNcGp_Wh|VMobvfjMHIdF0gj)KSiC43r}{P?smCo>W^>tS)_(r-_^+Oz8qjg`F zA@5z$@19KbCnVgtiNQC$L_U@JLGf8!K^3cNd@^gG)7K*c=RwttJN+o0|0LnU!v|we z#P7WOFTnb|W8v{3t0G%He5EKK=z#afw-c<$K0f@TM(-aw_yNUVqa4y4cy{Nj{^jyLR6X*izn~jH4kO+9OOiWBbZUR4xq1pK`#zumDYip}i zqdpSQC1#TmJQ!-F;;vk=*h2X{MD?-+7L$?RuBZCX#EajH8*q>ZFcR2=rE)EvyX)hq z=4Kx7Apn5WWg5XzQ8J-k8I&{ZT@5W_wf;H|+e=WEq{EscUD&o2&C9^H{MuT!i2|7d zfMI~JczSw1Bb&O_(Hl}PPtPPai;rz>ZCnH^l?LzvPd6l>@G``%Fc_(sG4b8X6e*4a zn&o|e$ieNnb252Z^>jE_9SqJiR-q%1FPSU|a(ZX3+T{BvO{9;I9$hx20rq@ML<64R(#ltYBEZD=V29d>+iYe_$4umO!ca z!xFy$IH2)RQv1+-OHJ{8CSnSSB!!2GikFu?q(?8ZP01yb zKHJ+fr}MZNum1VYYB3#NuGK8wEgDv|Dxn0Bnf%iyUlNhXzs)W-v~+Zr3W=DCBRL{b zoIX#yBO@aW8}b@o08tf|mq(kPo{nSC$vE2_WC09{Uc03aFuKaxTBC`4DbLex%tm2v zk1B6PY7EHJ9ZfI_A>1cM#-AS7NB=f9x&Y5#-Y~c$(6M&yg5q%NVMnGoC&RZO~~Ow{XXFe#77`HI@&G6 z4l~%Jtk>YZckJ1W%TL+1x-VYP6?YQE3i4>xq&##qYrm*We8^!*0C}t7NdJ^Mn+@cV z9cY7ZIb*087~Lss7CT`6K+WH; z?g|VH#^Li`fGpq-hGVk?fl|iguqD}cyM^n1xffq)-~BN7BP?tp7|l059Uo|;O~Bu~ zm5)IrFo-wWZzl1&F{6`9{sQlCC8YDZ#YE!s*x#NR4Gj&M&excgOB9;drog`aXuI>5 z$dO1TOHAUC86Ft|MrPo@^%SQ~@nmkdY1n%F1!)?~6de?*QQoq|-Y_M~{<7wyqBs zg3HS2fQlSFb#-;UQ8*_2knaf;3*h`Q%Zq&euv8rsG%n9LQd}9 zst~q-H!qLN(Z)zRPov*%FroMAv!+;W_Xo7r7AT3Uqoop^a#_mfq;)x3)SIg^3is4^ zL`PRZ2Y&1YI$}bNqk)6pyma^Gj_0IUG~D%)z^ch$K1t*ae5!$EZjRhAC;#Ire}#(7 zFP8;IZvok&S)B<`PU7O?@S?~lC=ATa?{l1?>x%v46 zc`Y%{masMECtdB74#nY+iZ8KWx^X1|=TX(rs0RG^(Ew!rWSK@H2(oRpp5<1L+shf9 zOCB9CLrEMq1nWH!gS85qCQD6DNQC@&4h{~Va!9(Hoc4csV$%>2eS!LvDTj&YQOh$b zRcNP+#gbP7s-zEjp&{=iI}X&N5Wt&@bo?p>>Udhvp90xZ4OG&kIUC4NgMm1zyUS*$ z{h-Q9roqJRM~%FV4MSR5+TS%br1a$*%`a9gZm~Bz%5{AfdRLJx-}DPhYW+)Cul%}w zh=9aq)TyFkHu0(Hn;&qU-lNB+84#qTrf%QAI(?43Ey0t>;iy!LlFBR{B3{%35y@@g z$zzl59~dB0grU%JMS-7o6&rvm8lJ9old%_RnU(l=oSdC?jEzMDS+>WpU!+J)tx`S& zw70E;g8@*^z-y3~m%lk56-Z{cN_o2Vfj)9TACG-*SD;V5AX?q*%L_tI5Z2b#cBad4 zA&*xYkw(vrDmL@UzRR0)akhGrY-<;oktV0UTHewxIvqF{c>|S!iY+zmx{^nE{Zmsl+ImA88X7cnDhw}SM!4T+2>9@W8n+HMJQ)4^sm1MA zbQywqtxl(2^NE<4ScLLR&*OJsQ0N*N1%d^++@Be>s~{7}!^S=Zj6WtVO;S~L)}b{j z)BA2W&{O+=bV`%#un&;J0H8pb5(^!zxy#=B)!=zKrNZ?@!0XDWRII2slE#IFiwnYY zZ~^p50oKb>$Jdxm1f_D=?9D)Yp#0yze}4v3E{Cnv>OI?Xf6#~sOJX%cQ!o1nc6h9< zuLF_$4rq}L=u^c_lihxt`9QXLJQ|fr@uqW#U1(e;fNFqsSbXlR)yBgeJv~8S^(yT* z2e#=Qz|IiOW@jlMLDF`^Pi1)rbbbJ>*7o;POX30M1_BG@aK3gOM1D9PR}2s`yNNEl zJD7)TM@!klQ)AAFOQaGC-!}aTw zVq}^gFEqw8@K}Ujo$Zdnjr91}5%^rhpX&pd4^WJ6*P{e77(`2yI)V3ea&m%~UR`Ii zf*iFa%7`t_D3#8AS+s8~fTh!DF9dB_<5i$5_dols*LeZW6!7tmH1YJke94Ah+=7wMjc^r&lFZ}qZ0vPjzI6IP-?iW`v7@66ktl-Aow1yd zZ4;BcER7Nb;a0*Ji@EquZ`ld#Xe(zT!@{0ZKTv&Ug5|i}X%6BqmAb_T^yf8_%YEx_ zUGx2DrhQAhglpb*MJ!O@!Df~UMLbj^QDW>EQQl_4Iu&xxj)1FoIfd#(*zqrq_?Z9s zX5w3*DAHE0FomknCM};)x>8?^6O)tYt1_e_oEHB^fSis~z|bOBzUe7l-tOA-lCF{? zOFGLpeXt*`GeT7{V|pWp$822nj!NiQ$ZvP*-jpcaU_fx{}X2+Eh9JRUM z!eFyYyCYL5E(Bx6RkOB|fiWp}PAi0EB_T6z?-~pGa%56x>pR8r1#`L-<{9I7KyWm9 zw|u@)pTMg&96^zwf(+M4>oY;^?L|78k)Ag4(6J!qzl5+k#3Xe>neEwzE%Y34mbf|6t*-=Bbs5BT-$ z-!Pg=n|sK=DKV|K{zgn=Ql= zt{;B3q+(fGN|~q}5lbNhzK!-DDWXuB@_B{NV;H~J-Ltm@fFtwYTt6JXB*P=FV!3#a zS6+yO*d&4FC=YJ&gj*u6&7*7}H9l?FCL6g_%C{2bGSj&#vadl%SpX)Vsq)z{>MfiC zpylM`KXi^%~Rf$|Yp1tZ~qX zZ2@3U8W z69p)stKfOk4iEDT(I4s5e_PCyFJcr)ZS@s8GFHDzPh*4Z5wDG#?xw5me6g;cwzIeI zn4MLn{`(mijC$7A+QddWp!iTX)W9joy9$54vGJ7L=?@71E!g|`XgAxGD*x!EQEHKb zYS-o(#6qpp#`_8$d1JLboIe3nB?HjlB&9HUAU6hb3JIV411>IZM`!2rz9vAupT#WD zFaouU!FGth%aQG_jVhR5r#ODjgJ`P!EOm+#&s#KL&RzOUUmZWJ`=cr2wH9L1Rzrvv z@iWmMpO}!N`~X}b+lw8!XIB^I;NT$S$6dj-&NStMv$M11LR|zXNI<`ia)8Y@U=dXt zY>Be6vKqB>VrtP?Lv=M2uBgBM5{Eq!XNT|KsW{6qNFRp!#lCg@ho0ue|VCn4jMxoY{3eSZs_1xTtSt2HJb* z*c{e)AlOHgq*FS=>st&EB0}R@2Ve{#y%)5JXP~%ITqhv~sF(G0b$z4GriD{b_lq19 z2%az%6iOK#;8UO7%!~TbitFYKR9`+&e0bcBA#rgZfMMPXq9r6EViQofE+A$BXG6op z>}~b9wsjfxK#WpMf3F)*TYI1w^ofoxA|N2Z#F|M>O-;{`6q1g<7LQEs>v3-&79mk! zY{OG(VVIWHwJ7%QDuI&VF5%$97LYewP_rJM?vFuA`}z4LF&V)9w1ezE6GlJ=atjL3 zfwx@0;!Wcrh5bvB8kBz`BBB=(5`&%;@-=mJ{q|kY~=vY_R zYZ4NYMmhR`*9z^D#RV^z+59M}VRhC2aPKBV16<{L+27<7JGgd+&(C67xWQVtnD4^z zWyC%2^2Q(&-Al#f1iqXg!l|I2S#zRKC`35SooW^FF``qBFOKMY0IJc~rdTvu(j@Ub z4jma-6|5~BYOwz~+wx6}uDfv)M55eM$Y72l5EU3ZTzHrMB;MUB+8QXXgCxgB+YfZ7 z=k;-AG};KsyUHy{z?WjRkBZf${z2l+=%98Q&vBkp03VwPAsf6lM6%gfUHt=kQ>P~< zpUaeEV`4ghbO!D;IgkxtyB`jlc`l)qW{AnVyzpde4c!kZP|12_W#!zwyne}gVt7Dq zDKb3;0gY$k;Ml%7UH7~{tS2Qi8Y(;);W=j1??SM&v}^z*_Izs?$R*s*A?@J5m%V>Q3nx!+2VH>D06zU)breV|pmxH= z^RfXsl}urM12EPbm|dW|6$IKLK)3%{TEYh}UL7vg@#Za`Qz*l!}jXw+L9 z0e_vEk#i10Wx)x}VAioLHC}7}AdU|x|flxe_aL^{drm4d$dw3Ryr)xsM z5E=dT#h;O#eOl%N76Czqffm>!4XmIMctX!#kZ;vi3+(O}JO6P|nk+km-T@5j2(Cl7W`QV0eZnF!k02Fboa90q3u6 zu*$Hu>x={}1Qu9`AsX|+>lq!N?)mu?fc)qHwm`t`E;h1&G6h(=o_UHxnlK9s3$QL_ zac$ER26WA*VD_eb#lpZh|F;)5!UX9D%b*H2*MV9#%(>sUHI^d+gM`gY+UvOQ+g6k@ zQ$@iru_0pI#74_JVKt|WSG@?XhuP=Jb89pc1i&cDgPUQ${&=#loe^z(>rVpwFCR(xxwhPuMG1X z!Np1?uImvT4W75mgx)tq{r&x#4YuvS{EH^FB!1Tiq0F}*8JtiSn;HoT3p0b+#$h%6 z1G?YOU2bqC&%G#s1}|;THpI-Qio1afWMXGGo-S2;ZgGPcsIy)Y2ZqYYaot81a|n#p8BC5&>j zt?0<8^A7JP*TPx{~KtGpwJlW%5pJ2%gBIuo{5Q|TwVo$xQQNJ+? zw!|U|pe$YG?A#MoFz1g@aN8M&Lx#Tlj*{_sOF>_9%9}$@KOAlmMQ133AsF|@=@31{ zqmZd^RMFhe%#edop3kQsI95dx3(vF*+SH4VuRoNp5n0uXNQ9(*oD#+8G|lLqswZ`> z06so_7VabTKH-=Cjk83XE6Wcus}3_|D!D67qJS^lkAKrr-wPux8H>64>0O?&Ht5_0 zcGD!c2<*#eJp2GnInpHU7(X~L2+*I4`>GKYu4WQe%?s`%3KDs-BYr33=##h#Z%T_R z_kZ4Y6oN+l${E~w^uM`<=s$NR{r}=lq-@3~!Xzkp0z&i3XK-H_jF^!0w{k&UzyAYn COuvf& diff --git a/doc/img/BeamsteeringCWMod_plugin.xcf b/doc/img/BeamsteeringCWMod_plugin.xcf index beb80beb87bf0a7393859d7a6c7b9716dc331cca..dbe0728d36a0c40743627162bc81111d2d38b06d 100644 GIT binary patch literal 30301 zcmeHQ34B!5)xWc3VG%KG)fyO(3`hytxCt1@qzDM2W-u&5YcYfnf?-Xd;5NaH_A~wI z=K_jAMoL|xR;gGZT8tHC$xs)x{wykp5Y`}s?3wrO_djo$c@q*c2CDJ<`QXew=dS16 zd)|5Ho_A-GoilHtZCU1Q+w7d&EXJ5=BR;kP=(+-6GU1~eAbiwgj1l1K4zL0S0eWgU z)$0M+1?ZZMZliPNWn^bXXD`l~g)BzpuMEt~U!I#~%Uh6}Gwa%p17~MvF34T5*fvl} zq8De+%&^5O|KhGi@yuq5<7~=5DqfbiFe5W(ezq+x7S!?pN+Y6|%+JZU<;>5Vla)s; z7R;WVmz8f*>Kkws4XjK&YnN#OV<8_f*5MRmm){TZ1!GqhF&24@v3?ITHn0z4gFj|0 zZVF@kC}YVfj5+ROZ1fh!7QL-@lK-;SSudnCXEYxcTN34(=>u0NfTk7Qv%Z8o3h;@k z2T(pW>7e0ASU&@PwE^#Fz-moD>Zw;d=Tjw z^Ye5nIxpw0EL+^LA+gG()lqWQga^p z=0`74I-Hr2mlZuLXK_|$e$ImVw%q*1XxCI1ZE@OIw%8%VltF=LqoexVC3*QdvzOa) zvu5XmoVj3G^t=VLvTU=K&(D~blc{AX3g}~PP7Zj3#wv`F=-OrZlZNQpfoZcm7no<+ zM0AXZU={_eOvs^F04r!C-jPX?Xr$bZ%p`{(?ZBi)A*q;27S)7>GwGZxhpLz)yo7}@ zZ-FeAs;J}@a+s2dCUt~`GO0$E!&OWc5@9+s5nwFF14;oa3-^ElK$u6A5eBCAc=o86 z$pZ#FUGir80jPAPrnFJ>w)!=VyT~a zzOk1^fbH&euZ61BKr9s#Gy-CnzC3L4QPK_KK@ttjgfZzgxt~TrieGyF5w4biC=k;$ zB9L{Jf>@%o7w$V#Ds*EqfNVskw53^#ad*v#Rm@9!GAr?5kD#q)h3*gS3Vl~ae4^u? z3H=5Z5>KEGp8^gBmWROvg~5b{aljY=rYj83yzpxQcLH7od`E3uT`w`P0_HMV#0X{% z0$e609L%M*@t{o*fHoeq2}xDjISO3te(3y&0u$701Z(prdH^57QK0j~gv(5__&H=A zEvL!&)QU=om-g2~GE7(N;4)c#E5SUd9|WL{VJ1=vL~07wAVrK&?N73LW4AqlLD2hB zJ9c0Q2WhPvKn@vQP5{(hxd!5sJOaA(#|v2ri!G6|+LLu4r(*`7t=573sx~`@3ik7ewlF06UCj zKDzEb;TaKr)m=GS4w4j6-nD~jQX$@;(_C}o*9~FhT^xy^XSjzYhCk( zIGNTZBBQQ8!lE~mR*QUy_mmjz&zhlSUGK}P>3a55+GST{)YW#?^00a{LxjlFI)@^r zeNbKf!6a?;w)k%C&N^SL??zig?dp23iWq&juIsRS+z&VkI2Tyn6}#&QJgFi^0kE@< zC_zx72K9ymegi;%-HWJ=tFwuvI7I+hoOpHz2`NschZ6K)080sa2oh3ClpgHp!2lLJ zdI%Cy?9_uu9a$i=f|1x_lIYA7>kC1tr-sZ&c! zwA8MqisyfcY8csEVOT1c6_kO+6^5A`35S@s4Ghir>eYKU34T>PLtYvF7lUCGi1$J~a@iXg2SjaY z*{;%Ed&=~{p0v}0T#Kg@lRbLWPs>8(J!N}#yuVwkjGoFuJ%W5^G^s<>&x3!;AmCVw z;I$sT>xzj#=JW7GASrbfkNNz;0HUs|IC#AFv-`pWn##L6_Z%FIxh_7VaV+YR`Bj?PfDXjien zTJUo*jtXAR6~Mcw)kW_0a0c8!i`*(2#v0cbty6J?KGwR|Yjr@li$8tl!DPJS@qAt;Yw*Jc=pPY znhiG3%6Lbr_;3im$zG-r;MptJ=oEN9ElbQ#nq;u7RRxohCt&ht}f?XhO1`=ZVtF}zE#m#AVR_bD7U~p0N>l$T?YIt>P#Yu|i7#q-t z6D8R04Wz9bF5L~f<};KZ0aP<)hyOR=SpZIz40sDr2G|ccNiBkMb3!UugnMuRSlpK} zfxI6p4dlmVrN<<=6yVYT76C3n|5&M!mk5NDRW3#96$OLgRrj;IiJUjuON>^I+bqH(y5|#RMnyBHol= z%v<)RZNZ7c70t2{E}_dv?k6rc;QP6a3DgXMc;#G|K-!mZM*%)@dIA6=rkp|fq0Xu8 zz<99$7);;Uv?v4KpYRKuQ=_wQZHkX2v>&%ocM{a>a=o!3N>(f@-KkNs6Q>)s?)4~{Hnv

0B9|Fykqz? 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 literal 42258 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 Nj+TKYL;Zx${{UsNfbjqT literal 59405 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 Z2ko!;xzSR3tdwr_YaNHt@7!vC^Z!;C1fT!_ literal 176482 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 NzO~~v{;XF1{{da%mlFU0 diff --git a/plugins/channelrx/demodadsb/readme.md b/plugins/channelrx/demodadsb/readme.md index 48cc25afb..8237c0d2f 100644 --- a/plugins/channelrx/demodadsb/readme.md +++ b/plugins/channelrx/demodadsb/readme.md @@ -2,6 +2,8 @@

Introduction

+The top and bottom bars of the device window are described [here](../../../sdrgui/device/readme.md) + The ADS-B demodulator plugin can be used to receive and display ADS-B aircraft information. This is information about an aircraft, such as position, altitude, heading and speed, broadcast by aircraft on 1090MHz, in the 1090ES (Extended Squitter) format. 1090ES frames have a chip rate of 2Mchip/s, so the baseband sample rate should be set to be greater than 2MSa/s. As well as displaying information received via ADS-B, the plugin can also combine information from a number of databases to display more information about the aircraft and flight. diff --git a/plugins/samplemimo/bladerf2mimo/bladerf2mimogui.cpp b/plugins/samplemimo/bladerf2mimo/bladerf2mimogui.cpp index 6639a8b06..6621d25ac 100644 --- a/plugins/samplemimo/bladerf2mimo/bladerf2mimogui.cpp +++ b/plugins/samplemimo/bladerf2mimo/bladerf2mimogui.cpp @@ -29,7 +29,6 @@ #include "device/deviceuiset.h" #include "gui/colormapper.h" #include "gui/glspectrum.h" -#include "gui/crightclickenabler.h" #include "gui/basicdevicesettingsdialog.h" #include "dsp/dspengine.h" #include "dsp/dspdevicemimoengine.h" @@ -100,8 +99,7 @@ BladeRF2MIMOGui::BladeRF2MIMOGui(DeviceUISet *deviceUISet, QWidget* parent) : connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()), Qt::QueuedConnection); m_sampleMIMO->setMessageQueueToGUI(&m_inputMessageQueue); - CRightClickEnabler *startStopRightClickEnabler = new CRightClickEnabler(ui->startStopRx); - connect(startStopRightClickEnabler, SIGNAL(rightClick(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &))); + connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &))); sendSettings(); makeUIConnections(); @@ -794,21 +792,26 @@ void BladeRF2MIMOGui::updateStatus() void BladeRF2MIMOGui::openDeviceSettingsDialog(const QPoint& p) { - BasicDeviceSettingsDialog dialog(this); - dialog.setUseReverseAPI(m_settings.m_useReverseAPI); - dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); - dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); - dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); + if (m_contextMenuType == ContextMenuDeviceSettings) + { + BasicDeviceSettingsDialog dialog(this); + dialog.setUseReverseAPI(m_settings.m_useReverseAPI); + dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); + dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); + dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); - dialog.move(p); - dialog.exec(); + dialog.move(p); + dialog.exec(); - m_settings.m_useReverseAPI = dialog.useReverseAPI(); - m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); - m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); - m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); + m_settings.m_useReverseAPI = dialog.useReverseAPI(); + m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); + m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); + m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); - sendSettings(); + sendSettings(); + } + + resetContextMenuType(); } float BladeRF2MIMOGui::getGainDB(int gainValue, int gainMin, int gainMax, int gainStep, float gainScale) diff --git a/plugins/samplemimo/bladerf2mimo/readme.md b/plugins/samplemimo/bladerf2mimo/readme.md index 5425a54bb..f8dc2c303 100644 --- a/plugins/samplemimo/bladerf2mimo/readme.md +++ b/plugins/samplemimo/bladerf2mimo/readme.md @@ -19,6 +19,8 @@ The BladeRF Host library is also provided by many Linux distributions (check its

Interface

+The top and bottom bars of the device window are described [here](../../../sdrgui/device/readme.md) + ![BladeRF2 MIMO plugin GUI](../../../doc/img/BladeRF2MIMO_plugin.png)

1. Rx/Tx settings selection

diff --git a/plugins/samplemimo/limesdrmimo/limesdrmimogui.cpp b/plugins/samplemimo/limesdrmimo/limesdrmimogui.cpp index ceeb25951..0e94a7ec5 100644 --- a/plugins/samplemimo/limesdrmimo/limesdrmimogui.cpp +++ b/plugins/samplemimo/limesdrmimo/limesdrmimogui.cpp @@ -28,7 +28,6 @@ #include "device/deviceuiset.h" #include "gui/colormapper.h" #include "gui/glspectrum.h" -#include "gui/crightclickenabler.h" #include "gui/basicdevicesettingsdialog.h" #include "dsp/dspengine.h" #include "dsp/dspdevicemimoengine.h" @@ -100,8 +99,7 @@ LimeSDRMIMOGUI::LimeSDRMIMOGUI(DeviceUISet *deviceUISet, QWidget* parent) : connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()), Qt::QueuedConnection); m_limeSDRMIMO->setMessageQueueToGUI(&m_inputMessageQueue); - CRightClickEnabler *startStopRightClickEnabler = new CRightClickEnabler(ui->startStopRx); - connect(startStopRightClickEnabler, SIGNAL(rightClick(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &))); + connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &))); sendSettings(); makeUIConnections(); @@ -1126,21 +1124,26 @@ void LimeSDRMIMOGUI::on_antenna_currentIndexChanged(int index) void LimeSDRMIMOGUI::openDeviceSettingsDialog(const QPoint& p) { - BasicDeviceSettingsDialog dialog(this); - dialog.setUseReverseAPI(m_settings.m_useReverseAPI); - dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); - dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); - dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); + if (m_contextMenuType == ContextMenuDeviceSettings) + { + BasicDeviceSettingsDialog dialog(this); + dialog.setUseReverseAPI(m_settings.m_useReverseAPI); + dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); + dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); + dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); - dialog.move(p); - dialog.exec(); + dialog.move(p); + dialog.exec(); - m_settings.m_useReverseAPI = dialog.useReverseAPI(); - m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); - m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); - m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); + m_settings.m_useReverseAPI = dialog.useReverseAPI(); + m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); + m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); + m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); - sendSettings(); + sendSettings(); + } + + resetContextMenuType(); } void LimeSDRMIMOGUI::makeUIConnections() diff --git a/plugins/samplemimo/limesdrmimo/readme.md b/plugins/samplemimo/limesdrmimo/readme.md index dee0039c4..6e70b2046 100644 --- a/plugins/samplemimo/limesdrmimo/readme.md +++ b/plugins/samplemimo/limesdrmimo/readme.md @@ -10,6 +10,8 @@ This MIMO plugin sends and receives its samples to/from a [LimeSDR device](https

Interface

+The top and bottom bars of the device window are described [here](../../../sdrgui/device/readme.md) + ![LimeSDR MIMO plugin GUI](../../../doc/img/LimeSDRMIMO_plugin.png)

1. Rx/Tx settings selection

diff --git a/plugins/samplemimo/metismiso/metismisogui.cpp b/plugins/samplemimo/metismiso/metismisogui.cpp index 47b6b5b60..b7d5ce714 100644 --- a/plugins/samplemimo/metismiso/metismisogui.cpp +++ b/plugins/samplemimo/metismiso/metismisogui.cpp @@ -28,7 +28,6 @@ #include "device/deviceuiset.h" #include "gui/colormapper.h" #include "gui/glspectrum.h" -#include "gui/crightclickenabler.h" #include "gui/basicdevicesettingsdialog.h" #include "dsp/dspengine.h" #include "dsp/dspdevicemimoengine.h" @@ -73,8 +72,7 @@ MetisMISOGui::MetisMISOGui(DeviceUISet *deviceUISet, QWidget* parent) : connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()), Qt::QueuedConnection); m_sampleMIMO->setMessageQueueToGUI(&m_inputMessageQueue); - CRightClickEnabler *startStopRightClickEnabler = new CRightClickEnabler(ui->startStop); - connect(startStopRightClickEnabler, SIGNAL(rightClick(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &))); + connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &))); makeUIConnections(); } @@ -564,21 +562,26 @@ void MetisMISOGui::updateSubsamplingIndex() void MetisMISOGui::openDeviceSettingsDialog(const QPoint& p) { - BasicDeviceSettingsDialog dialog(this); - dialog.setUseReverseAPI(m_settings.m_useReverseAPI); - dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); - dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); - dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); + if (m_contextMenuType == ContextMenuDeviceSettings) + { + BasicDeviceSettingsDialog dialog(this); + dialog.setUseReverseAPI(m_settings.m_useReverseAPI); + dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); + dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); + dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); - dialog.move(p); - dialog.exec(); + dialog.move(p); + dialog.exec(); - m_settings.m_useReverseAPI = dialog.useReverseAPI(); - m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); - m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); - m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); + m_settings.m_useReverseAPI = dialog.useReverseAPI(); + m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); + m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); + m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); - sendSettings(); + sendSettings(); + } + + resetContextMenuType(); } void MetisMISOGui::makeUIConnections() diff --git a/plugins/samplemimo/metismiso/readme.md b/plugins/samplemimo/metismiso/readme.md index 02dced384..c8ca6f173 100644 --- a/plugins/samplemimo/metismiso/readme.md +++ b/plugins/samplemimo/metismiso/readme.md @@ -17,6 +17,8 @@ The plugin is present in the core of the software and thus is always present in

Interface

+The top and bottom bars of the device window are described [here](../../../sdrgui/device/readme.md) + ![Metis MISO plugin GUI](../../../doc/img/MetisMISO_plugin.png)

1: Active stream selection

diff --git a/plugins/samplemimo/plutosdrmimo/plutosdrmimogui.cpp b/plugins/samplemimo/plutosdrmimo/plutosdrmimogui.cpp index 412b34629..497e18a6e 100644 --- a/plugins/samplemimo/plutosdrmimo/plutosdrmimogui.cpp +++ b/plugins/samplemimo/plutosdrmimo/plutosdrmimogui.cpp @@ -27,7 +27,6 @@ #include "device/deviceuiset.h" #include "gui/colormapper.h" #include "gui/glspectrum.h" -#include "gui/crightclickenabler.h" #include "gui/basicdevicesettingsdialog.h" #include "dsp/dspengine.h" #include "dsp/dspdevicemimoengine.h" @@ -93,8 +92,7 @@ PlutoSDRMIMOGUI::PlutoSDRMIMOGUI(DeviceUISet *deviceUISet, QWidget* parent) : ui->swDecimLabel->setText(QString::fromUtf8("S\u2193")); ui->lpFIRDecimationLabel->setText(QString::fromUtf8("\u2193")); - CRightClickEnabler *startStopRightClickEnabler = new CRightClickEnabler(ui->startStopRx); - connect(startStopRightClickEnabler, SIGNAL(rightClick(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &))); + connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &))); blockApplySettings(true); displaySettings(); @@ -881,21 +879,26 @@ void PlutoSDRMIMOGUI::on_sampleRateMode_toggled(bool checked) void PlutoSDRMIMOGUI::openDeviceSettingsDialog(const QPoint& p) { - BasicDeviceSettingsDialog dialog(this); - dialog.setUseReverseAPI(m_settings.m_useReverseAPI); - dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); - dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); - dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); + if (m_contextMenuType == ContextMenuDeviceSettings) + { + BasicDeviceSettingsDialog dialog(this); + dialog.setUseReverseAPI(m_settings.m_useReverseAPI); + dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); + dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); + dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); - dialog.move(p); - dialog.exec(); + dialog.move(p); + dialog.exec(); - m_settings.m_useReverseAPI = dialog.useReverseAPI(); - m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); - m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); - m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); + m_settings.m_useReverseAPI = dialog.useReverseAPI(); + m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); + m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); + m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); - sendSettings(); + sendSettings(); + } + + resetContextMenuType(); } void PlutoSDRMIMOGUI::makeUIConnections() diff --git a/plugins/samplemimo/plutosdrmimo/readme.md b/plugins/samplemimo/plutosdrmimo/readme.md index e571f1151..e30bfd53d 100644 --- a/plugins/samplemimo/plutosdrmimo/readme.md +++ b/plugins/samplemimo/plutosdrmimo/readme.md @@ -36,6 +36,8 @@ Then add the following defines on `cmake` command line when compiling SDRangel:

Interface

+The top and bottom bars of the device window are described [here](../../../sdrgui/device/readme.md) + ![PlutoSDR MIMO plugin GUI](../../../doc/img/PlutoSDRMIMO_plugin.png)

1. Rx/Tx settings selection

diff --git a/plugins/samplemimo/testmi/readme.md b/plugins/samplemimo/testmi/readme.md index 1f66d548e..639506a4a 100644 --- a/plugins/samplemimo/testmi/readme.md +++ b/plugins/samplemimo/testmi/readme.md @@ -10,6 +10,8 @@ The plugin is present in the core of the software and thus is always present in

Interface

+The top and bottom bars of the device window are described [here](../../../sdrgui/device/readme.md) + ![Test source input plugin GUI](../../../doc/img/TestSourceInput_plugin.png)

1: Common stream parameters

diff --git a/plugins/samplemimo/testmi/testmigui.cpp b/plugins/samplemimo/testmi/testmigui.cpp index 173e71eb5..061305401 100644 --- a/plugins/samplemimo/testmi/testmigui.cpp +++ b/plugins/samplemimo/testmi/testmigui.cpp @@ -28,7 +28,6 @@ #include "device/deviceuiset.h" #include "gui/colormapper.h" #include "gui/glspectrum.h" -#include "gui/crightclickenabler.h" #include "gui/basicdevicesettingsdialog.h" #include "dsp/dspengine.h" #include "dsp/dspdevicemimoengine.h" @@ -85,8 +84,7 @@ TestMIGui::TestMIGui(DeviceUISet *deviceUISet, QWidget* parent) : connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()), Qt::QueuedConnection); m_sampleMIMO->setMessageQueueToGUI(&m_inputMessageQueue); - CRightClickEnabler *startStopRightClickEnabler = new CRightClickEnabler(ui->startStop); - connect(startStopRightClickEnabler, SIGNAL(rightClick(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &))); + connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &))); makeUIConnections(); } @@ -551,21 +549,26 @@ void TestMIGui::updateSampleRateAndFrequency() void TestMIGui::openDeviceSettingsDialog(const QPoint& p) { - BasicDeviceSettingsDialog dialog(this); - dialog.setUseReverseAPI(m_settings.m_useReverseAPI); - dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); - dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); - dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); + if (m_contextMenuType == ContextMenuDeviceSettings) + { + BasicDeviceSettingsDialog dialog(this); + dialog.setUseReverseAPI(m_settings.m_useReverseAPI); + dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); + dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); + dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); - dialog.move(p); - dialog.exec(); + dialog.move(p); + dialog.exec(); - m_settings.m_useReverseAPI = dialog.useReverseAPI(); - m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); - m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); - m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); + m_settings.m_useReverseAPI = dialog.useReverseAPI(); + m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); + m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); + m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); - sendSettings(); + sendSettings(); + } + + resetContextMenuType(); } void TestMIGui::makeUIConnections() diff --git a/plugins/samplemimo/testmi/testmisettings.h b/plugins/samplemimo/testmi/testmisettings.h index 95898b1ac..063b9ba19 100644 --- a/plugins/samplemimo/testmi/testmisettings.h +++ b/plugins/samplemimo/testmi/testmisettings.h @@ -65,7 +65,8 @@ struct TestMIStreamSettings { void resetToDefaults(); }; -struct TestMISettings { +struct TestMISettings +{ bool m_useReverseAPI; QString m_reverseAPIAddress; uint16_t m_reverseAPIPort; diff --git a/plugins/samplemimo/testmosync/readme.md b/plugins/samplemimo/testmosync/readme.md index 8c053be4d..e805cc17b 100644 --- a/plugins/samplemimo/testmosync/readme.md +++ b/plugins/samplemimo/testmosync/readme.md @@ -10,6 +10,8 @@ The plugin is always built.

Interface

+The top and bottom bars of the device window are described [here](../../../sdrgui/device/readme.md) + ![Test MO sync plugin GUI](../../../doc/img/TestMOSync_plugin.png)

1: Start/Stop

diff --git a/plugins/samplemimo/testmosync/testmosyncgui.cpp b/plugins/samplemimo/testmosync/testmosyncgui.cpp index 845844ac8..c4e3f45aa 100644 --- a/plugins/samplemimo/testmosync/testmosyncgui.cpp +++ b/plugins/samplemimo/testmosync/testmosyncgui.cpp @@ -24,6 +24,7 @@ #include "plugin/pluginapi.h" #include "gui/colormapper.h" #include "gui/glspectrum.h" +#include "gui/basicdevicesettingsdialog.h" #include "dsp/dspengine.h" #include "dsp/dspcommands.h" #include "dsp/spectrumvis.h" @@ -83,6 +84,7 @@ TestMOSyncGui::TestMOSyncGui(DeviceUISet *deviceUISet, QWidget* parent) : m_deviceUISet->m_deviceAPI->setSpectrumSinkInput(false, 0); m_deviceUISet->setSpectrumScalingFactor(SDR_TX_SCALEF); + connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &))); } TestMOSyncGui::~TestMOSyncGui() @@ -289,6 +291,30 @@ void TestMOSyncGui::tick() { } +void TestMOSyncGui::openDeviceSettingsDialog(const QPoint& p) +{ + if (m_contextMenuType == ContextMenuDeviceSettings) + { + BasicDeviceSettingsDialog dialog(this); + dialog.setUseReverseAPI(m_settings.m_useReverseAPI); + dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); + dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); + dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); + + dialog.move(p); + dialog.exec(); + + m_settings.m_useReverseAPI = dialog.useReverseAPI(); + m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); + m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); + m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); + + sendSettings(); + } + + resetContextMenuType(); +} + void TestMOSyncGui::makeUIConnections() { QObject::connect(ui->centerFrequency, &ValueDial::changed, this, &TestMOSyncGui::on_centerFrequency_changed); diff --git a/plugins/samplemimo/testmosync/testmosyncgui.h b/plugins/samplemimo/testmosync/testmosyncgui.h index 13a509d67..be24d06e1 100644 --- a/plugins/samplemimo/testmosync/testmosyncgui.h +++ b/plugins/samplemimo/testmosync/testmosyncgui.h @@ -85,6 +85,7 @@ private slots: void on_startStop_toggled(bool checked); void on_interp_currentIndexChanged(int index); void on_spectrumIndex_currentIndexChanged(int index); + void openDeviceSettingsDialog(const QPoint& p); void updateHardware(); void updateStatus(); void tick(); diff --git a/plugins/samplemimo/testmosync/testmosyncsettings.cpp b/plugins/samplemimo/testmosync/testmosyncsettings.cpp index ca026deec..5b53cdcf7 100644 --- a/plugins/samplemimo/testmosync/testmosyncsettings.cpp +++ b/plugins/samplemimo/testmosync/testmosyncsettings.cpp @@ -32,6 +32,10 @@ void TestMOSyncSettings::resetToDefaults() m_log2Interp = 0; m_fcPosTx = FC_POS_CENTER; m_workspaceIndex = 0; + m_useReverseAPI = false; + m_reverseAPIAddress = "127.0.0.1"; + m_reverseAPIPort = 8888; + m_reverseAPIDeviceIndex = 0; } QByteArray TestMOSyncSettings::serialize() const @@ -43,6 +47,10 @@ QByteArray TestMOSyncSettings::serialize() const s.writeS32(3, (int) m_fcPosTx); s.writeS32(4, m_workspaceIndex); s.writeBlob(5, m_geometryBytes); + s.writeBool(6, m_useReverseAPI); + s.writeString(7, m_reverseAPIAddress); + s.writeU32(8, m_reverseAPIPort); + s.writeU32(9, m_reverseAPIDeviceIndex); return s.final(); } @@ -60,6 +68,7 @@ bool TestMOSyncSettings::deserialize(const QByteArray& data) if (d.getVersion() == 1) { int intval; + uint32_t utmp; d.readU64(1, &m_sampleRate, 48000); d.readU32(2, &m_log2Interp, 0); @@ -67,6 +76,18 @@ bool TestMOSyncSettings::deserialize(const QByteArray& data) m_fcPosTx = (fcPos_t) intval; d.readS32(4, &m_workspaceIndex, 0); d.readBlob(5, &m_geometryBytes); + d.readBool(1, &m_useReverseAPI, false); + d.readString(2, &m_reverseAPIAddress, "127.0.0.1"); + d.readU32(3, &utmp, 0); + + if ((utmp > 1023) && (utmp < 65535)) { + m_reverseAPIPort = utmp; + } else { + m_reverseAPIPort = 8888; + } + + d.readU32(4, &utmp, 0); + m_reverseAPIDeviceIndex = utmp > 99 ? 99 : utmp; return true; } diff --git a/plugins/samplemimo/testmosync/testmosyncsettings.h b/plugins/samplemimo/testmosync/testmosyncsettings.h index 23976fb85..8743979a7 100644 --- a/plugins/samplemimo/testmosync/testmosyncsettings.h +++ b/plugins/samplemimo/testmosync/testmosyncsettings.h @@ -31,6 +31,10 @@ struct TestMOSyncSettings { quint64 m_sampleRate; quint32 m_log2Interp; fcPos_t m_fcPosTx; + bool m_useReverseAPI; + QString m_reverseAPIAddress; + uint16_t m_reverseAPIPort; + uint16_t m_reverseAPIDeviceIndex; int m_workspaceIndex; QByteArray m_geometryBytes; diff --git a/plugins/samplemimo/xtrxmimo/readme.md b/plugins/samplemimo/xtrxmimo/readme.md index 9041318c8..9037dac77 100644 --- a/plugins/samplemimo/xtrxmimo/readme.md +++ b/plugins/samplemimo/xtrxmimo/readme.md @@ -40,6 +40,8 @@ For a group the syntax is the same but the group name is prefixed with `@` like:

Interface

+The top and bottom bars of the device window are described [here](../../../sdrgui/device/readme.md) + ![XTRX MIMO plugin GUI](../../../doc/img/XTRXMIMO_plugin.png)

1. Rx/Tx settings selection

diff --git a/plugins/samplemimo/xtrxmimo/xtrxmimogui.cpp b/plugins/samplemimo/xtrxmimo/xtrxmimogui.cpp index b054ff5df..dea49ea2d 100644 --- a/plugins/samplemimo/xtrxmimo/xtrxmimogui.cpp +++ b/plugins/samplemimo/xtrxmimo/xtrxmimogui.cpp @@ -26,7 +26,6 @@ #include "device/deviceuiset.h" #include "gui/colormapper.h" #include "gui/glspectrum.h" -#include "gui/crightclickenabler.h" #include "gui/basicdevicesettingsdialog.h" #include "dsp/dspengine.h" #include "dsp/dspdevicemimoengine.h" @@ -97,8 +96,7 @@ XTRXMIMOGUI::XTRXMIMOGUI(DeviceUISet *deviceUISet, QWidget* parent) : connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()), Qt::QueuedConnection); m_xtrxMIMO->setMessageQueueToGUI(&m_inputMessageQueue); - CRightClickEnabler *startStopRightClickEnabler = new CRightClickEnabler(ui->startStopRx); - connect(startStopRightClickEnabler, SIGNAL(rightClick(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &))); + connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &))); sendSettings(); makeUIConnections(); @@ -1002,21 +1000,26 @@ void XTRXMIMOGUI::on_antenna_currentIndexChanged(int index) void XTRXMIMOGUI::openDeviceSettingsDialog(const QPoint& p) { - BasicDeviceSettingsDialog dialog(this); - dialog.setUseReverseAPI(m_settings.m_useReverseAPI); - dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); - dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); - dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); + if (m_contextMenuType == ContextMenuDeviceSettings) + { + BasicDeviceSettingsDialog dialog(this); + dialog.setUseReverseAPI(m_settings.m_useReverseAPI); + dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); + dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); + dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); - dialog.move(p); - dialog.exec(); + dialog.move(p); + dialog.exec(); - m_settings.m_useReverseAPI = dialog.useReverseAPI(); - m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); - m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); - m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); + m_settings.m_useReverseAPI = dialog.useReverseAPI(); + m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); + m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); + m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); - sendSettings(); + sendSettings(); + } + + resetContextMenuType(); } void XTRXMIMOGUI::makeUIConnections() diff --git a/plugins/samplesink/audiooutput/audiooutputgui.cpp b/plugins/samplesink/audiooutput/audiooutputgui.cpp index d298dfe39..a9664a96b 100644 --- a/plugins/samplesink/audiooutput/audiooutputgui.cpp +++ b/plugins/samplesink/audiooutput/audiooutputgui.cpp @@ -21,7 +21,6 @@ #include "ui_audiooutputgui.h" #include "gui/colormapper.h" #include "gui/glspectrum.h" -#include "gui/crightclickenabler.h" #include "gui/basicdevicesettingsdialog.h" #include "gui/audioselectdialog.h" #include "dsp/dspengine.h" @@ -49,9 +48,7 @@ AudioOutputGui::AudioOutputGui(DeviceUISet *deviceUISet, QWidget* parent) : m_helpURL = "plugins/samplesink/audiooutput/readme.md"; connect(&m_updateTimer, SIGNAL(timeout()), this, SLOT(updateHardware())); - - CRightClickEnabler *startStopRightClickEnabler = new CRightClickEnabler(ui->startStop); - connect(startStopRightClickEnabler, SIGNAL(rightClick(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &))); + connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &))); m_sampleRate = m_audioOutput->getSampleRate(); m_centerFrequency = m_audioOutput->getCenterFrequency(); @@ -227,21 +224,26 @@ void AudioOutputGui::updateHardware() void AudioOutputGui::openDeviceSettingsDialog(const QPoint& p) { - BasicDeviceSettingsDialog dialog(this); - dialog.setUseReverseAPI(m_settings.m_useReverseAPI); - dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); - dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); - dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); + if (m_contextMenuType == ContextMenuDeviceSettings) + { + BasicDeviceSettingsDialog dialog(this); + dialog.setUseReverseAPI(m_settings.m_useReverseAPI); + dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); + dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); + dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); - dialog.move(p); - dialog.exec(); + dialog.move(p); + dialog.exec(); - m_settings.m_useReverseAPI = dialog.useReverseAPI(); - m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); - m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); - m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); + m_settings.m_useReverseAPI = dialog.useReverseAPI(); + m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); + m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); + m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); - sendSettings(); + sendSettings(); + } + + resetContextMenuType(); } void AudioOutputGui::makeUIConnections() diff --git a/plugins/samplesink/audiooutput/readme.md b/plugins/samplesink/audiooutput/readme.md index 7275c48a9..dacf5d877 100644 --- a/plugins/samplesink/audiooutput/readme.md +++ b/plugins/samplesink/audiooutput/readme.md @@ -6,6 +6,8 @@ This output plugin sends its samples to an audio device.

Interface

+The top and bottom bars of the device window are described [here](../../../sdrgui/device/readme.md) + ![Audio output plugin GUI](../../../doc/img/AudioOutput_plugin.png)

1: Start/Stop

diff --git a/plugins/samplesink/bladerf1output/bladerf1outputgui.cpp b/plugins/samplesink/bladerf1output/bladerf1outputgui.cpp index b8766bb00..fa5a967e0 100644 --- a/plugins/samplesink/bladerf1output/bladerf1outputgui.cpp +++ b/plugins/samplesink/bladerf1output/bladerf1outputgui.cpp @@ -23,7 +23,6 @@ #include "ui_bladerf1outputgui.h" #include "gui/colormapper.h" #include "gui/glspectrum.h" -#include "gui/crightclickenabler.h" #include "gui/basicdevicesettingsdialog.h" #include "dsp/dspengine.h" #include "dsp/dspcommands.h" @@ -66,8 +65,7 @@ Bladerf1OutputGui::Bladerf1OutputGui(DeviceUISet *deviceUISet, QWidget* parent) connect(&m_statusTimer, SIGNAL(timeout()), this, SLOT(updateStatus())); m_statusTimer.start(500); - CRightClickEnabler *startStopRightClickEnabler = new CRightClickEnabler(ui->startStop); - connect(startStopRightClickEnabler, SIGNAL(rightClick(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &))); + connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &))); displaySettings(); makeUIConnections(); @@ -448,21 +446,26 @@ unsigned int Bladerf1OutputGui::getXb200Index(bool xb_200, bladerf_xb200_path xb void Bladerf1OutputGui::openDeviceSettingsDialog(const QPoint& p) { - BasicDeviceSettingsDialog dialog(this); - dialog.setUseReverseAPI(m_settings.m_useReverseAPI); - dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); - dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); - dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); + if (m_contextMenuType == ContextMenuDeviceSettings) + { + BasicDeviceSettingsDialog dialog(this); + dialog.setUseReverseAPI(m_settings.m_useReverseAPI); + dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); + dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); + dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); - dialog.move(p); - dialog.exec(); + dialog.move(p); + dialog.exec(); - m_settings.m_useReverseAPI = dialog.useReverseAPI(); - m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); - m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); - m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); + m_settings.m_useReverseAPI = dialog.useReverseAPI(); + m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); + m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); + m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); - sendSettings(); + sendSettings(); + } + + resetContextMenuType(); } void Bladerf1OutputGui::makeUIConnections() diff --git a/plugins/samplesink/bladerf1output/readme.md b/plugins/samplesink/bladerf1output/readme.md index 9617d8ca5..8de72b15f 100644 --- a/plugins/samplesink/bladerf1output/readme.md +++ b/plugins/samplesink/bladerf1output/readme.md @@ -37,6 +37,8 @@ The BladeRF Host library is also provided by many Linux distributions (check its

Interface

+The top and bottom bars of the device window are described [here](../../../sdrgui/device/readme.md) + ![BladeRF1 output plugin GUI](../../../doc/img/BladeRF1Output_plugin.png)

1: Start/Stop

diff --git a/plugins/samplesink/bladerf2output/bladerf2outputgui.cpp b/plugins/samplesink/bladerf2output/bladerf2outputgui.cpp index fd40138d2..7e82197d2 100644 --- a/plugins/samplesink/bladerf2output/bladerf2outputgui.cpp +++ b/plugins/samplesink/bladerf2output/bladerf2outputgui.cpp @@ -23,7 +23,6 @@ #include "ui_bladerf2outputgui.h" #include "gui/colormapper.h" #include "gui/glspectrum.h" -#include "gui/crightclickenabler.h" #include "gui/basicdevicesettingsdialog.h" #include "dsp/dspengine.h" #include "dsp/dspcommands.h" @@ -80,8 +79,7 @@ BladeRF2OutputGui::BladeRF2OutputGui(DeviceUISet *deviceUISet, QWidget* parent) connect(&m_statusTimer, SIGNAL(timeout()), this, SLOT(updateStatus())); m_statusTimer.start(500); - CRightClickEnabler *startStopRightClickEnabler = new CRightClickEnabler(ui->startStop); - connect(startStopRightClickEnabler, SIGNAL(rightClick(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &))); + connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &))); displaySettings(); makeUIConnections(); @@ -426,21 +424,26 @@ void BladeRF2OutputGui::updateStatus() void BladeRF2OutputGui::openDeviceSettingsDialog(const QPoint& p) { - BasicDeviceSettingsDialog dialog(this); - dialog.setUseReverseAPI(m_settings.m_useReverseAPI); - dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); - dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); - dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); + if (m_contextMenuType == ContextMenuDeviceSettings) + { + BasicDeviceSettingsDialog dialog(this); + dialog.setUseReverseAPI(m_settings.m_useReverseAPI); + dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); + dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); + dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); - dialog.move(p); - dialog.exec(); + dialog.move(p); + dialog.exec(); - m_settings.m_useReverseAPI = dialog.useReverseAPI(); - m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); - m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); - m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); + m_settings.m_useReverseAPI = dialog.useReverseAPI(); + m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); + m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); + m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); - sendSettings(); + sendSettings(); + } + + resetContextMenuType(); } float BladeRF2OutputGui::getGainDB(int gainValue) diff --git a/plugins/samplesink/bladerf2output/readme.md b/plugins/samplesink/bladerf2output/readme.md index 661dfa507..5288003dd 100644 --- a/plugins/samplesink/bladerf2output/readme.md +++ b/plugins/samplesink/bladerf2output/readme.md @@ -14,6 +14,8 @@ The BladeRF Host library is also provided by many Linux distributions (check its

Interface

+The top and bottom bars of the device window are described [here](../../../sdrgui/device/readme.md) + ![BladeRF2 output plugin GUI](../../../doc/img/BladeRF2Output_plugin.png)

1: Start/Stop

diff --git a/plugins/samplesink/fileoutput/fileoutputgui.cpp b/plugins/samplesink/fileoutput/fileoutputgui.cpp index 918944a15..dfdef4781 100644 --- a/plugins/samplesink/fileoutput/fileoutputgui.cpp +++ b/plugins/samplesink/fileoutput/fileoutputgui.cpp @@ -27,6 +27,7 @@ #include "plugin/pluginapi.h" #include "gui/colormapper.h" #include "gui/glspectrum.h" +#include "gui/basicdevicesettingsdialog.h" #include "dsp/dspengine.h" #include "dsp/dspcommands.h" @@ -76,6 +77,7 @@ FileOutputGui::FileOutputGui(DeviceUISet *deviceUISet, QWidget* parent) : m_deviceSampleSink = (FileOutput*) m_deviceUISet->m_deviceAPI->getSampleSink(); connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()), Qt::QueuedConnection); + connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &))); } FileOutputGui::~FileOutputGui() @@ -323,6 +325,30 @@ void FileOutputGui::tick() } } +void FileOutputGui::openDeviceSettingsDialog(const QPoint& p) +{ + if (m_contextMenuType == ContextMenuDeviceSettings) + { + BasicDeviceSettingsDialog dialog(this); + dialog.setUseReverseAPI(m_settings.m_useReverseAPI); + dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); + dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); + dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); + + dialog.move(p); + dialog.exec(); + + m_settings.m_useReverseAPI = dialog.useReverseAPI(); + m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); + m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); + m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); + + sendSettings(); + } + + resetContextMenuType(); +} + void FileOutputGui::makeUIConnections() { QObject::connect(ui->centerFrequency, &ValueDial::changed, this, &FileOutputGui::on_centerFrequency_changed); diff --git a/plugins/samplesink/fileoutput/fileoutputgui.h b/plugins/samplesink/fileoutput/fileoutputgui.h index ef3445595..a95c25b92 100644 --- a/plugins/samplesink/fileoutput/fileoutputgui.h +++ b/plugins/samplesink/fileoutput/fileoutputgui.h @@ -89,6 +89,7 @@ private slots: void on_startStop_toggled(bool checked); void on_showFileDialog_clicked(bool checked); void on_interp_currentIndexChanged(int index); + void openDeviceSettingsDialog(const QPoint& p); void updateHardware(); void updateStatus(); void tick(); diff --git a/plugins/samplesink/fileoutput/readme.md b/plugins/samplesink/fileoutput/readme.md index a54dcdf63..6ba050a36 100644 --- a/plugins/samplesink/fileoutput/readme.md +++ b/plugins/samplesink/fileoutput/readme.md @@ -18,6 +18,8 @@ The plugin is always built.

Interface

+The top and bottom bars of the device window are described [here](../../../sdrgui/device/readme.md) + ![File output plugin GUI](../../../doc/img/FileOutput_plugin.png)

1: Start/Stop

diff --git a/plugins/samplesink/hackrfoutput/hackrfoutputgui.cpp b/plugins/samplesink/hackrfoutput/hackrfoutputgui.cpp index 03e613cd3..e48eba340 100644 --- a/plugins/samplesink/hackrfoutput/hackrfoutputgui.cpp +++ b/plugins/samplesink/hackrfoutput/hackrfoutputgui.cpp @@ -22,7 +22,6 @@ #include "gui/colormapper.h" #include "gui/glspectrum.h" -#include "gui/crightclickenabler.h" #include "gui/basicdevicesettingsdialog.h" #include "dsp/dspengine.h" #include "dsp/dspcommands.h" @@ -62,8 +61,7 @@ HackRFOutputGui::HackRFOutputGui(DeviceUISet *deviceUISet, QWidget* parent) : connect(&m_statusTimer, SIGNAL(timeout()), this, SLOT(updateStatus())); m_statusTimer.start(500); - CRightClickEnabler *startStopRightClickEnabler = new CRightClickEnabler(ui->startStop); - connect(startStopRightClickEnabler, SIGNAL(rightClick(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &))); + connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &))); displaySettings(); displayBandwidths(); @@ -443,21 +441,26 @@ void HackRFOutputGui::updateStatus() void HackRFOutputGui::openDeviceSettingsDialog(const QPoint& p) { - BasicDeviceSettingsDialog dialog(this); - dialog.setUseReverseAPI(m_settings.m_useReverseAPI); - dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); - dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); - dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); + if (m_contextMenuType == ContextMenuDeviceSettings) + { + BasicDeviceSettingsDialog dialog(this); + dialog.setUseReverseAPI(m_settings.m_useReverseAPI); + dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); + dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); + dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); - dialog.move(p); - dialog.exec(); + dialog.move(p); + dialog.exec(); - m_settings.m_useReverseAPI = dialog.useReverseAPI(); - m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); - m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); - m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); + m_settings.m_useReverseAPI = dialog.useReverseAPI(); + m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); + m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); + m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); - sendSettings(); + sendSettings(); + } + + resetContextMenuType(); } void HackRFOutputGui::makeUIConnections() diff --git a/plugins/samplesink/hackrfoutput/readme.md b/plugins/samplesink/hackrfoutput/readme.md index 512e18488..898e7b3e0 100644 --- a/plugins/samplesink/hackrfoutput/readme.md +++ b/plugins/samplesink/hackrfoutput/readme.md @@ -6,6 +6,8 @@ This output sample sink plugin sends its samples to a [HackRF device](https://gr

Interface

+The top and bottom bars of the device window are described [here](../../../sdrgui/device/readme.md) + ![HackRF output plugin GUI](../../../doc/img/HackRFOutput_plugin.png)

1: Start/Stop

@@ -111,4 +113,4 @@ When a device set for the same physical device is present the device center freq When the center frequency position Fc (control 9) is set to center (Cen) in both Tx and Rx the actual frequency of reception and transmission are the same. -In other cases for both frequencies to match you have to set the same sample rate and Fc position (either Inf or Sup) in the Tx and Rx. \ No newline at end of file +In other cases for both frequencies to match you have to set the same sample rate and Fc position (either Inf or Sup) in the Tx and Rx. diff --git a/plugins/samplesink/limesdroutput/limesdroutputgui.cpp b/plugins/samplesink/limesdroutput/limesdroutputgui.cpp index c8e9f9777..158da7f39 100644 --- a/plugins/samplesink/limesdroutput/limesdroutputgui.cpp +++ b/plugins/samplesink/limesdroutput/limesdroutputgui.cpp @@ -21,7 +21,6 @@ #include "ui_limesdroutputgui.h" #include "gui/colormapper.h" #include "gui/glspectrum.h" -#include "gui/crightclickenabler.h" #include "gui/basicdevicesettingsdialog.h" #include "dsp/dspengine.h" #include "dsp/dspcommands.h" @@ -93,9 +92,7 @@ LimeSDROutputGUI::LimeSDROutputGUI(DeviceUISet *deviceUISet, QWidget* parent) : sprintf(recFileNameCStr, "test_%d.sdriq", m_deviceUISet->m_deviceAPI->getDeviceUID()); connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()), Qt::QueuedConnection); - - CRightClickEnabler *startStopRightClickEnabler = new CRightClickEnabler(ui->startStop); - connect(startStopRightClickEnabler, SIGNAL(rightClick(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &))); + connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &))); sendSettings(); makeUIConnections(); @@ -619,21 +616,26 @@ void LimeSDROutputGUI::on_sampleRateMode_toggled(bool checked) void LimeSDROutputGUI::openDeviceSettingsDialog(const QPoint& p) { - BasicDeviceSettingsDialog dialog(this); - dialog.setUseReverseAPI(m_settings.m_useReverseAPI); - dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); - dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); - dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); + if (m_contextMenuType == ContextMenuDeviceSettings) + { + BasicDeviceSettingsDialog dialog(this); + dialog.setUseReverseAPI(m_settings.m_useReverseAPI); + dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); + dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); + dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); - dialog.move(p); - dialog.exec(); + dialog.move(p); + dialog.exec(); - m_settings.m_useReverseAPI = dialog.useReverseAPI(); - m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); - m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); - m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); + m_settings.m_useReverseAPI = dialog.useReverseAPI(); + m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); + m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); + m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); - sendSettings(); + sendSettings(); + } + + resetContextMenuType(); } void LimeSDROutputGUI::makeUIConnections() diff --git a/plugins/samplesink/limesdroutput/readme.md b/plugins/samplesink/limesdroutput/readme.md index eecccd764..71dbc87e7 100644 --- a/plugins/samplesink/limesdroutput/readme.md +++ b/plugins/samplesink/limesdroutput/readme.md @@ -32,6 +32,8 @@ Then add the following defines on `cmake` command line:

Interface

+The top and bottom bars of the device window are described [here](../../../sdrgui/device/readme.md) + ![LimeSDR output plugin GUI](../../../doc/img/LimeSDROutput_plugin.png)

1: Start/Stop

diff --git a/plugins/samplesink/localoutput/localoutputgui.cpp b/plugins/samplesink/localoutput/localoutputgui.cpp index c85432967..22a1da49f 100644 --- a/plugins/samplesink/localoutput/localoutputgui.cpp +++ b/plugins/samplesink/localoutput/localoutputgui.cpp @@ -32,7 +32,6 @@ #include "ui_localoutputgui.h" #include "gui/colormapper.h" #include "gui/glspectrum.h" -#include "gui/crightclickenabler.h" #include "gui/basicdevicesettingsdialog.h" #include "dsp/dspengine.h" #include "dsp/dspcommands.h" @@ -67,8 +66,7 @@ LocalOutputGui::LocalOutputGui(DeviceUISet *deviceUISet, QWidget* parent) : .arg(palette().highlight().color().darker(115).name()))); m_helpURL = "plugins/samplesink/localoutput/readme.md"; - CRightClickEnabler *startStopRightClickEnabler = new CRightClickEnabler(ui->startStop); - connect(startStopRightClickEnabler, SIGNAL(rightClick(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &))); + connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &))); displaySettings(); @@ -282,21 +280,26 @@ void LocalOutputGui::updateStatus() void LocalOutputGui::openDeviceSettingsDialog(const QPoint& p) { - BasicDeviceSettingsDialog dialog(this); - dialog.setUseReverseAPI(m_settings.m_useReverseAPI); - dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); - dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); - dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); + if (m_contextMenuType == ContextMenuDeviceSettings) + { + BasicDeviceSettingsDialog dialog(this); + dialog.setUseReverseAPI(m_settings.m_useReverseAPI); + dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); + dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); + dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); - dialog.move(p); - dialog.exec(); + dialog.move(p); + dialog.exec(); - m_settings.m_useReverseAPI = dialog.useReverseAPI(); - m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); - m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); - m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); + m_settings.m_useReverseAPI = dialog.useReverseAPI(); + m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); + m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); + m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); - sendSettings(); + sendSettings(); + } + + resetContextMenuType(); } void LocalOutputGui::makeUIConnections() diff --git a/plugins/samplesink/localoutput/readme.md b/plugins/samplesink/localoutput/readme.md index fedd3fcfd..796ec7f2a 100644 --- a/plugins/samplesink/localoutput/readme.md +++ b/plugins/samplesink/localoutput/readme.md @@ -6,6 +6,8 @@ This output sample sink plugin sends its samples to a Local Source channel in an

Interface

+The top and bottom bars of the device window are described [here](../../../sdrgui/device/readme.md) + ![SDR Local output plugin GUI](../../../doc/img/LocalOutput_plugin.png)

1: Start/Stop

diff --git a/plugins/samplesink/plutosdroutput/plutosdroutputgui.cpp b/plugins/samplesink/plutosdroutput/plutosdroutputgui.cpp index a0aaf5368..edb24a8d7 100644 --- a/plugins/samplesink/plutosdroutput/plutosdroutputgui.cpp +++ b/plugins/samplesink/plutosdroutput/plutosdroutputgui.cpp @@ -22,7 +22,6 @@ #include "dsp/dspengine.h" #include "dsp/dspcommands.h" #include "gui/glspectrum.h" -#include "gui/crightclickenabler.h" #include "gui/basicdevicesettingsdialog.h" #include "device/deviceapi.h" #include "device/deviceuiset.h" @@ -70,8 +69,7 @@ PlutoSDROutputGUI::PlutoSDROutputGUI(DeviceUISet *deviceUISet, QWidget* parent) ui->swInterpLabel->setText(QString::fromUtf8("S\u2191")); ui->lpFIRInterpolationLabel->setText(QString::fromUtf8("\u2191")); - CRightClickEnabler *startStopRightClickEnabler = new CRightClickEnabler(ui->startStop); - connect(startStopRightClickEnabler, SIGNAL(rightClick(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &))); + connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &))); blockApplySettings(true); displaySettings(); @@ -482,21 +480,26 @@ void PlutoSDROutputGUI::updateSampleRateAndFrequency() void PlutoSDROutputGUI::openDeviceSettingsDialog(const QPoint& p) { - BasicDeviceSettingsDialog dialog(this); - dialog.setUseReverseAPI(m_settings.m_useReverseAPI); - dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); - dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); - dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); + if (m_contextMenuType == ContextMenuDeviceSettings) + { + BasicDeviceSettingsDialog dialog(this); + dialog.setUseReverseAPI(m_settings.m_useReverseAPI); + dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); + dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); + dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); - dialog.move(p); - dialog.exec(); + dialog.move(p); + dialog.exec(); - m_settings.m_useReverseAPI = dialog.useReverseAPI(); - m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); - m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); - m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); + m_settings.m_useReverseAPI = dialog.useReverseAPI(); + m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); + m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); + m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); - sendSettings(); + sendSettings(); + } + + resetContextMenuType(); } void PlutoSDROutputGUI::makeUIConnections() diff --git a/plugins/samplesink/plutosdroutput/readme.md b/plugins/samplesink/plutosdroutput/readme.md index fd2a6c223..6907cb500 100644 --- a/plugins/samplesink/plutosdroutput/readme.md +++ b/plugins/samplesink/plutosdroutput/readme.md @@ -27,6 +27,8 @@ Then add the following defines on `cmake` command line when compiling SDRangel:

Interface

+The top and bottom bars of the device window are described [here](../../../sdrgui/device/readme.md) + ![PlutoSDR output plugin GUI](../../../doc/img/PlutoSDROutput_plugin.png)

1: Common stream parameters

diff --git a/plugins/samplesink/remoteoutput/readme.md b/plugins/samplesink/remoteoutput/readme.md index 6f2d154c0..046b61d07 100644 --- a/plugins/samplesink/remoteoutput/readme.md +++ b/plugins/samplesink/remoteoutput/readme.md @@ -16,6 +16,8 @@ The plugin will be built only if the [CM256cc library](https://github.com/f4exb/

Interface

+The top and bottom bars of the device window are described [here](../../../sdrgui/device/readme.md) + ![SDR Remote output plugin GUI](../../../doc/img/RemoteOutput_plugin.png)

1: Start/Stop

diff --git a/plugins/samplesink/remoteoutput/remoteoutputgui.cpp b/plugins/samplesink/remoteoutput/remoteoutputgui.cpp index 65343e43f..6a384c80c 100644 --- a/plugins/samplesink/remoteoutput/remoteoutputgui.cpp +++ b/plugins/samplesink/remoteoutput/remoteoutputgui.cpp @@ -28,7 +28,6 @@ #include "plugin/pluginapi.h" #include "gui/colormapper.h" #include "gui/glspectrum.h" -#include "gui/crightclickenabler.h" #include "gui/basicdevicesettingsdialog.h" #include "dsp/dspengine.h" #include "dsp/dspcommands.h" @@ -87,8 +86,7 @@ RemoteOutputSinkGui::RemoteOutputSinkGui(DeviceUISet *deviceUISet, QWidget* pare displayEventCounts(); displayEventTimer(); - CRightClickEnabler *startStopRightClickEnabler = new CRightClickEnabler(ui->startStop); - connect(startStopRightClickEnabler, SIGNAL(rightClick(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &))); + connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &))); displaySettings(); sendSettings(); @@ -529,21 +527,26 @@ void RemoteOutputSinkGui::displayRemoteFixedData(const RemoteOutput::MsgReportRe void RemoteOutputSinkGui::openDeviceSettingsDialog(const QPoint& p) { - BasicDeviceSettingsDialog dialog(this); - dialog.setUseReverseAPI(m_settings.m_useReverseAPI); - dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); - dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); - dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); + if (m_contextMenuType == ContextMenuDeviceSettings) + { + BasicDeviceSettingsDialog dialog(this); + dialog.setUseReverseAPI(m_settings.m_useReverseAPI); + dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); + dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); + dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); - dialog.move(p); - dialog.exec(); + dialog.move(p); + dialog.exec(); - m_settings.m_useReverseAPI = dialog.useReverseAPI(); - m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); - m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); - m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); + m_settings.m_useReverseAPI = dialog.useReverseAPI(); + m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); + m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); + m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); - sendSettings(); + sendSettings(); + } + + resetContextMenuType(); } void RemoteOutputSinkGui::makeUIConnections() diff --git a/plugins/samplesink/soapysdroutput/readme.md b/plugins/samplesink/soapysdroutput/readme.md index 1956a740a..69e49d74a 100644 --- a/plugins/samplesink/soapysdroutput/readme.md +++ b/plugins/samplesink/soapysdroutput/readme.md @@ -65,6 +65,8 @@ When installed the Red Pitaya SoapySDR plugin lists a Red Pitaya device even if

Interface

+The top and bottom bars of the device window are described [here](../../../sdrgui/device/readme.md) + ![SoapySDR input plugin GUI](../../../doc/img/SoapySDROutput_plugin1.png) The top part described by number tags is common for all devices. The bottom part under the "A" tag depends on the SoapySDR device implementation. The corresponding widgets are stacked vertically inside a scrollable area as there may be many controls depending on how the device interface is implemented in SoapySDR. Move the slider on the right to see all parameters available. diff --git a/plugins/samplesink/soapysdroutput/soapysdroutputgui.cpp b/plugins/samplesink/soapysdroutput/soapysdroutputgui.cpp index fe7f7c800..d6525a0d6 100644 --- a/plugins/samplesink/soapysdroutput/soapysdroutputgui.cpp +++ b/plugins/samplesink/soapysdroutput/soapysdroutputgui.cpp @@ -25,7 +25,6 @@ #include "util/simpleserializer.h" #include "ui_soapysdroutputgui.h" #include "gui/glspectrum.h" -#include "gui/crightclickenabler.h" #include "gui/basicdevicesettingsdialog.h" #include "soapygui/discreterangegui.h" #include "soapygui/intervalrangegui.h" @@ -94,8 +93,7 @@ SoapySDROutputGui::SoapySDROutputGui(DeviceUISet *deviceUISet, QWidget* parent) connect(&m_statusTimer, SIGNAL(timeout()), this, SLOT(updateStatus())); m_statusTimer.start(500); - CRightClickEnabler *startStopRightClickEnabler = new CRightClickEnabler(ui->startStop); - connect(startStopRightClickEnabler, SIGNAL(rightClick(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &))); + connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &))); displaySettings(); @@ -872,21 +870,26 @@ void SoapySDROutputGui::updateStatus() void SoapySDROutputGui::openDeviceSettingsDialog(const QPoint& p) { - BasicDeviceSettingsDialog dialog(this); - dialog.setUseReverseAPI(m_settings.m_useReverseAPI); - dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); - dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); - dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); + if (m_contextMenuType == ContextMenuDeviceSettings) + { + BasicDeviceSettingsDialog dialog(this); + dialog.setUseReverseAPI(m_settings.m_useReverseAPI); + dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); + dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); + dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); - dialog.move(p); - dialog.exec(); + dialog.move(p); + dialog.exec(); - m_settings.m_useReverseAPI = dialog.useReverseAPI(); - m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); - m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); - m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); + m_settings.m_useReverseAPI = dialog.useReverseAPI(); + m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); + m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); + m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); - sendSettings(); + sendSettings(); + } + + resetContextMenuType(); } void SoapySDROutputGui::makeUIConnections() diff --git a/plugins/samplesink/testsink/readme.md b/plugins/samplesink/testsink/readme.md index cf3ceff0b..919202ace 100644 --- a/plugins/samplesink/testsink/readme.md +++ b/plugins/samplesink/testsink/readme.md @@ -10,6 +10,8 @@ The plugin is always built.

Interface

+The top and bottom bars of the device window are described [here](../../../sdrgui/device/readme.md) + ![Test sink plugin GUI](../../../doc/img/TestSink.png)

1: Start/Stop

@@ -40,4 +42,4 @@ Use the wheels to adjust the sample rate. Left click on a digit sets the cursor

6: Spectrum display

-This is the final output stream spectrum display after interpolation (4). This would be sent to the hardware device. Controls on the bottom of the panel are identical to the ones of the main spectrum display. \ No newline at end of file +This is the final output stream spectrum display after interpolation (4). This would be sent to the hardware device. Controls on the bottom of the panel are identical to the ones of the main spectrum display. diff --git a/plugins/samplesink/testsink/testsinkgui.cpp b/plugins/samplesink/testsink/testsinkgui.cpp index e63ba074c..08b6bcb59 100644 --- a/plugins/samplesink/testsink/testsinkgui.cpp +++ b/plugins/samplesink/testsink/testsinkgui.cpp @@ -25,6 +25,7 @@ #include "plugin/pluginapi.h" #include "gui/colormapper.h" #include "gui/glspectrum.h" +#include "gui/basicdevicesettingsdialog.h" #include "dsp/spectrumvis.h" #include "dsp/dspengine.h" #include "dsp/dspcommands.h" @@ -76,6 +77,7 @@ TestSinkGui::TestSinkGui(DeviceUISet *deviceUISet, QWidget* parent) : makeUIConnections(); connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()), Qt::QueuedConnection); + connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &))); } TestSinkGui::~TestSinkGui() @@ -263,6 +265,30 @@ void TestSinkGui::tick() { } +void TestSinkGui::openDeviceSettingsDialog(const QPoint& p) +{ + if (m_contextMenuType == ContextMenuDeviceSettings) + { + BasicDeviceSettingsDialog dialog(this); + dialog.setUseReverseAPI(m_settings.m_useReverseAPI); + dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); + dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); + dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); + + dialog.move(p); + dialog.exec(); + + m_settings.m_useReverseAPI = dialog.useReverseAPI(); + m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); + m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); + m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); + + sendSettings(); + } + + resetContextMenuType(); +} + void TestSinkGui::makeUIConnections() { QObject::connect(ui->centerFrequency, &ValueDial::changed, this, &TestSinkGui::on_centerFrequency_changed); diff --git a/plugins/samplesink/testsink/testsinkgui.h b/plugins/samplesink/testsink/testsinkgui.h index 255d7b1de..dc7566492 100644 --- a/plugins/samplesink/testsink/testsinkgui.h +++ b/plugins/samplesink/testsink/testsinkgui.h @@ -85,6 +85,7 @@ private slots: void on_sampleRate_changed(quint64 value); void on_startStop_toggled(bool checked); void on_interp_currentIndexChanged(int index); + void openDeviceSettingsDialog(const QPoint& p); void updateHardware(); void updateStatus(); void tick(); diff --git a/plugins/samplesink/testsink/testsinksettings.cpp b/plugins/samplesink/testsink/testsinksettings.cpp index ea14da772..ec32a5dbf 100644 --- a/plugins/samplesink/testsink/testsinksettings.cpp +++ b/plugins/samplesink/testsink/testsinksettings.cpp @@ -31,6 +31,10 @@ void TestSinkSettings::resetToDefaults() m_log2Interp = 0; m_spectrumGUI = nullptr; m_workspaceIndex = 0; + m_useReverseAPI = false; + m_reverseAPIAddress = "127.0.0.1"; + m_reverseAPIPort = 8888; + m_reverseAPIDeviceIndex = 0; } QByteArray TestSinkSettings::serialize() const @@ -47,7 +51,10 @@ QByteArray TestSinkSettings::serialize() const s.writeS32(5, m_workspaceIndex); s.writeBlob(6, m_geometryBytes); - + s.writeBool(7, m_useReverseAPI); + s.writeString(8, m_reverseAPIAddress); + s.writeU32(9, m_reverseAPIPort); + s.writeU32(10, m_reverseAPIDeviceIndex); return s.final(); } @@ -64,6 +71,7 @@ bool TestSinkSettings::deserialize(const QByteArray& data) if (d.getVersion() == 1) { QByteArray bytetmp; + uint32_t uintval; d.readU64(1, &m_sampleRate, 435000*1000); d.readU64(2, &m_sampleRate, 48000); @@ -77,6 +85,18 @@ bool TestSinkSettings::deserialize(const QByteArray& data) d.readS32(5, &m_workspaceIndex, 0); d.readBlob(6, &m_geometryBytes); + d.readBool(7, &m_useReverseAPI, false); + d.readString(8, &m_reverseAPIAddress, "127.0.0.1"); + d.readU32(9, &uintval, 0); + + if ((uintval > 1023) && (uintval < 65535)) { + m_reverseAPIPort = uintval; + } else { + m_reverseAPIPort = 8888; + } + + d.readU32(10, &uintval, 0); + m_reverseAPIDeviceIndex = uintval > 99 ? 99 : uintval; return true; } diff --git a/plugins/samplesink/testsink/testsinksettings.h b/plugins/samplesink/testsink/testsinksettings.h index 79e082488..6f6377ae6 100644 --- a/plugins/samplesink/testsink/testsinksettings.h +++ b/plugins/samplesink/testsink/testsinksettings.h @@ -29,6 +29,10 @@ struct TestSinkSettings { Serializable *m_spectrumGUI; int m_workspaceIndex; QByteArray m_geometryBytes; + bool m_useReverseAPI; + QString m_reverseAPIAddress; + uint16_t m_reverseAPIPort; + uint16_t m_reverseAPIDeviceIndex; TestSinkSettings(); void resetToDefaults(); diff --git a/plugins/samplesink/usrpoutput/readme.md b/plugins/samplesink/usrpoutput/readme.md index 3500ad751..130aec688 100644 --- a/plugins/samplesink/usrpoutput/readme.md +++ b/plugins/samplesink/usrpoutput/readme.md @@ -6,6 +6,8 @@ This output sample sink plugin sends its samples to a [USRP device](https://www.

Interface

+The top and bottom bars of the device window are described [here](../../../sdrgui/device/readme.md) + ![USRP output plugin GUI](../../../doc/img/USRPOutput_plugin.png)

1: Start/Stop

@@ -18,7 +20,7 @@ Device start / stop button.

2A: Sample rate

-This is the sample rate at which IQ samples are transfered from SDRangel to the device, in kS/s (k) or MS/s (M). +This is the sample rate at which IQ samples are transfered from SDRangel to the device, in kS/s (k) or MS/s (M).

2B: Stream sample rate

diff --git a/plugins/samplesink/usrpoutput/usrpoutputgui.cpp b/plugins/samplesink/usrpoutput/usrpoutputgui.cpp index 4271c44de..3b8ac6f6b 100644 --- a/plugins/samplesink/usrpoutput/usrpoutputgui.cpp +++ b/plugins/samplesink/usrpoutput/usrpoutputgui.cpp @@ -22,7 +22,6 @@ #include "ui_usrpoutputgui.h" #include "gui/colormapper.h" #include "gui/glspectrum.h" -#include "gui/crightclickenabler.h" #include "gui/basicdevicesettingsdialog.h" #include "dsp/dspengine.h" #include "dsp/dspcommands.h" @@ -86,9 +85,7 @@ USRPOutputGUI::USRPOutputGUI(DeviceUISet *deviceUISet, QWidget* parent) : sprintf(recFileNameCStr, "test_%d.sdriq", m_deviceUISet->m_deviceAPI->getDeviceUID()); connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()), Qt::QueuedConnection); - - CRightClickEnabler *startStopRightClickEnabler = new CRightClickEnabler(ui->startStop); - connect(startStopRightClickEnabler, SIGNAL(rightClick(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &))); + connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &))); sendSettings(); makeUIConnections(); @@ -563,21 +560,26 @@ void USRPOutputGUI::on_sampleRateMode_toggled(bool checked) void USRPOutputGUI::openDeviceSettingsDialog(const QPoint& p) { - BasicDeviceSettingsDialog dialog(this); - dialog.setUseReverseAPI(m_settings.m_useReverseAPI); - dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); - dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); - dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); + if (m_contextMenuType == ContextMenuDeviceSettings) + { + BasicDeviceSettingsDialog dialog(this); + dialog.setUseReverseAPI(m_settings.m_useReverseAPI); + dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); + dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); + dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); - dialog.move(p); - dialog.exec(); + dialog.move(p); + dialog.exec(); - m_settings.m_useReverseAPI = dialog.useReverseAPI(); - m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); - m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); - m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); + m_settings.m_useReverseAPI = dialog.useReverseAPI(); + m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); + m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); + m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); - sendSettings(); + sendSettings(); + } + + resetContextMenuType(); } void USRPOutputGUI::makeUIConnections() diff --git a/plugins/samplesink/xtrxoutput/readme.md b/plugins/samplesink/xtrxoutput/readme.md index c641f7d4a..5676003f5 100644 --- a/plugins/samplesink/xtrxoutput/readme.md +++ b/plugins/samplesink/xtrxoutput/readme.md @@ -40,9 +40,12 @@ For a group the syntax is the same but the group name is prefixed with `@` like: ``` @realtime - rtprio 99 @realtime - memlock unlimited +```

Interface

+The top and bottom bars of the device window are described [here](../../../sdrgui/device/readme.md) + ![XTRX output plugin GUI](../../../doc/img/XTRXOutput_plugin.png)

1: Start/Stop

diff --git a/plugins/samplesink/xtrxoutput/xtrxoutputgui.cpp b/plugins/samplesink/xtrxoutput/xtrxoutputgui.cpp index c20372447..b2bccc8a8 100644 --- a/plugins/samplesink/xtrxoutput/xtrxoutputgui.cpp +++ b/plugins/samplesink/xtrxoutput/xtrxoutputgui.cpp @@ -23,7 +23,6 @@ #include "ui_xtrxoutputgui.h" #include "gui/colormapper.h" #include "gui/glspectrum.h" -#include "gui/crightclickenabler.h" #include "gui/basicdevicesettingsdialog.h" #include "dsp/dspengine.h" #include "dsp/dspcommands.h" @@ -74,8 +73,7 @@ XTRXOutputGUI::XTRXOutputGUI(DeviceUISet *deviceUISet, QWidget* parent) : connect(&m_statusTimer, SIGNAL(timeout()), this, SLOT(updateStatus())); m_statusTimer.start(500); - CRightClickEnabler *startStopRightClickEnabler = new CRightClickEnabler(ui->startStop); - connect(startStopRightClickEnabler, SIGNAL(rightClick(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &))); + connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &))); displaySettings(); makeUIConnections(); @@ -554,21 +552,26 @@ void XTRXOutputGUI::on_sampleRateMode_toggled(bool checked) void XTRXOutputGUI::openDeviceSettingsDialog(const QPoint& p) { - BasicDeviceSettingsDialog dialog(this); - dialog.setUseReverseAPI(m_settings.m_useReverseAPI); - dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); - dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); - dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); + if (m_contextMenuType == ContextMenuDeviceSettings) + { + BasicDeviceSettingsDialog dialog(this); + dialog.setUseReverseAPI(m_settings.m_useReverseAPI); + dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); + dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); + dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); - dialog.move(p); - dialog.exec(); + dialog.move(p); + dialog.exec(); - m_settings.m_useReverseAPI = dialog.useReverseAPI(); - m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); - m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); - m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); + m_settings.m_useReverseAPI = dialog.useReverseAPI(); + m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); + m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); + m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); - sendSettings(); + sendSettings(); + } + + resetContextMenuType(); } void XTRXOutputGUI::makeUIConnections() diff --git a/plugins/samplesource/airspy/airspygui.cpp b/plugins/samplesource/airspy/airspygui.cpp index b0d8de826..951b14c0d 100644 --- a/plugins/samplesource/airspy/airspygui.cpp +++ b/plugins/samplesource/airspy/airspygui.cpp @@ -29,7 +29,6 @@ #include "ui_airspygui.h" #include "gui/colormapper.h" #include "gui/glspectrum.h" -#include "gui/crightclickenabler.h" #include "gui/basicdevicesettingsdialog.h" #include "dsp/dspengine.h" #include "dsp/dspcommands.h" @@ -57,8 +56,7 @@ AirspyGui::AirspyGui(DeviceUISet *deviceUISet, QWidget* parent) : connect(&m_statusTimer, SIGNAL(timeout()), this, SLOT(updateStatus())); m_statusTimer.start(500); - CRightClickEnabler *startStopRightClickEnabler = new CRightClickEnabler(ui->startStop); - connect(startStopRightClickEnabler, SIGNAL(rightClick(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &))); + connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &))); displaySettings(); @@ -435,21 +433,26 @@ int AirspyGui::getDevSampleRateIndex(uint32_t sampeRate) void AirspyGui::openDeviceSettingsDialog(const QPoint& p) { - BasicDeviceSettingsDialog dialog(this); - dialog.setUseReverseAPI(m_settings.m_useReverseAPI); - dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); - dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); - dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); + if (m_contextMenuType == ContextMenuDeviceSettings) + { + BasicDeviceSettingsDialog dialog(this); + dialog.setUseReverseAPI(m_settings.m_useReverseAPI); + dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); + dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); + dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); - dialog.move(p); - dialog.exec(); + dialog.move(p); + dialog.exec(); - m_settings.m_useReverseAPI = dialog.useReverseAPI(); - m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); - m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); - m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); + m_settings.m_useReverseAPI = dialog.useReverseAPI(); + m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); + m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); + m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); - sendSettings(); + sendSettings(); + } + + resetContextMenuType(); } void AirspyGui::makeUIConnections() diff --git a/plugins/samplesource/airspyhf/airspyhfgui.cpp b/plugins/samplesource/airspyhf/airspyhfgui.cpp index 3dcb22d42..c8831abaa 100644 --- a/plugins/samplesource/airspyhf/airspyhfgui.cpp +++ b/plugins/samplesource/airspyhf/airspyhfgui.cpp @@ -27,7 +27,6 @@ #include "ui_airspyhfgui.h" #include "gui/colormapper.h" #include "gui/glspectrum.h" -#include "gui/crightclickenabler.h" #include "gui/basicdevicesettingsdialog.h" #include "dsp/dspengine.h" #include "dsp/dspcommands.h" @@ -57,8 +56,7 @@ AirspyHFGui::AirspyHFGui(DeviceUISet *deviceUISet, QWidget* parent) : connect(&m_statusTimer, SIGNAL(timeout()), this, SLOT(updateStatus())); m_statusTimer.start(500); - CRightClickEnabler *startStopRightClickEnabler = new CRightClickEnabler(ui->startStop); - connect(startStopRightClickEnabler, SIGNAL(rightClick(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &))); + connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &))); displaySettings(); @@ -455,21 +453,26 @@ int AirspyHFGui::getDevSampleRateIndex(uint32_t sampeRate) void AirspyHFGui::openDeviceSettingsDialog(const QPoint& p) { - BasicDeviceSettingsDialog dialog(this); - dialog.setUseReverseAPI(m_settings.m_useReverseAPI); - dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); - dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); - dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); + if (m_contextMenuType == ContextMenuDeviceSettings) + { + BasicDeviceSettingsDialog dialog(this); + dialog.setUseReverseAPI(m_settings.m_useReverseAPI); + dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); + dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); + dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); - dialog.move(p); - dialog.exec(); + dialog.move(p); + dialog.exec(); - m_settings.m_useReverseAPI = dialog.useReverseAPI(); - m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); - m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); - m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); + m_settings.m_useReverseAPI = dialog.useReverseAPI(); + m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); + m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); + m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); - sendSettings(); + sendSettings(); + } + + resetContextMenuType(); } void AirspyHFGui::makeUIConnections() diff --git a/plugins/samplesource/airspyhf/readme.md b/plugins/samplesource/airspyhf/readme.md index b4df31de0..05e1992a6 100644 --- a/plugins/samplesource/airspyhf/readme.md +++ b/plugins/samplesource/airspyhf/readme.md @@ -14,6 +14,8 @@ Note: if you use binary distributions this is included in the bundle.

Interface

+The top and bottom bars of the device window are described [here](../../../sdrgui/device/readme.md) + For controls 10 to 13 to be effective you will need a recent version (up to date in January 2019) of both libairspy and the firmware. ![AirspyHF input plugin GUI](../../../doc/img/AirspyHFInput_plugin.png) diff --git a/plugins/samplesource/audioinput/audioinputgui.cpp b/plugins/samplesource/audioinput/audioinputgui.cpp index c6497aa6f..5d186ed09 100644 --- a/plugins/samplesource/audioinput/audioinputgui.cpp +++ b/plugins/samplesource/audioinput/audioinputgui.cpp @@ -22,7 +22,6 @@ #include "ui_audioinputgui.h" #include "gui/colormapper.h" #include "gui/glspectrum.h" -#include "gui/crightclickenabler.h" #include "gui/basicdevicesettingsdialog.h" #include "dsp/dspengine.h" #include "dsp/dspcommands.h" @@ -49,9 +48,7 @@ AudioInputGui::AudioInputGui(DeviceUISet *deviceUISet, QWidget* parent) : m_helpURL = "plugins/samplesource/audioinput/readme.md"; connect(&m_updateTimer, SIGNAL(timeout()), this, SLOT(updateHardware())); - - CRightClickEnabler *startStopRightClickEnabler = new CRightClickEnabler(ui->startStop); - connect(startStopRightClickEnabler, SIGNAL(rightClick(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &))); + connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &))); displaySettings(); makeUIConnections(); @@ -295,21 +292,26 @@ void AudioInputGui::updateHardware() void AudioInputGui::openDeviceSettingsDialog(const QPoint& p) { - BasicDeviceSettingsDialog dialog(this); - dialog.setUseReverseAPI(m_settings.m_useReverseAPI); - dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); - dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); - dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); + if (m_contextMenuType == ContextMenuDeviceSettings) + { + BasicDeviceSettingsDialog dialog(this); + dialog.setUseReverseAPI(m_settings.m_useReverseAPI); + dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); + dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); + dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); - dialog.move(p); - dialog.exec(); + dialog.move(p); + dialog.exec(); - m_settings.m_useReverseAPI = dialog.useReverseAPI(); - m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); - m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); - m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); + m_settings.m_useReverseAPI = dialog.useReverseAPI(); + m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); + m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); + m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); - sendSettings(); + sendSettings(); + } + + resetContextMenuType(); } void AudioInputGui::makeUIConnections() diff --git a/plugins/samplesource/audioinput/readme.md b/plugins/samplesource/audioinput/readme.md index 9767705b4..08e70b8c9 100644 --- a/plugins/samplesource/audioinput/readme.md +++ b/plugins/samplesource/audioinput/readme.md @@ -6,6 +6,8 @@ This input sample source plugin gets its samples from an audio device.

Interface

+The top and bottom bars of the device window are described [here](../../../sdrgui/device/readme.md) + ![Audio input plugin GUI](../../../doc/img/AudioInput_plugin.png)

1: Start/Stop

diff --git a/plugins/samplesource/bladerf1input/bladerf1inputgui.cpp b/plugins/samplesource/bladerf1input/bladerf1inputgui.cpp index 11ac97b8d..5c378ef94 100644 --- a/plugins/samplesource/bladerf1input/bladerf1inputgui.cpp +++ b/plugins/samplesource/bladerf1input/bladerf1inputgui.cpp @@ -24,7 +24,6 @@ #include "ui_bladerf1inputgui.h" #include "gui/colormapper.h" #include "gui/glspectrum.h" -#include "gui/crightclickenabler.h" #include "gui/basicdevicesettingsdialog.h" #include "dsp/dspengine.h" #include "dsp/dspcommands.h" @@ -69,8 +68,7 @@ Bladerf1InputGui::Bladerf1InputGui(DeviceUISet *deviceUISet, QWidget* parent) : connect(&m_statusTimer, SIGNAL(timeout()), this, SLOT(updateStatus())); m_statusTimer.start(500); - CRightClickEnabler *startStopRightClickEnabler = new CRightClickEnabler(ui->startStop); - connect(startStopRightClickEnabler, SIGNAL(rightClick(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &))); + connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &))); displaySettings(); @@ -514,21 +512,26 @@ unsigned int Bladerf1InputGui::getXb200Index(bool xb_200, bladerf_xb200_path xb2 void Bladerf1InputGui::openDeviceSettingsDialog(const QPoint& p) { - BasicDeviceSettingsDialog dialog(this); - dialog.setUseReverseAPI(m_settings.m_useReverseAPI); - dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); - dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); - dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); + if (m_contextMenuType == ContextMenuDeviceSettings) + { + BasicDeviceSettingsDialog dialog(this); + dialog.setUseReverseAPI(m_settings.m_useReverseAPI); + dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); + dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); + dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); - dialog.move(p); - dialog.exec(); + dialog.move(p); + dialog.exec(); - m_settings.m_useReverseAPI = dialog.useReverseAPI(); - m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); - m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); - m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); + m_settings.m_useReverseAPI = dialog.useReverseAPI(); + m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); + m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); + m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); - sendSettings(); + sendSettings(); + } + + resetContextMenuType(); } void Bladerf1InputGui::makeUIConnections() diff --git a/plugins/samplesource/bladerf1input/readme.md b/plugins/samplesource/bladerf1input/readme.md index 1e3157fa0..adc814a03 100644 --- a/plugins/samplesource/bladerf1input/readme.md +++ b/plugins/samplesource/bladerf1input/readme.md @@ -19,6 +19,8 @@ The BladeRF Host library is also provided by many Linux distributions (check its

Interface

+The top and bottom bars of the device window are described [here](../../../sdrgui/device/readme.md) + ![BladeRF1 input plugin GUI](../../../doc/img/BladeRF1Input_plugin.png)

1: Common stream parameters

diff --git a/plugins/samplesource/bladerf2input/bladerf2inputgui.cpp b/plugins/samplesource/bladerf2input/bladerf2inputgui.cpp index e05c5c149..f1a2a3637 100644 --- a/plugins/samplesource/bladerf2input/bladerf2inputgui.cpp +++ b/plugins/samplesource/bladerf2input/bladerf2inputgui.cpp @@ -24,7 +24,6 @@ #include "ui_bladerf2inputgui.h" #include "gui/colormapper.h" #include "gui/glspectrum.h" -#include "gui/crightclickenabler.h" #include "gui/basicdevicesettingsdialog.h" #include "dsp/dspengine.h" #include "dsp/dspcommands.h" @@ -91,8 +90,7 @@ BladeRF2InputGui::BladeRF2InputGui(DeviceUISet *deviceUISet, QWidget* parent) : connect(&m_statusTimer, SIGNAL(timeout()), this, SLOT(updateStatus())); m_statusTimer.start(500); - CRightClickEnabler *startStopRightClickEnabler = new CRightClickEnabler(ui->startStop); - connect(startStopRightClickEnabler, SIGNAL(rightClick(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &))); + connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &))); displaySettings(); @@ -515,21 +513,26 @@ void BladeRF2InputGui::updateStatus() void BladeRF2InputGui::openDeviceSettingsDialog(const QPoint& p) { - BasicDeviceSettingsDialog dialog(this); - dialog.setUseReverseAPI(m_settings.m_useReverseAPI); - dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); - dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); - dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); + if (m_contextMenuType == ContextMenuDeviceSettings) + { + BasicDeviceSettingsDialog dialog(this); + dialog.setUseReverseAPI(m_settings.m_useReverseAPI); + dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); + dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); + dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); - dialog.move(p); - dialog.exec(); + dialog.move(p); + dialog.exec(); - m_settings.m_useReverseAPI = dialog.useReverseAPI(); - m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); - m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); - m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); + m_settings.m_useReverseAPI = dialog.useReverseAPI(); + m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); + m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); + m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); - sendSettings(); + sendSettings(); + } + + resetContextMenuType(); } float BladeRF2InputGui::getGainDB(int gainValue) diff --git a/plugins/samplesource/bladerf2input/readme.md b/plugins/samplesource/bladerf2input/readme.md index 99b66e1a1..30cf214ba 100644 --- a/plugins/samplesource/bladerf2input/readme.md +++ b/plugins/samplesource/bladerf2input/readme.md @@ -14,6 +14,8 @@ The BladeRF Host library is also provided by many Linux distributions (check its

Interface

+The top and bottom bars of the device window are described [here](../../../sdrgui/device/readme.md) + ![BladeRF2 input plugin GUI](../../../doc/img/BladeRF2Input_plugin.png)

1: Common stream parameters

diff --git a/plugins/samplesource/fcdpro/fcdprogui.cpp b/plugins/samplesource/fcdpro/fcdprogui.cpp index 33840a7da..ee7a60bd0 100644 --- a/plugins/samplesource/fcdpro/fcdprogui.cpp +++ b/plugins/samplesource/fcdpro/fcdprogui.cpp @@ -21,7 +21,6 @@ #include "ui_fcdprogui.h" #include "gui/colormapper.h" #include "gui/glspectrum.h" -#include "gui/crightclickenabler.h" #include "gui/basicdevicesettingsdialog.h" #include "dsp/dspengine.h" #include "dsp/dspcommands.h" @@ -151,8 +150,7 @@ FCDProGui::FCDProGui(DeviceUISet *deviceUISet, QWidget* parent) : connect(&m_statusTimer, SIGNAL(timeout()), this, SLOT(updateStatus())); m_statusTimer.start(500); - CRightClickEnabler *startStopRightClickEnabler = new CRightClickEnabler(ui->startStop); - connect(startStopRightClickEnabler, SIGNAL(rightClick(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &))); + connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &))); displaySettings(); makeUIConnections(); @@ -539,21 +537,26 @@ void FCDProGui::updateHardware() void FCDProGui::openDeviceSettingsDialog(const QPoint& p) { - BasicDeviceSettingsDialog dialog(this); - dialog.setUseReverseAPI(m_settings.m_useReverseAPI); - dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); - dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); - dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); + if (m_contextMenuType == ContextMenuDeviceSettings) + { + BasicDeviceSettingsDialog dialog(this); + dialog.setUseReverseAPI(m_settings.m_useReverseAPI); + dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); + dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); + dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); - dialog.move(p); - dialog.exec(); + dialog.move(p); + dialog.exec(); - m_settings.m_useReverseAPI = dialog.useReverseAPI(); - m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); - m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); - m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); + m_settings.m_useReverseAPI = dialog.useReverseAPI(); + m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); + m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); + m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); - sendSettings(); + sendSettings(); + } + + resetContextMenuType(); } void FCDProGui::makeUIConnections() diff --git a/plugins/samplesource/fcdpro/readme.md b/plugins/samplesource/fcdpro/readme.md index 8d1f12e92..5a117c858 100644 --- a/plugins/samplesource/fcdpro/readme.md +++ b/plugins/samplesource/fcdpro/readme.md @@ -6,6 +6,8 @@ This input sample source plugin gets its samples from a [FunCube Dongle (FCD) Pr

Interface

+The top and bottom bars of the device window are described [here](../../../sdrgui/device/readme.md) + ![FCD Pro input plugin GUI](../../../doc/img/FCDPro_plugin.png)

1: Common stream parameters

diff --git a/plugins/samplesource/fcdproplus/fcdproplusgui.cpp b/plugins/samplesource/fcdproplus/fcdproplusgui.cpp index 3fa840b25..33503edfd 100644 --- a/plugins/samplesource/fcdproplus/fcdproplusgui.cpp +++ b/plugins/samplesource/fcdproplus/fcdproplusgui.cpp @@ -22,7 +22,6 @@ #include "ui_fcdproplusgui.h" #include "gui/colormapper.h" #include "gui/glspectrum.h" -#include "gui/crightclickenabler.h" #include "gui/basicdevicesettingsdialog.h" #include "dsp/dspengine.h" #include "dsp/dspcommands.h" @@ -69,8 +68,7 @@ FCDProPlusGui::FCDProPlusGui(DeviceUISet *deviceUISet, QWidget* parent) : connect(&m_statusTimer, SIGNAL(timeout()), this, SLOT(updateStatus())); m_statusTimer.start(500); - CRightClickEnabler *startStopRightClickEnabler = new CRightClickEnabler(ui->startStop); - connect(startStopRightClickEnabler, SIGNAL(rightClick(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &))); + connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &))); displaySettings(); makeUIConnections(); @@ -361,21 +359,26 @@ void FCDProPlusGui::on_transverter_clicked() void FCDProPlusGui::openDeviceSettingsDialog(const QPoint& p) { - BasicDeviceSettingsDialog dialog(this); - dialog.setUseReverseAPI(m_settings.m_useReverseAPI); - dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); - dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); - dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); + if (m_contextMenuType == ContextMenuDeviceSettings) + { + BasicDeviceSettingsDialog dialog(this); + dialog.setUseReverseAPI(m_settings.m_useReverseAPI); + dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); + dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); + dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); - dialog.move(p); - dialog.exec(); + dialog.move(p); + dialog.exec(); - m_settings.m_useReverseAPI = dialog.useReverseAPI(); - m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); - m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); - m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); + m_settings.m_useReverseAPI = dialog.useReverseAPI(); + m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); + m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); + m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); - sendSettings(); + sendSettings(); + } + + resetContextMenuType(); } void FCDProPlusGui::makeUIConnections() diff --git a/plugins/samplesource/fcdproplus/readme.md b/plugins/samplesource/fcdproplus/readme.md index 3a775ada1..8c75e2783 100644 --- a/plugins/samplesource/fcdproplus/readme.md +++ b/plugins/samplesource/fcdproplus/readme.md @@ -6,6 +6,8 @@ This input sample source plugin gets its samples from a [FunCube Dongle (FCD) Pr

Interface

+The top and bottom bars of the device window are described [here](../../../sdrgui/device/readme.md) + ![FCD Pro+ input plugin GUI](../../../doc/img/FCDProPlus_plugin.png)

1: Common stream parameters

@@ -80,4 +82,4 @@ This controls the tuner filter (band) used:

11: IF filter

-Selects the IF filter bandwidth \ No newline at end of file +Selects the IF filter bandwidth diff --git a/plugins/samplesource/fileinput/fileinputgui.cpp b/plugins/samplesource/fileinput/fileinputgui.cpp index 3a3d26dc3..39bc4004b 100644 --- a/plugins/samplesource/fileinput/fileinputgui.cpp +++ b/plugins/samplesource/fileinput/fileinputgui.cpp @@ -27,7 +27,6 @@ #include "plugin/pluginapi.h" #include "gui/colormapper.h" #include "gui/glspectrum.h" -#include "gui/crightclickenabler.h" #include "gui/basicdevicesettingsdialog.h" #include "dsp/dspengine.h" #include "dsp/dspcommands.h" @@ -66,8 +65,7 @@ FileInputGUI::FileInputGUI(DeviceUISet *deviceUISet, QWidget* parent) : connect(&m_statusTimer, SIGNAL(timeout()), this, SLOT(updateStatus())); m_statusTimer.start(500); - CRightClickEnabler *startStopRightClickEnabler = new CRightClickEnabler(ui->startStop); - connect(startStopRightClickEnabler, SIGNAL(rightClick(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &))); + connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &))); setAccelerationCombo(); displaySettings(); @@ -436,21 +434,26 @@ void FileInputGUI::setNumberStr(int n, QString& s) void FileInputGUI::openDeviceSettingsDialog(const QPoint& p) { - BasicDeviceSettingsDialog dialog(this); - dialog.setUseReverseAPI(m_settings.m_useReverseAPI); - dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); - dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); - dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); + if (m_contextMenuType == ContextMenuDeviceSettings) + { + BasicDeviceSettingsDialog dialog(this); + dialog.setUseReverseAPI(m_settings.m_useReverseAPI); + dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); + dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); + dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); - dialog.move(p); - dialog.exec(); + dialog.move(p); + dialog.exec(); - m_settings.m_useReverseAPI = dialog.useReverseAPI(); - m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); - m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); - m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); + m_settings.m_useReverseAPI = dialog.useReverseAPI(); + m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); + m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); + m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); - sendSettings(); + sendSettings(); + } + + resetContextMenuType(); } void FileInputGUI::makeUIConnections() diff --git a/plugins/samplesource/fileinput/readme.md b/plugins/samplesource/fileinput/readme.md index 447d7de8e..f0dad6dd3 100644 --- a/plugins/samplesource/fileinput/readme.md +++ b/plugins/samplesource/fileinput/readme.md @@ -48,6 +48,8 @@ The header takes an integer number of 16 (4 bytes) or 24 (8 bytes) bits samples.

Interface

+The top and bottom bars of the device window are described [here](../../../sdrgui/device/readme.md) + ![File input plugin GUI](../../../doc/img/FileInput_plugin.png)

1: Start/Stop

diff --git a/plugins/samplesource/hackrfinput/hackrfinputgui.cpp b/plugins/samplesource/hackrfinput/hackrfinputgui.cpp index 7f2c2a8d1..2c1658f28 100644 --- a/plugins/samplesource/hackrfinput/hackrfinputgui.cpp +++ b/plugins/samplesource/hackrfinput/hackrfinputgui.cpp @@ -25,7 +25,6 @@ #include "gui/colormapper.h" #include "gui/glspectrum.h" -#include "gui/crightclickenabler.h" #include "gui/basicdevicesettingsdialog.h" #include "dsp/dspengine.h" #include "dsp/dspcommands.h" @@ -63,8 +62,7 @@ HackRFInputGui::HackRFInputGui(DeviceUISet *deviceUISet, QWidget* parent) : connect(&m_statusTimer, SIGNAL(timeout()), this, SLOT(updateStatus())); m_statusTimer.start(500); - CRightClickEnabler *startStopRightClickEnabler = new CRightClickEnabler(ui->startStop); - connect(startStopRightClickEnabler, SIGNAL(rightClick(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &))); + connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &))); displaySettings(); displayBandwidths(); @@ -497,21 +495,26 @@ void HackRFInputGui::updateStatus() void HackRFInputGui::openDeviceSettingsDialog(const QPoint& p) { - BasicDeviceSettingsDialog dialog(this); - dialog.setUseReverseAPI(m_settings.m_useReverseAPI); - dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); - dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); - dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); + if (m_contextMenuType == ContextMenuDeviceSettings) + { + BasicDeviceSettingsDialog dialog(this); + dialog.setUseReverseAPI(m_settings.m_useReverseAPI); + dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); + dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); + dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); - dialog.move(p); - dialog.exec(); + dialog.move(p); + dialog.exec(); - m_settings.m_useReverseAPI = dialog.useReverseAPI(); - m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); - m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); - m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); + m_settings.m_useReverseAPI = dialog.useReverseAPI(); + m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); + m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); + m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); - sendSettings(); + sendSettings(); + } + + resetContextMenuType(); } void HackRFInputGui::makeUIConnections() diff --git a/plugins/samplesource/hackrfinput/readme.md b/plugins/samplesource/hackrfinput/readme.md index f256e06e9..c45712d0c 100644 --- a/plugins/samplesource/hackrfinput/readme.md +++ b/plugins/samplesource/hackrfinput/readme.md @@ -6,6 +6,8 @@ This input sample source plugin gets its samples from a [HackRF device](https://

Interface

+The top and bottom bars of the device window are described [here](../../../sdrgui/device/readme.md) + ![HackRF input plugin GUI](../../../doc/img/HackRFInput_plugin.png)

1: Common stream parameters

@@ -113,4 +115,4 @@ When a device set for the same physical device is present the device center freq When the center frequency position Fc (control 8) is set to center (Cen) in both Rx and Tx the actual frequency of reception and transmission are the same. -In other cases for both frequencies to match you have to set the same sample rate and Fc position (either Inf or Sup) in the Rx and Tx. \ No newline at end of file +In other cases for both frequencies to match you have to set the same sample rate and Fc position (either Inf or Sup) in the Rx and Tx. diff --git a/plugins/samplesource/kiwisdr/kiwisdrgui.cpp b/plugins/samplesource/kiwisdr/kiwisdrgui.cpp index 1b1799b0f..2676ceea1 100644 --- a/plugins/samplesource/kiwisdr/kiwisdrgui.cpp +++ b/plugins/samplesource/kiwisdr/kiwisdrgui.cpp @@ -28,7 +28,6 @@ #include "plugin/pluginapi.h" #include "gui/colormapper.h" #include "gui/glspectrum.h" -#include "gui/crightclickenabler.h" #include "gui/basicdevicesettingsdialog.h" #include "dsp/dspengine.h" #include "dsp/dspcommands.h" @@ -84,8 +83,7 @@ KiwiSDRGui::KiwiSDRGui(DeviceUISet *deviceUISet, QWidget* parent) : connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()), Qt::QueuedConnection); m_sampleSource->setMessageQueueToGUI(&m_inputMessageQueue); - CRightClickEnabler *startStopRightClickEnabler = new CRightClickEnabler(ui->startStop); - connect(startStopRightClickEnabler, SIGNAL(rightClick(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &))); + connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &))); } KiwiSDRGui::~KiwiSDRGui() @@ -302,21 +300,26 @@ void KiwiSDRGui::updateSampleRateAndFrequency() void KiwiSDRGui::openDeviceSettingsDialog(const QPoint& p) { - BasicDeviceSettingsDialog dialog(this); - dialog.setUseReverseAPI(m_settings.m_useReverseAPI); - dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); - dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); - dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); + if (m_contextMenuType == ContextMenuDeviceSettings) + { + BasicDeviceSettingsDialog dialog(this); + dialog.setUseReverseAPI(m_settings.m_useReverseAPI); + dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); + dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); + dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); - dialog.move(p); - dialog.exec(); + dialog.move(p); + dialog.exec(); - m_settings.m_useReverseAPI = dialog.useReverseAPI(); - m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); - m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); - m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); + m_settings.m_useReverseAPI = dialog.useReverseAPI(); + m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); + m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); + m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); - sendSettings(); + sendSettings(); + } + + resetContextMenuType(); } void KiwiSDRGui::makeUIConnections() diff --git a/plugins/samplesource/kiwisdr/readme.md b/plugins/samplesource/kiwisdr/readme.md index f9896ea3d..fb0ce4998 100644 --- a/plugins/samplesource/kiwisdr/readme.md +++ b/plugins/samplesource/kiwisdr/readme.md @@ -6,6 +6,8 @@ This plugin is designed to enable connection to publicly available [KiwiSDR](htt

Interface

+The top and bottom bars of the device window are described [here](../../../sdrgui/device/readme.md) + ![KiwiSDR input plugin GUI](../../../doc/img/KiwiSDRInput_plugin.png)

1: Start/Stop

diff --git a/plugins/samplesource/limesdrinput/limesdrinputgui.cpp b/plugins/samplesource/limesdrinput/limesdrinputgui.cpp index 43937ca8c..20e374c75 100644 --- a/plugins/samplesource/limesdrinput/limesdrinputgui.cpp +++ b/plugins/samplesource/limesdrinput/limesdrinputgui.cpp @@ -26,7 +26,6 @@ #include "ui_limesdrinputgui.h" #include "gui/colormapper.h" #include "gui/glspectrum.h" -#include "gui/crightclickenabler.h" #include "gui/basicdevicesettingsdialog.h" #include "dsp/dspengine.h" #include "dsp/dspcommands.h" @@ -102,8 +101,7 @@ LimeSDRInputGUI::LimeSDRInputGUI(DeviceUISet *deviceUISet, QWidget* parent) : connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()), Qt::QueuedConnection); m_limeSDRInput->setMessageQueueToGUI(&m_inputMessageQueue); - CRightClickEnabler *startStopRightClickEnabler = new CRightClickEnabler(ui->startStop); - connect(startStopRightClickEnabler, SIGNAL(rightClick(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &))); + connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &))); } LimeSDRInputGUI::~LimeSDRInputGUI() @@ -707,21 +705,26 @@ void LimeSDRInputGUI::on_sampleRateMode_toggled(bool checked) void LimeSDRInputGUI::openDeviceSettingsDialog(const QPoint& p) { - BasicDeviceSettingsDialog dialog(this); - dialog.setUseReverseAPI(m_settings.m_useReverseAPI); - dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); - dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); - dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); + if (m_contextMenuType == ContextMenuDeviceSettings) + { + BasicDeviceSettingsDialog dialog(this); + dialog.setUseReverseAPI(m_settings.m_useReverseAPI); + dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); + dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); + dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); - dialog.move(p); - dialog.exec(); + dialog.move(p); + dialog.exec(); - m_settings.m_useReverseAPI = dialog.useReverseAPI(); - m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); - m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); - m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); + m_settings.m_useReverseAPI = dialog.useReverseAPI(); + m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); + m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); + m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); - sendSettings(); + sendSettings(); + } + + resetContextMenuType(); } void LimeSDRInputGUI::makeUIConnections() diff --git a/plugins/samplesource/limesdrinput/readme.md b/plugins/samplesource/limesdrinput/readme.md index 0b5464233..9c86f4687 100644 --- a/plugins/samplesource/limesdrinput/readme.md +++ b/plugins/samplesource/limesdrinput/readme.md @@ -12,6 +12,8 @@ LimeSDR is a 2x2 MIMO device so it has two receiving channels that can run concu

Interface

+The top and bottom bars of the device window are described [here](../../../sdrgui/device/readme.md) + ![LimeSDR input plugin GUI](../../../doc/img/LimeSDRInput_plugin.png)

1: Common stream parameters

@@ -204,4 +206,4 @@ This is the board temperature in degrees Celsius updated every ~5s. Before the f

15: GPIO pins status

-This is the hexadecimal representation of the 8 available GPIO pins of the on board LimeSDR GPIO header. \ No newline at end of file +This is the hexadecimal representation of the 8 available GPIO pins of the on board LimeSDR GPIO header. diff --git a/plugins/samplesource/localinput/localinputgui.cpp b/plugins/samplesource/localinput/localinputgui.cpp index 81edc2066..d5a657dfa 100644 --- a/plugins/samplesource/localinput/localinputgui.cpp +++ b/plugins/samplesource/localinput/localinputgui.cpp @@ -33,7 +33,6 @@ #include "ui_localinputgui.h" #include "gui/colormapper.h" #include "gui/glspectrum.h" -#include "gui/crightclickenabler.h" #include "gui/basicdevicesettingsdialog.h" #include "dsp/dspengine.h" #include "dsp/dspcommands.h" @@ -80,8 +79,7 @@ LocalInputGui::LocalInputGui(DeviceUISet *deviceUISet, QWidget* parent) : .arg(palette().highlight().color().darker(115).name()))); m_helpURL = "plugins/samplesource/localinput/readme.md"; - CRightClickEnabler *startStopRightClickEnabler = new CRightClickEnabler(ui->startStop); - connect(startStopRightClickEnabler, SIGNAL(rightClick(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &))); + connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &))); displaySettings(); makeUIConnections(); @@ -310,21 +308,26 @@ void LocalInputGui::updateStatus() void LocalInputGui::openDeviceSettingsDialog(const QPoint& p) { - BasicDeviceSettingsDialog dialog(this); - dialog.setUseReverseAPI(m_settings.m_useReverseAPI); - dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); - dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); - dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); + if (m_contextMenuType == ContextMenuDeviceSettings) + { + BasicDeviceSettingsDialog dialog(this); + dialog.setUseReverseAPI(m_settings.m_useReverseAPI); + dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); + dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); + dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); - dialog.move(p); - dialog.exec(); + dialog.move(p); + dialog.exec(); - m_settings.m_useReverseAPI = dialog.useReverseAPI(); - m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); - m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); - m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); + m_settings.m_useReverseAPI = dialog.useReverseAPI(); + m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); + m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); + m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); - sendSettings(); + sendSettings(); + } + + resetContextMenuType(); } void LocalInputGui::makeUIConnections() diff --git a/plugins/samplesource/localinput/readme.md b/plugins/samplesource/localinput/readme.md index 119d5abb7..bc58afcba 100644 --- a/plugins/samplesource/localinput/readme.md +++ b/plugins/samplesource/localinput/readme.md @@ -6,6 +6,8 @@ This input sample source plugin gets its samples from a Local Sink channel in an

Interface

+The top and bottom bars of the device window are described [here](../../../sdrgui/device/readme.md) + ![SDR Local input plugin GUI](../../../doc/img/LocalInput_plugin.png)

1: Start/Stop

diff --git a/plugins/samplesource/perseus/perseusgui.cpp b/plugins/samplesource/perseus/perseusgui.cpp index a25a47957..0b4a8c049 100644 --- a/plugins/samplesource/perseus/perseusgui.cpp +++ b/plugins/samplesource/perseus/perseusgui.cpp @@ -25,7 +25,6 @@ #include "ui_perseusgui.h" #include "gui/colormapper.h" #include "gui/glspectrum.h" -#include "gui/crightclickenabler.h" #include "gui/basicdevicesettingsdialog.h" #include "dsp/dspengine.h" #include "dsp/dspcommands.h" @@ -55,8 +54,7 @@ PerseusGui::PerseusGui(DeviceUISet *deviceUISet, QWidget* parent) : connect(&m_statusTimer, SIGNAL(timeout()), this, SLOT(updateStatus())); m_statusTimer.start(500); - CRightClickEnabler *startStopRightClickEnabler = new CRightClickEnabler(ui->startStop); - connect(startStopRightClickEnabler, SIGNAL(rightClick(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &))); + connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &))); displaySettings(); @@ -379,21 +377,26 @@ int PerseusGui::getDevSampleRateIndex(uint32_t sampeRate) void PerseusGui::openDeviceSettingsDialog(const QPoint& p) { - BasicDeviceSettingsDialog dialog(this); - dialog.setUseReverseAPI(m_settings.m_useReverseAPI); - dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); - dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); - dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); + if (m_contextMenuType == ContextMenuDeviceSettings) + { + BasicDeviceSettingsDialog dialog(this); + dialog.setUseReverseAPI(m_settings.m_useReverseAPI); + dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); + dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); + dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); - dialog.move(p); - dialog.exec(); + dialog.move(p); + dialog.exec(); - m_settings.m_useReverseAPI = dialog.useReverseAPI(); - m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); - m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); - m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); + m_settings.m_useReverseAPI = dialog.useReverseAPI(); + m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); + m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); + m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); - sendSettings(); + sendSettings(); + } + + resetContextMenuType(); } void PerseusGui::makeUIConnections() diff --git a/plugins/samplesource/perseus/readme.md b/plugins/samplesource/perseus/readme.md index dcc224458..787a6d8ad 100644 --- a/plugins/samplesource/perseus/readme.md +++ b/plugins/samplesource/perseus/readme.md @@ -17,6 +17,8 @@ If you build it from source and install it in a custom location say: `/opt/insta

Interface

+The top and bottom bars of the device window are described [here](../../../sdrgui/device/readme.md) + It has a limited number of controls compared to other source interfaces. This is because a lot of things are handled automatically within the Perseus: - gains diff --git a/plugins/samplesource/plutosdrinput/plutosdrinputgui.cpp b/plugins/samplesource/plutosdrinput/plutosdrinputgui.cpp index 7b9a71a29..5ce1de780 100644 --- a/plugins/samplesource/plutosdrinput/plutosdrinputgui.cpp +++ b/plugins/samplesource/plutosdrinput/plutosdrinputgui.cpp @@ -23,7 +23,6 @@ #include "dsp/dspengine.h" #include "dsp/dspcommands.h" #include "gui/glspectrum.h" -#include "gui/crightclickenabler.h" #include "gui/basicdevicesettingsdialog.h" #include "device/deviceapi.h" #include "device/deviceuiset.h" @@ -71,8 +70,7 @@ PlutoSDRInputGui::PlutoSDRInputGui(DeviceUISet *deviceUISet, QWidget* parent) : ui->swDecimLabel->setText(QString::fromUtf8("S\u2193")); ui->lpFIRDecimationLabel->setText(QString::fromUtf8("\u2193")); - CRightClickEnabler *startStopRightClickEnabler = new CRightClickEnabler(ui->startStop); - connect(startStopRightClickEnabler, SIGNAL(rightClick(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &))); + connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &))); blockApplySettings(true); displaySettings(); @@ -554,21 +552,26 @@ void PlutoSDRInputGui::updateSampleRateAndFrequency() void PlutoSDRInputGui::openDeviceSettingsDialog(const QPoint& p) { - BasicDeviceSettingsDialog dialog(this); - dialog.setUseReverseAPI(m_settings.m_useReverseAPI); - dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); - dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); - dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); + if (m_contextMenuType == ContextMenuDeviceSettings) + { + BasicDeviceSettingsDialog dialog(this); + dialog.setUseReverseAPI(m_settings.m_useReverseAPI); + dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); + dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); + dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); - dialog.move(p); - dialog.exec(); + dialog.move(p); + dialog.exec(); - m_settings.m_useReverseAPI = dialog.useReverseAPI(); - m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); - m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); - m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); + m_settings.m_useReverseAPI = dialog.useReverseAPI(); + m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); + m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); + m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); - sendSettings(); + sendSettings(); + } + + resetContextMenuType(); } void PlutoSDRInputGui::makeUIConnections() diff --git a/plugins/samplesource/plutosdrinput/readme.md b/plugins/samplesource/plutosdrinput/readme.md index b0cf00bef..33226490c 100644 --- a/plugins/samplesource/plutosdrinput/readme.md +++ b/plugins/samplesource/plutosdrinput/readme.md @@ -27,6 +27,8 @@ Then add the following defines on `cmake` command line when compiling SDRangel:

Interface

+The top and bottom bars of the device window are described [here](../../../sdrgui/device/readme.md) + ![PlutoSDR input plugin GUI](../../../doc/img/PlutoSDRInput_plugin.png)

1: Common stream parameters

diff --git a/plugins/samplesource/remoteinput/readme.md b/plugins/samplesource/remoteinput/readme.md index 142f2db1d..0f614bd9f 100644 --- a/plugins/samplesource/remoteinput/readme.md +++ b/plugins/samplesource/remoteinput/readme.md @@ -18,6 +18,8 @@ The plugin will be built only if the [CM256cc library](https://github.com/f4exb/

Interface

+The top and bottom bars of the device window are described [here](../../../sdrgui/device/readme.md) + ![SDR Remote input plugin GUI](../../../doc/img/RemoteInput_plugin.png)

1: Start/Stop

diff --git a/plugins/samplesource/remoteinput/remoteinputgui.cpp b/plugins/samplesource/remoteinput/remoteinputgui.cpp index d4e821181..aa8187ece 100644 --- a/plugins/samplesource/remoteinput/remoteinputgui.cpp +++ b/plugins/samplesource/remoteinput/remoteinputgui.cpp @@ -29,7 +29,6 @@ #include "ui_remoteinputgui.h" #include "gui/colormapper.h" #include "gui/glspectrum.h" -#include "gui/crightclickenabler.h" #include "gui/basicdevicesettingsdialog.h" #include "dsp/dspengine.h" #include "dsp/dspcommands.h" @@ -80,8 +79,7 @@ RemoteInputGui::RemoteInputGui(DeviceUISet *deviceUISet, QWidget* parent) : ui->remoteDeviceFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold)); ui->remoteDeviceFrequency->setValueRange(8, 0, 99999999); - CRightClickEnabler *startStopRightClickEnabler = new CRightClickEnabler(ui->startStop); - connect(startStopRightClickEnabler, SIGNAL(rightClick(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &))); + connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &))); displaySettings(); @@ -673,21 +671,26 @@ void RemoteInputGui::updateStatus() void RemoteInputGui::openDeviceSettingsDialog(const QPoint& p) { - BasicDeviceSettingsDialog dialog(this); - dialog.setUseReverseAPI(m_settings.m_useReverseAPI); - dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); - dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); - dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); + if (m_contextMenuType == ContextMenuDeviceSettings) + { + BasicDeviceSettingsDialog dialog(this); + dialog.setUseReverseAPI(m_settings.m_useReverseAPI); + dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); + dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); + dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); - dialog.move(p); - dialog.exec(); + dialog.move(p); + dialog.exec(); - m_settings.m_useReverseAPI = dialog.useReverseAPI(); - m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); - m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); - m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); + m_settings.m_useReverseAPI = dialog.useReverseAPI(); + m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); + m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); + m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); - sendSettings(); + sendSettings(); + } + + resetContextMenuType(); } void RemoteInputGui::makeUIConnections() diff --git a/plugins/samplesource/rtlsdr/readme.md b/plugins/samplesource/rtlsdr/readme.md index 6d8b1eee7..3d5920854 100644 --- a/plugins/samplesource/rtlsdr/readme.md +++ b/plugins/samplesource/rtlsdr/readme.md @@ -12,6 +12,8 @@ If you want to benefit from the direct sampling you will have to compile and ins

Interface

+The top and bottom bars of the device window are described [here](../../../sdrgui/device/readme.md) + ![RTLSDR input plugin GUI](../../../doc/img/RTLSDR_plugin.png)

1: Common stream parameters

diff --git a/plugins/samplesource/rtlsdr/rtlsdrgui.cpp b/plugins/samplesource/rtlsdr/rtlsdrgui.cpp index 9d10e9b83..35e9fc142 100644 --- a/plugins/samplesource/rtlsdr/rtlsdrgui.cpp +++ b/plugins/samplesource/rtlsdr/rtlsdrgui.cpp @@ -27,7 +27,6 @@ #include "ui_rtlsdrgui.h" #include "gui/colormapper.h" #include "gui/glspectrum.h" -#include "gui/crightclickenabler.h" #include "gui/basicdevicesettingsdialog.h" #include "dsp/dspengine.h" #include "dsp/dspcommands.h" @@ -59,6 +58,7 @@ RTLSDRGui::RTLSDRGui(DeviceUISet *deviceUISet, QWidget* parent) : ui->rfBW->setColorMapper(ColorMapper(ColorMapper::GrayYellow)); ui->rfBW->setValueRange(4, 0, 10000); + connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &))); connect(&m_updateTimer, SIGNAL(timeout()), this, SLOT(updateHardware())); connect(&m_statusTimer, SIGNAL(timeout()), this, SLOT(updateStatus())); m_statusTimer.start(500); @@ -71,9 +71,6 @@ RTLSDRGui::RTLSDRGui(DeviceUISet *deviceUISet, QWidget* parent) : connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()), Qt::QueuedConnection); m_sampleSource->setMessageQueueToGUI(&m_inputMessageQueue); - - CRightClickEnabler *startStopRightClickEnabler = new CRightClickEnabler(ui->startStop); - connect(startStopRightClickEnabler, SIGNAL(rightClick(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &))); } RTLSDRGui::~RTLSDRGui() @@ -525,21 +522,26 @@ void RTLSDRGui::on_lowSampleRate_toggled(bool checked) void RTLSDRGui::openDeviceSettingsDialog(const QPoint& p) { - BasicDeviceSettingsDialog dialog(this); - dialog.setUseReverseAPI(m_settings.m_useReverseAPI); - dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); - dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); - dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); + if (m_contextMenuType == ContextMenuDeviceSettings) + { + BasicDeviceSettingsDialog dialog(this); + dialog.setUseReverseAPI(m_settings.m_useReverseAPI); + dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); + dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); + dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); - dialog.move(p); - dialog.exec(); + dialog.move(p); + dialog.exec(); - m_settings.m_useReverseAPI = dialog.useReverseAPI(); - m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); - m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); - m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); + m_settings.m_useReverseAPI = dialog.useReverseAPI(); + m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); + m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); + m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); - sendSettings(); + sendSettings(); + } + + resetContextMenuType(); } void RTLSDRGui::makeUIConnections() diff --git a/plugins/samplesource/sdrplay/readme.md b/plugins/samplesource/sdrplay/readme.md index 629ea597b..16838f31f 100644 --- a/plugins/samplesource/sdrplay/readme.md +++ b/plugins/samplesource/sdrplay/readme.md @@ -4,7 +4,7 @@ This plugin supports input from SDRplay RSP1 devices. SDRplay is based on the MSi001 and MSi2500 chips from Mirics. The standard API provided by Mirics is closed source moreover it could not be implemented successfully in SDRangel. An open source API libmirisdr-2 has been written by Miroslav Slugen and later amended by Leif Asbrink SM5BSZ. This API uses a new flavour called [libmirisdr-4](https://github.com/f4exb/libmirisdr-4) in this very same Github space. It contains enhancements and bug fixes. -No Windows support +No Windows support Driver is too unstable in Windows randomly stopping the application and causing BSOD. @@ -14,6 +14,8 @@ As mentioned already the plugin depends on libmirisdr-4. You will have to compil

Interface

+The top and bottom bars of the device window are described [here](../../../sdrgui/device/readme.md) + ![SDRplay plugin GUI](../../../doc/img/SDRPlay_plugin.png)

1. Common controls

@@ -40,7 +42,7 @@ This selects a frequency range corresponding to the hardware path in the SDRplay - 250 MHz to 380 MHz - 380 MHz to 1 GHz - 1 GHz to 2 GHz - +

5. IF bandwidth

This selects the IF filter. Following bandwidths are available according to MSi001 specs: @@ -61,8 +63,8 @@ This selects the IF frequency between these values: - 0 for zero IF - 450 kHz: you have to set sample rate to 1792 kHz (7) and use decimation (8) with an infradyne position (9) - 1620 kHz: you have to set sample rate to 6400 kHz (7) and use decimation (8) with an infradyne position (9) - - 2048 kHz: you have to set sample rate to 8192 kHz (7) and use decimation (8) with an infradyne position (9) - + - 2048 kHz: you have to set sample rate to 8192 kHz (7) and use decimation (8) with an infradyne position (9) +

7. Sample rate

You have the choice between various sample rates from 1536 to 8192 kHz. Some values have a special destination: @@ -70,7 +72,7 @@ You have the choice between various sample rates from 1536 to 8192 kHz. Some val - 1792 kHz: for use with an IF of 450 kHz. - 6400 kHz: for use with an IF of 1620 kHz. - 8192 kHz: for use with an IF of 2048 kHz. - +

8. Decimation

Decimation in powers of two from 1 (no decimation) to 64. @@ -78,24 +80,24 @@ Decimation in powers of two from 1 (no decimation) to 64.

9: Decimated bandpass center frequency position relative the SDRplay center frequency

- **Cen**: the decimation operation takes place around the SDRplay center frequency Fs - - **Inf**: the decimation operation takes place around Fs - Fc. + - **Inf**: the decimation operation takes place around Fs - Fc. - **Sup**: the decimation operation takes place around Fs + Fc. - -With SR as the sample rate before decimation Fc is calculated as: + +With SR as the sample rate before decimation Fc is calculated as: - if decimation n is 4 or lower: Fc = SR/2^(log2(n)-1). The device center frequency is on the side of the baseband. You need a RF filter bandwidth at least twice the baseband. - if decimation n is 8 or higher: Fc = SR/n. The device center frequency is half the baseband away from the side of the baseband. You need a RF filter bandwidth at least 3 times the baseband. - +

10. Tuner gain mode

-Use this radiobutton to select a mode where the gain of the LNA (or mixer buffer below 50 MHz), mixer and baseband amplifiers are automatically selected depending on the tuner gain index (11). This index is the gain value in dB at the nominal gain of all amplifiers. This is not the exact gain at all frequencies because the LNA gain decreases significantly at higher frequencies. +Use this radiobutton to select a mode where the gain of the LNA (or mixer buffer below 50 MHz), mixer and baseband amplifiers are automatically selected depending on the tuner gain index (11). This index is the gain value in dB at the nominal gain of all amplifiers. This is not the exact gain at all frequencies because the LNA gain decreases significantly at higher frequencies.

11. Tuner gain setting

The tuner gain index can be set between 0 and 102 points (corresponds to dB in the nominal case).

12. Manual gain mode

- + Use this radiobutton to select a mode where the gain of all amplifiers can be set independently

13. LNA toggle

diff --git a/plugins/samplesource/sdrplay/sdrplaygui.cpp b/plugins/samplesource/sdrplay/sdrplaygui.cpp index ddeddb627..0ab1d750e 100644 --- a/plugins/samplesource/sdrplay/sdrplaygui.cpp +++ b/plugins/samplesource/sdrplay/sdrplaygui.cpp @@ -27,7 +27,6 @@ #include "ui_sdrplaygui.h" #include "gui/colormapper.h" #include "gui/glspectrum.h" -#include "gui/crightclickenabler.h" #include "gui/basicdevicesettingsdialog.h" #include "dsp/dspengine.h" #include "dsp/dspcommands.h" @@ -78,8 +77,7 @@ SDRPlayGui::SDRPlayGui(DeviceUISet *deviceUISet, QWidget* parent) : connect(&m_statusTimer, SIGNAL(timeout()), this, SLOT(updateStatus())); m_statusTimer.start(500); - CRightClickEnabler *startStopRightClickEnabler = new CRightClickEnabler(ui->startStop); - connect(startStopRightClickEnabler, SIGNAL(rightClick(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &))); + connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &))); displaySettings(); makeUIConnections(); @@ -445,21 +443,26 @@ void SDRPlayGui::on_startStop_toggled(bool checked) void SDRPlayGui::openDeviceSettingsDialog(const QPoint& p) { - BasicDeviceSettingsDialog dialog(this); - dialog.setUseReverseAPI(m_settings.m_useReverseAPI); - dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); - dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); - dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); + if (m_contextMenuType == ContextMenuDeviceSettings) + { + BasicDeviceSettingsDialog dialog(this); + dialog.setUseReverseAPI(m_settings.m_useReverseAPI); + dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); + dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); + dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); - dialog.move(p); - dialog.exec(); + dialog.move(p); + dialog.exec(); - m_settings.m_useReverseAPI = dialog.useReverseAPI(); - m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); - m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); - m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); + m_settings.m_useReverseAPI = dialog.useReverseAPI(); + m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); + m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); + m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); - sendSettings(); + sendSettings(); + } + + resetContextMenuType(); } void SDRPlayGui::makeUIConnections() diff --git a/plugins/samplesource/sdrplayv3/readme.md b/plugins/samplesource/sdrplayv3/readme.md index a24083e4c..903336b8b 100644 --- a/plugins/samplesource/sdrplayv3/readme.md +++ b/plugins/samplesource/sdrplayv3/readme.md @@ -10,6 +10,8 @@ This plugin requires the SDRplay API V3.07 to have been installed and for the se

Interface

+The top and bottom bars of the device window are described [here](../../../sdrgui/device/readme.md) + ![SDRplay v3 plugin GUI](../../../doc/img/SDRPlayV3_plugin.png)

1: Start/Stop

diff --git a/plugins/samplesource/sdrplayv3/sdrplayv3gui.cpp b/plugins/samplesource/sdrplayv3/sdrplayv3gui.cpp index 73be322df..53fd850da 100644 --- a/plugins/samplesource/sdrplayv3/sdrplayv3gui.cpp +++ b/plugins/samplesource/sdrplayv3/sdrplayv3gui.cpp @@ -28,7 +28,6 @@ #include "ui_sdrplayv3gui.h" #include "gui/colormapper.h" #include "gui/glspectrum.h" -#include "gui/crightclickenabler.h" #include "gui/basicdevicesettingsdialog.h" #include "dsp/dspengine.h" #include "dsp/dspcommands.h" @@ -69,8 +68,7 @@ SDRPlayV3Gui::SDRPlayV3Gui(DeviceUISet *deviceUISet, QWidget* parent) : connect(&m_statusTimer, SIGNAL(timeout()), this, SLOT(updateStatus())); m_statusTimer.start(500); - CRightClickEnabler *startStopRightClickEnabler = new CRightClickEnabler(ui->startStop); - connect(startStopRightClickEnabler, SIGNAL(rightClick(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &))); + connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &))); ui->tuner->blockSignals(true); ui->antenna->blockSignals(true); @@ -509,21 +507,26 @@ void SDRPlayV3Gui::on_transverter_clicked() void SDRPlayV3Gui::openDeviceSettingsDialog(const QPoint& p) { - BasicDeviceSettingsDialog dialog(this); - dialog.setUseReverseAPI(m_settings.m_useReverseAPI); - dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); - dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); - dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); + if (m_contextMenuType == ContextMenuDeviceSettings) + { + BasicDeviceSettingsDialog dialog(this); + dialog.setUseReverseAPI(m_settings.m_useReverseAPI); + dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); + dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); + dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); - dialog.move(p); - dialog.exec(); + dialog.move(p); + dialog.exec(); - m_settings.m_useReverseAPI = dialog.useReverseAPI(); - m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); - m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); - m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); + m_settings.m_useReverseAPI = dialog.useReverseAPI(); + m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); + m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); + m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); - sendSettings(); + sendSettings(); + } + + resetContextMenuType(); } void SDRPlayV3Gui::makeUIConnections() diff --git a/plugins/samplesource/sigmffileinput/readme.md b/plugins/samplesource/sigmffileinput/readme.md index 996eb0383..62dab8880 100644 --- a/plugins/samplesource/sigmffileinput/readme.md +++ b/plugins/samplesource/sigmffileinput/readme.md @@ -12,6 +12,8 @@ Note: this plugin is officially supported since version 6.

Interface

+The top and bottom bars of the device window are described [here](../../../sdrgui/device/readme.md) + ![SigMF File input plugin GUI](../../../doc/img/SigMFFileInput_plugin.png)

1: Start/Stop

diff --git a/plugins/samplesource/sigmffileinput/sigmffileinputgui.cpp b/plugins/samplesource/sigmffileinput/sigmffileinputgui.cpp index 41bcd1979..d2f88919a 100644 --- a/plugins/samplesource/sigmffileinput/sigmffileinputgui.cpp +++ b/plugins/samplesource/sigmffileinput/sigmffileinputgui.cpp @@ -27,7 +27,6 @@ #include "plugin/pluginapi.h" #include "gui/colormapper.h" #include "gui/glspectrum.h" -#include "gui/crightclickenabler.h" #include "gui/basicdevicesettingsdialog.h" #include "dsp/dspengine.h" #include "dsp/dspcommands.h" @@ -75,8 +74,7 @@ SigMFFileInputGUI::SigMFFileInputGUI(DeviceUISet *deviceUISet, QWidget* parent) connect(&m_statusTimer, SIGNAL(timeout()), this, SLOT(updateStatus())); m_statusTimer.start(500); - CRightClickEnabler *startStopRightClickEnabler = new CRightClickEnabler(ui->startStop); - connect(startStopRightClickEnabler, SIGNAL(rightClick(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &))); + connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &))); setAccelerationCombo(); displaySettings(); @@ -668,21 +666,26 @@ void SigMFFileInputGUI::setNumberStr(int n, QString& s) void SigMFFileInputGUI::openDeviceSettingsDialog(const QPoint& p) { - BasicDeviceSettingsDialog dialog(this); - dialog.setUseReverseAPI(m_settings.m_useReverseAPI); - dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); - dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); - dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); + if (m_contextMenuType == ContextMenuDeviceSettings) + { + BasicDeviceSettingsDialog dialog(this); + dialog.setUseReverseAPI(m_settings.m_useReverseAPI); + dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); + dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); + dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); - dialog.move(p); - dialog.exec(); + dialog.move(p); + dialog.exec(); - m_settings.m_useReverseAPI = dialog.useReverseAPI(); - m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); - m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); - m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); + m_settings.m_useReverseAPI = dialog.useReverseAPI(); + m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); + m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); + m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); - sendSettings(); + sendSettings(); + } + + resetContextMenuType(); } void SigMFFileInputGUI::makeUIConnections() diff --git a/plugins/samplesource/soapysdrinput/readme.md b/plugins/samplesource/soapysdrinput/readme.md index 05b8b6fab..31b116333 100644 --- a/plugins/samplesource/soapysdrinput/readme.md +++ b/plugins/samplesource/soapysdrinput/readme.md @@ -73,6 +73,8 @@ When installed the Red Pitaya SoapySDR plugin lists a Red Pitaya device even if

Interface

+The top and bottom bars of the device window are described [here](../../../sdrgui/device/readme.md) + ![SoapySDR input plugin GUI](../../../doc/img/SoapySDRInput_plugin1.png) The top part described by number tags is common for all devices. The bottom part under the "A" tag depends on the SoapySDR device implementation. The corresponding widgets are stacked vertically inside a scrollable area as there may be many controls depending on how the device interface is implemented in SoapySDR. Move the slider on the right to see all parameters available. diff --git a/plugins/samplesource/soapysdrinput/soapysdrinputgui.cpp b/plugins/samplesource/soapysdrinput/soapysdrinputgui.cpp index 500b3cedc..fa97d6638 100644 --- a/plugins/samplesource/soapysdrinput/soapysdrinputgui.cpp +++ b/plugins/samplesource/soapysdrinput/soapysdrinputgui.cpp @@ -25,7 +25,6 @@ #include "device/deviceuiset.h" #include "util/simpleserializer.h" #include "gui/glspectrum.h" -#include "gui/crightclickenabler.h" #include "gui/basicdevicesettingsdialog.h" #include "soapygui/discreterangegui.h" #include "soapygui/intervalrangegui.h" @@ -96,8 +95,7 @@ SoapySDRInputGui::SoapySDRInputGui(DeviceUISet *deviceUISet, QWidget* parent) : connect(&m_statusTimer, SIGNAL(timeout()), this, SLOT(updateStatus())); m_statusTimer.start(500); - CRightClickEnabler *startStopRightClickEnabler = new CRightClickEnabler(ui->startStop); - connect(startStopRightClickEnabler, SIGNAL(rightClick(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &))); + connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &))); displaySettings(); @@ -913,21 +911,26 @@ void SoapySDRInputGui::updateStatus() void SoapySDRInputGui::openDeviceSettingsDialog(const QPoint& p) { - BasicDeviceSettingsDialog dialog(this); - dialog.setUseReverseAPI(m_settings.m_useReverseAPI); - dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); - dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); - dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); + if (m_contextMenuType == ContextMenuDeviceSettings) + { + BasicDeviceSettingsDialog dialog(this); + dialog.setUseReverseAPI(m_settings.m_useReverseAPI); + dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); + dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); + dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); - dialog.move(p); - dialog.exec(); + dialog.move(p); + dialog.exec(); - m_settings.m_useReverseAPI = dialog.useReverseAPI(); - m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); - m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); - m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); + m_settings.m_useReverseAPI = dialog.useReverseAPI(); + m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); + m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); + m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); - sendSettings(); + sendSettings(); + } + + resetContextMenuType(); } void SoapySDRInputGui::makeUIConnections() diff --git a/plugins/samplesource/testsource/testsourcegui.cpp b/plugins/samplesource/testsource/testsourcegui.cpp index 27a859af3..e6236daf8 100644 --- a/plugins/samplesource/testsource/testsourcegui.cpp +++ b/plugins/samplesource/testsource/testsourcegui.cpp @@ -27,7 +27,6 @@ #include "plugin/pluginapi.h" #include "gui/colormapper.h" #include "gui/glspectrum.h" -#include "gui/crightclickenabler.h" #include "gui/basicdevicesettingsdialog.h" #include "dsp/dspengine.h" #include "dsp/dspcommands.h" @@ -76,8 +75,7 @@ TestSourceGui::TestSourceGui(DeviceUISet *deviceUISet, QWidget* parent) : connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()), Qt::QueuedConnection); m_sampleSource->setMessageQueueToGUI(&m_inputMessageQueue); - CRightClickEnabler *startStopRightClickEnabler = new CRightClickEnabler(ui->startStop); - connect(startStopRightClickEnabler, SIGNAL(rightClick(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &))); + connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &))); } TestSourceGui::~TestSourceGui() @@ -492,21 +490,26 @@ void TestSourceGui::updateSampleRateAndFrequency() void TestSourceGui::openDeviceSettingsDialog(const QPoint& p) { - BasicDeviceSettingsDialog dialog(this); - dialog.setUseReverseAPI(m_settings.m_useReverseAPI); - dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); - dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); - dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); + if (m_contextMenuType == ContextMenuDeviceSettings) + { + BasicDeviceSettingsDialog dialog(this); + dialog.setUseReverseAPI(m_settings.m_useReverseAPI); + dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); + dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); + dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); - dialog.move(p); - dialog.exec(); + dialog.move(p); + dialog.exec(); - m_settings.m_useReverseAPI = dialog.useReverseAPI(); - m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); - m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); - m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); + m_settings.m_useReverseAPI = dialog.useReverseAPI(); + m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); + m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); + m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); - sendSettings(); + sendSettings(); + } + + resetContextMenuType(); } void TestSourceGui::makeUIConnections() diff --git a/plugins/samplesource/usrpinput/readme.md b/plugins/samplesource/usrpinput/readme.md index be639aa92..3d57a4803 100644 --- a/plugins/samplesource/usrpinput/readme.md +++ b/plugins/samplesource/usrpinput/readme.md @@ -6,8 +6,9 @@ This input sample source plugin gets its samples from a [USRP device](https://ww

Interface

-![USRP input plugin GUI](../../../doc/img/USRPInput_plugin.png) +The top and bottom bars of the device window are described [here](../../../sdrgui/device/readme.md) +![USRP input plugin GUI](../../../doc/img/USRPInput_plugin.png)

1: Start/Stop

@@ -19,7 +20,7 @@ Device start / stop button.

2A: Sample rate

-This is the sample rate at which IQ samples are transfered the device to SDRangel, in kS/s (k) or MS/s (M). +This is the sample rate at which IQ samples are transfered the device to SDRangel, in kS/s (k) or MS/s (M).

2B: Stream sample rate

@@ -130,7 +131,7 @@ This label turns green when data is being received from the device. - **O**: turns red if stream experiences overruns - **T**: turns red if stream experiences timeouts - + The stream warning indicators are reset when the acqusition is started.

Dependendices

diff --git a/plugins/samplesource/usrpinput/usrpinputgui.cpp b/plugins/samplesource/usrpinput/usrpinputgui.cpp index bdc8459d4..d2edc86bd 100644 --- a/plugins/samplesource/usrpinput/usrpinputgui.cpp +++ b/plugins/samplesource/usrpinput/usrpinputgui.cpp @@ -27,7 +27,6 @@ #include "ui_usrpinputgui.h" #include "gui/colormapper.h" #include "gui/glspectrum.h" -#include "gui/crightclickenabler.h" #include "gui/basicdevicesettingsdialog.h" #include "dsp/dspengine.h" #include "dsp/dspcommands.h" @@ -90,8 +89,7 @@ USRPInputGUI::USRPInputGUI(DeviceUISet *deviceUISet, QWidget* parent) : connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()), Qt::QueuedConnection); m_usrpInput->setMessageQueueToGUI(&m_inputMessageQueue); - CRightClickEnabler *startStopRightClickEnabler = new CRightClickEnabler(ui->startStop); - connect(startStopRightClickEnabler, SIGNAL(rightClick(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &))); + connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &))); } USRPInputGUI::~USRPInputGUI() @@ -609,21 +607,26 @@ void USRPInputGUI::on_sampleRateMode_toggled(bool checked) void USRPInputGUI::openDeviceSettingsDialog(const QPoint& p) { - BasicDeviceSettingsDialog dialog(this); - dialog.setUseReverseAPI(m_settings.m_useReverseAPI); - dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); - dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); - dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); + if (m_contextMenuType == ContextMenuDeviceSettings) + { + BasicDeviceSettingsDialog dialog(this); + dialog.setUseReverseAPI(m_settings.m_useReverseAPI); + dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); + dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); + dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); - dialog.move(p); - dialog.exec(); + dialog.move(p); + dialog.exec(); - m_settings.m_useReverseAPI = dialog.useReverseAPI(); - m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); - m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); - m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); + m_settings.m_useReverseAPI = dialog.useReverseAPI(); + m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); + m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); + m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); - sendSettings(); + sendSettings(); + } + + resetContextMenuType(); } void USRPInputGUI::makeUIConnections() diff --git a/plugins/samplesource/xtrxinput/readme.md b/plugins/samplesource/xtrxinput/readme.md index 57c63f134..b82329800 100644 --- a/plugins/samplesource/xtrxinput/readme.md +++ b/plugins/samplesource/xtrxinput/readme.md @@ -44,6 +44,8 @@ For a group the syntax is the same but the group name is prefixed with `@` like:

Interface

+The top and bottom bars of the device window are described [here](../../../sdrgui/device/readme.md) + ![LimeSDR input plugin GUI](../../../doc/img/XTRXInput_plugin.png)

1: Common stream parameters

diff --git a/plugins/samplesource/xtrxinput/xtrxinputgui.cpp b/plugins/samplesource/xtrxinput/xtrxinputgui.cpp index 4f46ed0da..92409fad1 100644 --- a/plugins/samplesource/xtrxinput/xtrxinputgui.cpp +++ b/plugins/samplesource/xtrxinput/xtrxinputgui.cpp @@ -27,7 +27,6 @@ #include "ui_xtrxinputgui.h" #include "gui/colormapper.h" #include "gui/glspectrum.h" -#include "gui/crightclickenabler.h" #include "gui/basicdevicesettingsdialog.h" #include "dsp/dspengine.h" #include "dsp/dspcommands.h" @@ -77,8 +76,7 @@ XTRXInputGUI::XTRXInputGUI(DeviceUISet *deviceUISet, QWidget* parent) : connect(&m_statusTimer, SIGNAL(timeout()), this, SLOT(updateStatus())); m_statusTimer.start(500); - CRightClickEnabler *startStopRightClickEnabler = new CRightClickEnabler(ui->startStop); - connect(startStopRightClickEnabler, SIGNAL(rightClick(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &))); + connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &))); displaySettings(); makeUIConnections(); @@ -636,21 +634,26 @@ void XTRXInputGUI::on_sampleRateMode_toggled(bool checked) void XTRXInputGUI::openDeviceSettingsDialog(const QPoint& p) { - BasicDeviceSettingsDialog dialog(this); - dialog.setUseReverseAPI(m_settings.m_useReverseAPI); - dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); - dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); - dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); + if (m_contextMenuType == ContextMenuDeviceSettings) + { + BasicDeviceSettingsDialog dialog(this); + dialog.setUseReverseAPI(m_settings.m_useReverseAPI); + dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); + dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); + dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); - dialog.move(p); - dialog.exec(); + dialog.move(p); + dialog.exec(); - m_settings.m_useReverseAPI = dialog.useReverseAPI(); - m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); - m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); - m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); + m_settings.m_useReverseAPI = dialog.useReverseAPI(); + m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); + m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); + m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); - sendSettings(); + sendSettings(); + } + + resetContextMenuType(); } void XTRXInputGUI::makeUIConnections() diff --git a/sdrgui/channel/channelgui.cpp b/sdrgui/channel/channelgui.cpp index 1ad76e619..7b274bf85 100644 --- a/sdrgui/channel/channelgui.cpp +++ b/sdrgui/channel/channelgui.cpp @@ -240,7 +240,7 @@ void ChannelGUI::setStreamIndicator(const QString& indicator) void ChannelGUI::activateSettingsDialog() { - QPoint p = mapFromGlobal(QCursor::pos()); + QPoint p = QCursor::pos(); m_contextMenuType = ContextMenuChannelSettings; emit customContextMenuRequested(p); } diff --git a/sdrgui/device/devicegui.cpp b/sdrgui/device/devicegui.cpp index 34eba9840..231ee3a4f 100644 --- a/sdrgui/device/devicegui.cpp +++ b/sdrgui/device/devicegui.cpp @@ -48,6 +48,11 @@ DeviceGUI::DeviceGUI(QWidget *parent) : m_indexLabel->setText(tr("X:%1").arg(m_deviceSetIndex)); m_indexLabel->setToolTip("Device type and set index"); + m_settingsButton = new QPushButton(); + QIcon settingsIcon(":/gear.png"); + m_settingsButton->setIcon(settingsIcon); + m_settingsButton->setToolTip("Common settings"); + m_changeDeviceButton = new QPushButton(); m_changeDeviceButton->setFixedSize(20, 20); QIcon changeDeviceIcon(":/swap.png"); @@ -129,6 +134,7 @@ DeviceGUI::DeviceGUI(QWidget *parent) : m_topLayout = new QHBoxLayout(); m_topLayout->setContentsMargins(0, 0, 0, 0); m_topLayout->addWidget(m_indexLabel); + m_topLayout->addWidget(m_settingsButton); m_topLayout->addWidget(m_changeDeviceButton); m_topLayout->addWidget(m_reloadDeviceButton); m_topLayout->addWidget(m_addChannelsButton); @@ -166,6 +172,7 @@ DeviceGUI::DeviceGUI(QWidget *parent) : QObjectCleanupHandler().add(layout()); setLayout(m_layouts); + connect(m_settingsButton, SIGNAL(clicked()), this, SLOT(activateSettingsDialog())); connect(m_changeDeviceButton, SIGNAL(clicked()), this, SLOT(openChangeDeviceDialog())); connect(m_reloadDeviceButton, SIGNAL(clicked()), this, SLOT(deviceReload())); connect(m_addChannelsButton, SIGNAL(clicked()), this, SLOT(openAddChannelsDialog())); @@ -207,6 +214,7 @@ DeviceGUI::~DeviceGUI() delete m_addChannelsButton; delete m_reloadDeviceButton; delete m_changeDeviceButton; + delete m_settingsButton; delete m_indexLabel; qDebug("DeviceGUI::~DeviceGUI: end"); } @@ -255,6 +263,13 @@ void DeviceGUI::deviceReload() } } +void DeviceGUI::activateSettingsDialog() +{ + QPoint p = QCursor::pos(); + m_contextMenuType = ContextMenuDeviceSettings; + emit customContextMenuRequested(p); +} + void DeviceGUI::showHelp() { if (m_helpURL.isEmpty()) { diff --git a/sdrgui/device/devicegui.h b/sdrgui/device/devicegui.h index aee35e306..13fd111da 100644 --- a/sdrgui/device/devicegui.h +++ b/sdrgui/device/devicegui.h @@ -49,8 +49,7 @@ public: enum ContextMenuType { ContextMenuNone, - ContextMenuChannelSettings, - ContextMenuStreamSettings + ContextMenuDeviceSettings }; DeviceGUI(QWidget *parent = nullptr); @@ -100,6 +99,7 @@ private: QString getDeviceTypeTag(); QLabel *m_indexLabel; + QPushButton *m_settingsButton; QPushButton *m_changeDeviceButton; QPushButton *m_reloadDeviceButton; QPushButton *m_addChannelsButton; @@ -124,6 +124,7 @@ private: ChannelAddDialog m_channelAddDialog; private slots: + void activateSettingsDialog(); void openChangeDeviceDialog(); void openAddChannelsDialog(); void deviceReload(); diff --git a/sdrgui/device/readme.md b/sdrgui/device/readme.md new file mode 100644 index 000000000..d07c77aef --- /dev/null +++ b/sdrgui/device/readme.md @@ -0,0 +1,225 @@ +

Device common controls

+ +The device windows have common top and bottom bars + + +![Device window](../../doc/img/DeviceWindow.png) + +

A: Top bar

+ +![Device window top bar](../../doc/img/DeviceWindow_top.png) + +

A.1 Devcice type and index

+ +The format is: + - Device type: + - R: receiver + - T: transmitter + - M: MIMO + - Semicolon separator + - Device set index. A "device set" is the set of a device and its corresponding channels + +THe tooltip shows the device type, sequence number and serial number of the device if it exists. + +You may click on this area and drag the window with the mouse. + +

A.2: Device common settings

+ +All device types have common settings. Clicking on the button opens a dialog to update them + +![Basic device settings dialog](../../doc/img/BasicDeviceSettings.png) + +

A.2.1: Toggle reverse API feature

+ +Use this checkbox to toggle on/off the reverse API feature. With reverse API engaged the changes in the device settings are forwarded to an API endpoint given by address (2.1.2.2), port (2.1.2.3) and device index (2.1.2.4) in the same format as the SDRangel REST API device settings endpoint. With the values of the screenshot the API URL is: `http://127.0.0.1:8888/sdrangel/deviceset/0/device/settings` The JSON payload follows the same format as the SDRangel REST API device settings. For example with HachRF Rx this would be something like: + +``` +{ + "deviceHwType": "HackRF", + "hackRFInputSettings": { + "LOppmTenths": 0, + "bandwidth": 1750000, + "biasT": 0, + "centerFrequency": 435000000, + "dcBlock": 0, + "devSampleRate": 2400000, + "fcPos": 2, + "iqCorrection": 0, + "linkTxFrequency": 0, + "lnaExt": 0, + "lnaGain": 16, + "log2Decim": 0, + "vgaGain": 16 + }, + "tx": 0 +} +``` +Note that the PATCH method is used. The full set of parameters is sent only when the reverse API is toggled on or a full settings update is done. + +The start and stop actions are also forwarded with the `/sdrangel/deviceset/{deviceSetIndex}/device/run` API endpoint using POST (start) or DELETE (stop) methods. + +More details on this feature can be found on the corresponding Wiki page. + +

A.2.2: API address

+ +This is the IP address of the API endpoint + +

A.2.3: API port

+ +This is the IP port of the API endpoint + +

A.2.4: Device index

+ +This is the targeted device index + +

A.2.5: Cancel changes and exit dialog

+ +Do not make any changes and exit dialog + +

A.2.6: Validate and exit dialog

+ +Validates the data (saves it in the channel marker object) and exits the dialog + + +

A.3: Change device

+ +Opens a dialog that lets you choose a different devuce + +![Main Window sampling devices dialog](../../doc/img/MainWindow_SDDialog.png) + +

A.3.1: Device selection combo

+ +Use this combo box to select the device. Only available devices will appear in the list. For devices having more than one channel (ex: LimeSDR) the channel number will appear next to the device sequence number inside the brackets. Ex: `LimeSDR[0:1] 0009060B00473419` designates the second Rx (Rx #1) of the first encountered LimeSDR which serial number is 0009060B00473419. + +

A.3.2: Device selection confirmation

+ +Use the `OK` button to confirm your choice and exit the dialog + +

A.3.3: Device selection cancellation

+ +Use the `Cancel` button to exit the dialog without any change + +

A.4: Reload device

+ +This button activates a close/open sequence to recycle the device. It may be useful when the device is not streaming anymore or in an attempt to clear possible errors. Make sure the streaming is stopped first. + +

A.5: Add channels

+ +Opens a dialog to add one or more channels connected to the device. + +![Add channels dialog](../../doc/img/AddChannels_dialog.png) + +

A.5.1: Channel selection

+ +Use this combo to select which channel type to add + +

A.5.2: Close dialog

+ +Use this button to dismiss the dialog + +

A.5.3: Add channel

+ +Add a new channel by clicking on the `Apply` button. You may click it several times to add more channels. The dialog can be dismissed with the `Close` button or the closing window icon `X` on the top bar. + +

A.6: Device set presets

+ +Opens a dialog to manage device set presets. Device set presets store configurations related to a single device set + +![Presets dialog](../../doc/img/MainWindow_presets_view.png) + +

A.6.1: Preset selection

+You select a preset or a preset group by clicking on its line in the tree view. All actions (6) will be done relative to this preset or preset group. + +

A.6.2: Group

+You can organize your presets into groups. Groups can be collapsed or expanded by using the caret icon on the left. + +

A.6.3: Center frequency

+The center frequency used in this preset is displayed here. + +

A.6.4: Rx/Tx indicator

+"R" is displayed for a Rx device set and "T" for a Tx device set + +

A.6.5: Preset name

+You can give a name to your preset. Names need not to be unique. + +

A.6.6: Preset control or actions

+ +![Presets controls](../../doc/img/MainWindow_presets.png) + +
A.6.6.1: New preset
+Click on this icon to create a new preset with the current values in the selected sample device tab (Main window: 2). + +
A.6.6.2: Update preset
+Click on this icon to create a update the selected preset with the current values in the selected sample device tab (Main window: 2). Please note that this does not save the preset immediately on disk to save presets immediately you need to use the save button (4). + +
A.6.6.3: Edit preset
+Opens a new window where you can change the group name and description. + + - for group items you can rename the group or merge all group presets into an existing group by selecting this existing group + + - for preset items you can: + - move the preset to another existing group by selecting this existing group + - assign this preset to a new group by typing in this new group + - change the description + +
A.6.6.4: Export preset
+Using the previous icon presets are saved globally in a system dependent place. Using this icon you can export a specific preset in a single file that can be imported on another machine possibly with a different O/S. The preset binary data (BLOB) is saved in Base-64 format. + +
A.6.6.5: Import preset
+This is the opposite of the previous operation. This will create a new preset in the selected group or the same group as the preset being selected. + +
A.6.6.6: Delete preset
+on a preset item: deletes the selected preset. +on a preset group: deletes the group and all its presets. + +
A.6.6.7: Load preset
+Applies the selected preset to the current device set (source and channel plugins). + +

A.7: Title

+ +The window title shows the device type and a sequence number. + +You may click on this area and drag the window with the mouse. + +

A.8: Help

+ +Clicking on this button opens the documentation about the device controls in github in the browser. + +

A.9: Move to a workspace

+ +Opens a dialog to choose a destination workspace to move the device window to. Nothing happens if the same workspace is selected. + +

A.10: Shrink window

+ +Click this button to reduce the window to its minimum size + +

A.11: Close window

+ +Click this button to close the window and removes the channel from the device set. + +

A.12: Top size grip

+ +You can drag this gray square to resize the window + +

B: Botom bar

+ +![Device window bottom bar](../../doc/img/DeviceWindow_bottom.png) + +

B.1: Show spectrum

+ +When the main spectrum window of this device is hidden this will show it. It has no effect if the main spectrum window is displayed. + +

B.2: Show all channels

+ +This will show all hidden channel windows if any. It has no effects on channel windows already displayed. + +

B.3: Status text

+ +The status messages appear here if any. + +You may click on this area and drag the window with the mouse. + +

A.4: Bottom size grip

+ +You can drag this gray square to resize the window + From 2c070f811e8e2e07d8d23dc0513dd94c4e65ca27 Mon Sep 17 00:00:00 2001 From: f4exb Date: Sun, 17 Apr 2022 10:20:07 +0200 Subject: [PATCH 032/115] Massive UI revamping (v7): Channel window documentation. Show device type name in index tooltip --- doc/img/ChannelWindow.png | Bin 0 -> 31954 bytes doc/img/ChannelWindow.xcf | Bin 0 -> 74318 bytes doc/img/ChannelWindow_bottom.png | Bin 0 -> 5530 bytes doc/img/ChannelWindow_bottom.xcf | Bin 0 -> 18590 bytes doc/img/ChannelWindow_top.png | Bin 0 -> 9647 bytes doc/img/ChannelWindow_top.xcf | Bin 0 -> 35027 bytes .../channelmimo/beamsteeringcwmod/readme.md | 2 + plugins/channelmimo/interferometer/readme.md | 2 + plugins/channelrx/chanalyzer/readme.md | 2 + plugins/channelrx/demodadsb/readme.md | 2 + plugins/channelrx/demodais/readme.md | 2 + plugins/channelrx/demodam/readme.md | 4 +- plugins/channelrx/demodapt/readme.md | 4 +- plugins/channelrx/demodatv/readme.md | 2 + plugins/channelrx/demodbfm/readme.md | 2 + plugins/channelrx/demodchirpchat/readme.md | 2 + plugins/channelrx/demoddab/readme.md | 2 + plugins/channelrx/demoddatv/readme.md | 2 + plugins/channelrx/demoddsd/readme.md | 2 + plugins/channelrx/demodfreedv/readme.md | 8 +- plugins/channelrx/demodnfm/readme.md | 2 + plugins/channelrx/demodpacket/readme.md | 2 + plugins/channelrx/demodpager/readme.md | 2 + plugins/channelrx/demodradiosonde/readme.md | 14 +- plugins/channelrx/demodssb/readme.md | 2 + plugins/channelrx/demodvor/readme.md | 2 + plugins/channelrx/demodvorsc/readme.md | 2 + plugins/channelrx/demodwfm/readme.md | 2 + plugins/channelrx/filesink/readme.md | 2 + plugins/channelrx/freqtracker/readme.md | 2 + plugins/channelrx/localsink/readme.md | 2 + plugins/channelrx/noisefigure/readme.md | 2 + plugins/channelrx/radioastronomy/readme.md | 2 + plugins/channelrx/radioclock/readme.md | 2 + plugins/channelrx/remotesink/readme.md | 2 + plugins/channelrx/sigmffilesink/readme.md | 2 + plugins/channelrx/udpsink/readme.md | 24 +-- plugins/channeltx/filesource/readme.md | 2 + plugins/channeltx/localsource/readme.md | 2 + plugins/channeltx/mod802.15.4/readme.md | 2 + plugins/channeltx/modais/readme.md | 2 + plugins/channeltx/modam/readme.md | 2 + plugins/channeltx/modatv/readme.md | 2 + plugins/channeltx/modchirpchat/readme.md | 2 + plugins/channeltx/moddatv/readme.md | 2 + plugins/channeltx/modfreedv/readme.md | 2 + plugins/channeltx/modnfm/readme.md | 2 + plugins/channeltx/modpacket/readme.md | 2 + plugins/channeltx/modssb/readme.md | 2 + plugins/channeltx/modwfm/readme.md | 2 + plugins/channeltx/remotesource/readme.md | 2 + plugins/channeltx/udpsource/readme.md | 18 +- sdrgui/channel/channelgui.cpp | 7 +- sdrgui/channel/channelgui.h | 2 + sdrgui/channel/readme.md | 165 ++++++++++++++++++ sdrgui/device/deviceuiset.cpp | 19 +- sdrgui/device/readme.md | 11 +- sdrgui/mainwindow.cpp | 5 + 58 files changed, 317 insertions(+), 44 deletions(-) create mode 100644 doc/img/ChannelWindow.png create mode 100644 doc/img/ChannelWindow.xcf create mode 100644 doc/img/ChannelWindow_bottom.png create mode 100644 doc/img/ChannelWindow_bottom.xcf create mode 100644 doc/img/ChannelWindow_top.png create mode 100644 doc/img/ChannelWindow_top.xcf create mode 100644 sdrgui/channel/readme.md diff --git a/doc/img/ChannelWindow.png b/doc/img/ChannelWindow.png new file mode 100644 index 0000000000000000000000000000000000000000..fe980a6377f76527b60eb187a5ba2bc675751623 GIT binary patch literal 31954 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 IzjWLG1K*pmGynhq literal 0 HcmV?d00001 diff --git a/doc/img/ChannelWindow.xcf b/doc/img/ChannelWindow.xcf new file mode 100644 index 0000000000000000000000000000000000000000..834ea8e2fdf3e5534ea9c50433e0c148bae4de7c GIT binary patch literal 74318 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 OF}K#-BykPmvi}D%UsMYK literal 0 HcmV?d00001 diff --git a/doc/img/ChannelWindow_bottom.png b/doc/img/ChannelWindow_bottom.png new file mode 100644 index 0000000000000000000000000000000000000000..d3602df2dd9cf73ab65a5b7805d629b25f9596ce GIT binary patch literal 5530 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 literal 0 HcmV?d00001 diff --git a/doc/img/ChannelWindow_bottom.xcf b/doc/img/ChannelWindow_bottom.xcf new file mode 100644 index 0000000000000000000000000000000000000000..d92c777b9d88b870010bb38869191ce197c2f070 GIT binary patch literal 18590 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 YcUE9k;>v8BR0LN|`-Hz_?Zhnq3uHzgZU6uP literal 0 HcmV?d00001 diff --git a/doc/img/ChannelWindow_top.png b/doc/img/ChannelWindow_top.png new file mode 100644 index 0000000000000000000000000000000000000000..31d84ece0ef47cab515cf8a6073e5d60ff9a7d78 GIT binary patch literal 9647 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 literal 0 HcmV?d00001 diff --git a/doc/img/ChannelWindow_top.xcf b/doc/img/ChannelWindow_top.xcf new file mode 100644 index 0000000000000000000000000000000000000000..91e1fedbc16926cf07207557b858d57ea4cd7c1c GIT binary patch literal 35027 zcmeHQdt6l2_CIrG7@j_}yjO2VRyXVhVVP3DvYq*}YrNl}D2_0^6fnTIHr&**GEGa% z%ou!>Y5uajYLDynk*UF;Wj@MB8mLGhFd8tz%%B{OG_C%;_l=O-%Eh+QWMdJr9bt`&Y3w`@0p!BIW=*Rn^#hDLgwVmEKe^9yt9%g>O6g=e}e}>_QV@x z5B8M)VfKvdDY}HzjAYNjzM#!)qH)E0YDQ{~XKF@5iar}XGLw?B^*NqWd4jy8pKFUrs4!Y9lK zNN7hFS1tnx0~PXL6f$-TmdC>rk}PBMWZ6R@cT>ncK!*BMYXBdH4DE-O*CcxR{1(t8f};{_y@&5$XnGxU>jI_WZUQgxG4b=jUXrRg-+PdTIhUNz>fXu9Q@07~B_9tMH-~hvp%LBF_&#P?}LU$ODsR zyln4F`v@vW(h6Ft1>H$-fuux{ih{^(D66SP%6DyoTJS@=JI{A__{isTmd>vgoTxLD zQ`X%_=u7*Wz~V?+f&QQ|(S6V&lM~2V6Fmm40$DWK5A9NefQkytqF~!)YNA^~-vw;} zquNw|>ia^IsgS8!ldXu!TA}VR*oz9_1M}Pk>tT~=BNMR@tDtV9$#m=xo`C6em)3-B z(!!bTg+*{7jwZ9__ca+*^>8`L%hL+i`hRet{9!xN9&k-AmP1_su>o|ei^b{!tpkVJRdkD&^ItbpI*=Lc_f@H>wc;E4FzRq+ z4{Ru}+x@rwbO7pbWj!rd)l9U_r<-}T>TOSX^vku|hBBRMoG8u225N=+Q9I3*J4W1S znBb<9A40gQxCc+uXTkj#?<*}xooFGFXL*|!B3zIZhckPyN)|^K@O#Y}y95rmlN>K{ zyuuJa9X=qtjA3?{G>-h4?(#a<-{-iIMaL7BRP zemj#m66$YEFb^+4dYaEie3}p7GA<-YCh(A-X}Hv=J^j$siIy1EG-yvhNNwdh$lB4r z)m+D~lW-m2_dY`(^?Q7n?-PxOr^NT$o9lE#?kgYiyx{9^w@2j+KsgV@ZZF7o>7i_d zhq5DS1=|{9zS;;+JR>lSby_3MgC`-#b$LePhs##w8$m|hmIvh|Ny=Sg%v-(jL!Jvf zB~N3t=k3CxT*+V0{AiwNY}l9w-b_~+>4$2;mT&wJp2h6Epo4Wa)73^X544p`qaQd+ z^+P*X%k5OQA2^e-InDUb3=Zl6=FrIMw1Tsw%h<^JM5+enhHL@2mO_@<9%FMdTU62< ziN!T1K6fqpxwiD1QJna6KMN#dB5O$91p57>&V6UmpO|h-p>b-#UUr&i52!u(i1yCu z^flDAg5B?1k=iP9HC&x=9@K`7y?E(By5}+1KTWmmWV-GD=-trQ8|mHbN^H9&eG`3z z>9tFtybF}Cbz=tmJr&Tjne61(`mOl6?#jMcY<2T1R6NO>5}?kuI>}SN&8t&y#~CD{ z=ev!%gUs+7uxBC5>$8<3|AO0x+tKRq0lC*$R<741j{J*tuQ#~9isL4Ry%QMT(Vyer zIQ|bue~x20PU1M5VITfL>9dyU9YlUSz7(8|!=$7#J`J~aOOumeKwk>JkW>`>TAXgt z7YT|-uHa#O6jDpuE0=qe`BLz0aeL)!yG36mnL)V&$PA1`3wnf&xWRq5(6W(ZquiGA zc#Uo?XcLYkCWHG`EPvCH!O_FimhwtchcX5Xv3y0(_Xe0i{u*E9e;I zkkJK#SE##y-9>@Upu2)DV{=l_G4V>C;TIAN_^(l!mL6VwGsWr8|7452QGdELfq!}^q| zgBjGl?KVnm^bPI@;pJ9)jS3rOJSVcMj%e$3j1<_Bk}bjgg7@Up{Jv>f2y7^p?o3Px zzj)nB2n@fNwTUSW?IcX;Hip1F|BR#0519?|=Y;3uat5HB2cl=7Y`;DP<}BFP=Zi*r z{yKQ%WAy3+CmsaT3J|(lleapbhrmRz<2Mf}7-j?pBiZR1wUOonBLK}ABs>cQjf}SZ zbsvDo2M}42bO^*i5R7RM{xV9sk?HjiY|CH09xFfthS0Rq$(;oX(K&W{Ucq(uH2NunInQpUNIGRv3G=L~Nyk^VI2Elnw?vv94~ z>!<2|dAU1lll`8v@Va{jQ!L+7(^D?}W~HL)Bv^;ji#=HrZ42of)=0}|^r?Aeb*6Z% z3p)H?^dmTdfqB$!rRBlhd+RLMdP8?%_PaC(`g1uQ$@O@yKgaaip5}_+ZoPDtx}Io& z{+$SMu53J;vrr$rzouq?#EmXW!77@PvCg&v$dWURa643co)OO3jNSn-?ezAsqb zgJ~S`1ppimzRVCm9X=r6KZ4os592tIBQExE+|T{)U&izYPICO6V?D#ax^cvn5{|z< z#PLavFED&i#xYE9_P|#@{BK3}ctg0$=p`kUu{YeCUXVHh?*>8!by27bl8Qnd9fr5L zuIByU!DI}{2rl65GfHgq4eq~6;idK&6*kHpb^6q227Lq_Bp)*2;?Ided*uv3IS<4^CuMt6&d|tAWQHO_ECwI% zGRk&&FICg<7VfLSC%PQ&YGgQadFFo|b5X>Jq=?oY>R@nKsc*WqKBPK zuoFtMVoACmYL^0DwgS_SBE+UphiDQ6u~5Rj7fmfrLVcm>5bUfHr3`2=M<wvNmL=*deXO8|20aMp7dN<-LaQk8jITxxOzn%rsr8IXp|MrFnNN$U#$1 z)!7yjd6u@BQ{g62#qscwM#aI8_`Twd3n(1Ee6Ou3KVse`VMXoXTUn;7T8t(#>Da$gj%~ zF+c^rCh&!%GJ&ndNe(#OO5MOA=nzQCHnl_^BE2SbGl6f5+bdri_##(=SCnn8yMwtF zj6@51gpD}9_G{V5u~EkI$X!}n2gclQHZnMpm<;YUuygz7GYLlz6PCv|wYH89;b24X zuGB)p<)kz0f5&ZX7^i9Lt3!O_)g}%$q?2&4+ZYgg>>=nZ`H*=Qf37?>O3nb3^FVxT zyllTdAm%DqKQtQifFT&+O&o-zP;wC(@{I5gWeAz9H|D_(Fs|eq1+Ksz?|kEGS#uIB z>x@RBQMKnAVNW+Ns}bm07as5eIZu}1jUX>^BMI2Ve+h!QLa+-O1#jdSx(FIcmxEUL zVvYix#4-@wbI3?BRospg2DkcS84WB97%-Bt`I}Q&Z_aU=5?kodJIm=?tYOwqE-(># zG~{X|lc7g@mFkB$lr`H0u0W5DzT8jNphvrx#0rh7{ryeQqr5Ei;-6h8ox)m7J!KjC zk(aoU)V%EqqS2I}b+$!Dp22NiWZWEQkc6Q;9)*g7Yw`PNDCa{@^84n=H0Jvx=TA+1 zKp8fa@jICvoI{e*b2=ALmzozjB@PKtDUvpXknU07oB&Ps%u)>p=|LfuBiy zBkwlFxLkqTKwY;b#&w(PYRtb%Z?@1p3i)8!8xC&rndV574Bo3kdNVAriIi=_?EC6+Jbzkt$YjPTW#W7k988hbsJ+|PrVPFB_A@U;?Ievw#ylS zavq3J9g^)$sX=4ROO8=BjcK_8fk8sjNMP7oKH1)0F)te}EK)5!A>rAa?w=(Zy({r__-XDk*3&M6!K`iRZ-=GqVYWZyp=j!p>Bn0A~?P zEGWVG7tZu{?>_WXx{K-52$h=$GsSY}i~_Uem*t*#;uaIFPPe+qbGNOl3;qIIaXfRX zQE5)J2yTUdW8{MzrOp8T8K&c>!v|!N$L!=Yj=LB>JDnrX9}&&;5y>2R{)kt({w~Ll z89vAN)IZ1f+&@>x>?55y_GQ=tqUG@qVK{u#7*1^hy=gc?8LoH=ND=s&z}I9GOsxtU zfv-g1Zl!K`a0#ZbaEgM}n6EL9FvJqvYF9*O{TZcg;Yz#4g#$*UwfsCX`k=GwaLpk$q zB;yEUGPoBDQ2wSPhNFrJ%VT}5t)mHPYfj>%_O}C(dlc{D&P3!=pgp$|IK$4nzLX_&y;^(y7J>J4ZdZq`A^U=Or=e12hNFq!>&_t&O4wz_%*w&j|%(|Rhu~H;7-ChZ)5N<kR(~0rlc1)*^MpT2Q3M;< zph5`i(OnuY)o(Nv9x_P^wBwc#+95>@WrX(JBqXR68V1ca(etcVs76g?xn6yI!I0BZ zhO1z27Xf}vun%_NVH1G|H_FYsO+kX1a>q`$Ajs3LtqTI5&+=))3uQ^s1-wi+WqHx1 z9Qi#E&FyGaa>oeHLnEJO}SAx`)}L9^v>5$6$`}9OZgmWqLIKUKjl_*LQF{ z!0`k_;Eo=fh2HdGDcwA3qKu^UWWFZwg>>@>3dXtQ1ili(x|OFzUuucJM3yKaWy%b8)D>uwDjYf0H=j15Mj1wFz>99dG!Mh-`~wxY)J!3poDtKyk#v)RqrNC-6lyb|lN0$9Ip+`O=B zZPdc`By|{_4=?a!U9S$UH$Z+J&(}MJSig37qn7SCbD~qQ^$mxX;1pzC@6aYX1zN+_ zz0S`v(IpTs)9#~l36QRLNYof-e@PnlM3nOc>V^F>1pB2Goh3AO78kLzB+=1Xk|uYS zyI}v+Dq3pKy{Zw!H;T(Xze6L`-|<;l$s!S=iO@W`AKeA>r|_qiRS%uY5t5Ge)voyn z3VyU+d)JX9R#5I@JmVs@P_cA_uvaGt_gr)FpuR5k_Xes`fpI_2QvNy6{L zMB!SUw>qN7Lv)d+!FQ;&OGI-UP4Ob!v!Ro-P zcF)#J&l0j~J9^o4bhJq7ldU?Ea#S1MV4=3^f2d`*aU1t4+!mk7A*47r?9mB6&Xmp%WxsOPWSD;JC=(!c^$O~07VA$%Up?kwA*q#XO zw4296e=Vhrjw34OFvn{+a@S7228B|N!uYRu3bH4__}8X=1(V~ewAa^#IR)A$;@k@Q*cTjkA#}+ghX$sQ%iW2pWZ%n#oPTd`$-ifO``f34WSl5Dx113Dsf!1< zeo4MrOUTS(vfwxwwf$oG!LX5Kw&J)^n@fHx7(*5m?l~!veKA)O$hn>51RN205g{wt4O~~>>$@|QFLPBV;p+Zemm&}!0$=R8NT*xM*Jbm#|a9^`Ko{l2q zW2JlS&XdPWizN3-cy^DV6AWiu$RCZKvoC<>7n{Rq7$IMONxoV~$UNz?kA6IYtYn_2 z77!9f0}ZD&?joA(PHl6mI&8L>@o--SQIb^Owth-8IR)?eAxeoMQh(*TU2pjg=C3ZapmaT*wh)Rv2%}Jkt|crAI*+k zcKX8UWwF^u(UShpiaC+V9~GBgJlMuH!-c`NP2K^&c&SI;PfMCNeEhMJ~BNtW?D?>^dn}pObiTr ztE9YO^*gHz%1hqz#Q>KWP@BBvXnA${(JjdVzQhKRF44a(Soh-87j?n@{$PQKm-~dl z81HiTft|smt1r9yvQy4W={^^T3S%Y^vhW+)?FN#oPfa?;QV79yz&-sOSH3AX!ha5! zlaZd01B=5N^Fg_YOS&q?0znr-V43I^bB>AC^zvI{Q?}}2CjK*J?9xhlnHQKk-Z`cw zE;>5RhY+8%RTs2n_MfI{qW4L9ex<$Y{R0j9Xnn(h_p9uem$6bgXTL~@9;;8*kB?aq z6~$~{)*RTqd(WQT$IbJi)lo%~F=J^JZJ;zUDv<(KE{Xng^k}dOdsJY?lgop`OMOxLcC61Mxb?NY|9W|TBh!KC}mrqYd@8lh& z$zpg_%-pwEF56_UUJ@-?Q{vRRhWLo^hzV-lZ*dWlE&D)mW_Wy|<%{_6@OP_cC82lH z_R=IV%s!#O$!_^2jhT9cl}N(8i@UtiHq(inAo!?>`}uyN}6-^PSVwiovor-z1bZlHCW#ugsaJG|8)RiO*c%$ifPAtFRF zPKHahH&n%i#F<5UuHlPVR&-kFq0Epmo4+rp*;f8}gu`7EQvIgckhykjDD&1Q6hJ4B z4;c@g{Cxtqg}!t3)WVpsv2ok$O45Q8vEhl^E=>|+uf@bJGaEuf#o#(-4O?!75gr#j z4o7%J7%v+fYB+MW^6>s2%ymnHf)da_VO!02nw{38=9hxiLFXlt{9Cql;^F1lNFXDPFL_Mot5WD;U&Gzp03lQB1E@ zep9Juid0PMuKKdbPXf=(b0+7shVhe>Lhiv6-}i(Vmju_`YOk!rmu5vUejgF>;(7S3)k1ZI6%DSre|Uvw79W_vn7$ZpPbgKx@AqGa zm&9no{IIZH7qS^~hpj$uK=!X08hoGYPX6!;om9L(o^fX})ULz-RrsP#dTr}YD!gKH zk4)o?IV2VxYCy23M zF%bUpc%WUEpvIT0m>CfMc6@-$gW(;kJGtrwq}IQY@ZuX+cbFV%C7H$7uadDTfAw%o zBEd14a8h+msICd+_y*NAp}JP6PO(B6G$SeOFx>`;??e^GfGq#?)Ei_qBsd&D0Z@VB zK0=o;DLE^1YDOaFb*4?f*d9FEy6Kl+E<1@JEO3$~MHgt8ZNU=^U-_QnF^<16#7{@J zfxLj1Tks3ZUr@&J8pBsyIsTdBAdYf9qq!c-F_q&?j<0cC$?+48UopJySGTm;uW;L6 zZ@SZ;Hrv78@p{u8?l{#iKzf?@2mZOi0bG6pkpF_kF=jo&^^^4pIjNZ$o`Je->9@>$ z=p9qu0B-j}(mcb?Interface +The top and bottom bars of the channel window are described [here](../../../sdrgui/channel/readme.md) + ![Beam steering CW plugin GUI](../../../doc/img/BeamsteeringCWMod_plugin.png)

1: Channel output

diff --git a/plugins/channelmimo/interferometer/readme.md b/plugins/channelmimo/interferometer/readme.md index d76e038ec..ec8a67c3b 100644 --- a/plugins/channelmimo/interferometer/readme.md +++ b/plugins/channelmimo/interferometer/readme.md @@ -6,6 +6,8 @@ This MIMO reception only (MI) plugin can be used to study phase difference betwe

Interface

+The top and bottom bars of the channel window are described [here](../../../sdrgui/channel/readme.md) + ![Interferometer plugin GUI](../../../doc/img/Interferometer_plugin.png) The interface is divided in 3 sections that will be detailed next: diff --git a/plugins/channelrx/chanalyzer/readme.md b/plugins/channelrx/chanalyzer/readme.md index 954f0e15b..d46aafd7c 100644 --- a/plugins/channelrx/chanalyzer/readme.md +++ b/plugins/channelrx/chanalyzer/readme.md @@ -24,6 +24,8 @@ The same waveforms can be used to trigger the scope trace

B. General interface

+The top and bottom bars of the channel window are described [here](../../../sdrgui/channel/readme.md) + ![Channel Analyzer NG plugin GUI](../../../doc/img/ChAnalyzerNG_plugin.png) The interface is essentially divided in the following sections diff --git a/plugins/channelrx/demodadsb/readme.md b/plugins/channelrx/demodadsb/readme.md index 8237c0d2f..15fd1f990 100644 --- a/plugins/channelrx/demodadsb/readme.md +++ b/plugins/channelrx/demodadsb/readme.md @@ -16,6 +16,8 @@ The ADS-B plugin can send aicraft for display on the [Map Feature](../../feature

Interface

+The top and bottom bars of the channel window are described [here](../../../sdrgui/channel/readme.md) + ![ADS-B Demodulator plugin settings](../../../doc/img/ADSBDemod_plugin_settings.png)

1: Frequency shift from center frequency of reception value

diff --git a/plugins/channelrx/demodais/readme.md b/plugins/channelrx/demodais/readme.md index cb9104c90..f0b479a5d 100644 --- a/plugins/channelrx/demodais/readme.md +++ b/plugins/channelrx/demodais/readme.md @@ -16,6 +16,8 @@ The AIS specification is ITU-R M.1371-5: https://www.itu.int/dms_pubrec/itu-r/re

Interface

+The top and bottom bars of the channel window are described [here](../../../sdrgui/channel/readme.md) + ![AIS Demodulator plugin GUI](../../../doc/img/AISDemod_plugin.png)

1: Frequency shift from center frequency of reception

diff --git a/plugins/channelrx/demodam/readme.md b/plugins/channelrx/demodam/readme.md index 418dcce9b..ecd202c03 100644 --- a/plugins/channelrx/demodam/readme.md +++ b/plugins/channelrx/demodam/readme.md @@ -6,6 +6,8 @@ This plugin can be used to listen to a narrowband amplitude modulated signal. "N

Interface

+The top and bottom bars of the channel window are described [here](../../../sdrgui/channel/readme.md) + ![AM Demodulator plugin GUI](../../../doc/img/AMDemod_plugin.png)

1: Frequency shift from center frequency of reception

@@ -40,7 +42,7 @@ If you right click on it it will open a dialog to select the audio output device

7:Bandpass boxcar filter toggle

-Use this button to enable or disable the bandpass boxcar (sharp) filter with low cutoff at 300 Hz and high cutoff at half the RF bandwidth. This may help readability of low signals on air traffic communications but degrades audio on comfortable AM broadcast transmissions. +Use this button to enable or disable the bandpass boxcar (sharp) filter with low cutoff at 300 Hz and high cutoff at half the RF bandwidth. This may help readability of low signals on air traffic communications but degrades audio on comfortable AM broadcast transmissions.

8: RF bandwidth

diff --git a/plugins/channelrx/demodapt/readme.md b/plugins/channelrx/demodapt/readme.md index 0a6fcaee0..dc7f5c27c 100644 --- a/plugins/channelrx/demodapt/readme.md +++ b/plugins/channelrx/demodapt/readme.md @@ -18,6 +18,8 @@ The status of the NOAA POES satellites is available at: https://www.ospo.noaa.go

Interface

+The top and bottom bars of the channel window are described [here](../../../sdrgui/channel/readme.md) + ![APT Demodulator plugin GUI](../../../doc/img/APTDemod_plugin_settings.png)

1: Frequency shift from center frequency of reception

@@ -69,7 +71,7 @@ This includes: - Whether to automatically save images on LOS. - Whether a combined image including telemetry should be saved. - Whether separate images of channel A and B, without telemetry, should be saved. - - Whether equidistant cylindrical (plate carrée) project images used for the map, should be saved. + - Whether equidistant cylindrical (plate carr�e) project images used for the map, should be saved. - Path to save automatically saved images in. - The minimum number of scanlines required to be in an image, after noise cropping, for it to be automatically saved. - After how many scanlines image processing is applied and updates sent to the map. Lower values require more CPU power. diff --git a/plugins/channelrx/demodatv/readme.md b/plugins/channelrx/demodatv/readme.md index 2419d67da..50119b740 100644 --- a/plugins/channelrx/demodatv/readme.md +++ b/plugins/channelrx/demodatv/readme.md @@ -10,6 +10,8 @@ Experimental modes with smaller number of lines and FPS values can be used in co

Interface

+The top and bottom bars of the channel window are described [here](../../../sdrgui/channel/readme.md) + ![ATV Demodulator plugin GUI](../../../doc/img/ATVDemod_plugin.png) The interface is divided into three collapsable sections: diff --git a/plugins/channelrx/demodbfm/readme.md b/plugins/channelrx/demodbfm/readme.md index 5bff97055..90b055e0b 100644 --- a/plugins/channelrx/demodbfm/readme.md +++ b/plugins/channelrx/demodbfm/readme.md @@ -6,6 +6,8 @@ This plugin can be used to listen to a Broadcast FM modulated signal (BFM). In a

Interface

+The top and bottom bars of the channel window are described [here](../../../sdrgui/channel/readme.md) + ![BFM Demodulator plugin GUI](../../../doc/img/BFMDemod_plugin.png) The main three areas are: diff --git a/plugins/channelrx/demodchirpchat/readme.md b/plugins/channelrx/demodchirpchat/readme.md index d4bd871e3..80916e765 100644 --- a/plugins/channelrx/demodchirpchat/readme.md +++ b/plugins/channelrx/demodchirpchat/readme.md @@ -23,6 +23,8 @@ Note: this plugin is officially supported since version 6.

Interface

+The top and bottom bars of the channel window are described [here](../../../sdrgui/channel/readme.md) + ![ChirpChat Demodulator plugin GUI](../../../doc/img/ChirpChatDemod_plugin.png)

1: Frequency shift from center frequency of reception

diff --git a/plugins/channelrx/demoddab/readme.md b/plugins/channelrx/demoddab/readme.md index a3085c986..49518887d 100644 --- a/plugins/channelrx/demoddab/readme.md +++ b/plugins/channelrx/demoddab/readme.md @@ -8,6 +8,8 @@ The DAB demodulator uses a sample rate of 2.048MHz.

Interface

+The top and bottom bars of the channel window are described [here](../../../sdrgui/channel/readme.md) + ![DAB Demodulator plugin GUI](../../../doc/img/DABDemod_plugin.png)

1: Frequency shift from center frequency of reception

diff --git a/plugins/channelrx/demoddatv/readme.md b/plugins/channelrx/demoddatv/readme.md index d6ab075b5..52d07cd76 100644 --- a/plugins/channelrx/demoddatv/readme.md +++ b/plugins/channelrx/demoddatv/readme.md @@ -18,6 +18,8 @@ The whole bandwidth available to the channel is used. That is it runs at the dev

Interface

+The top and bottom bars of the channel window are described [here](../../../sdrgui/channel/readme.md) + ![DATV Demodulator plugin GUI](../../../doc/img/DATVDemod_plugin.png)

A: RF settings

diff --git a/plugins/channelrx/demoddsd/readme.md b/plugins/channelrx/demoddsd/readme.md index e4ca43dff..7c8712eaa 100644 --- a/plugins/channelrx/demoddsd/readme.md +++ b/plugins/channelrx/demoddsd/readme.md @@ -52,6 +52,8 @@ For software built from source if you choose to have `mbelib` support you will n

Interface

+The top and bottom bars of the channel window are described [here](../../../sdrgui/channel/readme.md) + ![DSD Demodulator plugin GUI](../../../doc/img/DSDdemod_plugin.png)

A section: settings

diff --git a/plugins/channelrx/demodfreedv/readme.md b/plugins/channelrx/demodfreedv/readme.md index 4d8d13f40..afaac4609 100644 --- a/plugins/channelrx/demodfreedv/readme.md +++ b/plugins/channelrx/demodfreedv/readme.md @@ -8,6 +8,8 @@ This plugin can be used to demodulate a signal following the [FreeDV digital voi

Interface

+The top and bottom bars of the channel window are described [here](../../../sdrgui/channel/readme.md) + ![FreeDV Demodulator plugin GUI](../../../doc/img/FreeDVDemod_plugin.png)

1: Frequency shift from center frequency of reception

@@ -21,7 +23,7 @@ Average total power in dB relative to a +/- 1.0 amplitude signal received in the

3: Manual re-synchronization

This works only for the presently disabled 700D mode. Use this push button to force loosing and re-acquiring synchronisation. - +

4: FreeDV mode

Use this combo box to select which FreeDV standard is used. The following are supported: @@ -30,7 +32,7 @@ Use this combo box to select which FreeDV standard is used. The following are su - `1600`: OFDM (16 QPSK carriers) narrowband (1.4 kHz) with 700 b/s compressed voice - `800XA`: FSK-4 narrowband (2 kHz) with 700 b/s compressed voice - `700C`: Another OFDM (14 QPSK carriers) narrowband (1.5 kHz) mode with 700 b/s compressed voice - +

5: Level meter in dB

- top bar (green): average value @@ -86,7 +88,7 @@ The transmitted signal is further decimated by a power of two before being appli - 4: 6 kHz (2400A) or 1 kHz (other modes) - 8: 3 kHz (2400A) or 0.5 kHz (other modes) - 16: 1.5 kHz (2400A) or 0.25 kHz (other modes) - +

9: Spectrum display

This is the spectrum display of the analog signal that enters the modem before AGC and volume control. Controls on the bottom of the panel are identical to the ones of the main spectrum display. It should be optimally centered using the frequency offset control (1) depending on the expected FreeDV mode: diff --git a/plugins/channelrx/demodnfm/readme.md b/plugins/channelrx/demodnfm/readme.md index 8307070ce..f32ec1005 100644 --- a/plugins/channelrx/demodnfm/readme.md +++ b/plugins/channelrx/demodnfm/readme.md @@ -6,6 +6,8 @@ This plugin can be used to listen to a narrowband FM modulated signal. "Narrowba

Interface

+The top and bottom bars of the channel window are described [here](../../../sdrgui/channel/readme.md) + ![NFM Demodulator plugin GUI](../../../doc/img/NFMdemod_plugin.png)

1: Frequency shift from center frequency of reception value

diff --git a/plugins/channelrx/demodpacket/readme.md b/plugins/channelrx/demodpacket/readme.md index 0d71dbdd6..2c00e91f1 100644 --- a/plugins/channelrx/demodpacket/readme.md +++ b/plugins/channelrx/demodpacket/readme.md @@ -6,6 +6,8 @@ This plugin can be used to demodulate packet radio (APRS/AX.25) data packets. Re

Interface

+The top and bottom bars of the channel window are described [here](../../../sdrgui/channel/readme.md) + ![Packet Demodulator plugin GUI](../../../doc/img/PacketDemod_plugin.png)

1: Frequency shift from center frequency of reception

diff --git a/plugins/channelrx/demodpager/readme.md b/plugins/channelrx/demodpager/readme.md index 2292113d3..e4409fe51 100644 --- a/plugins/channelrx/demodpager/readme.md +++ b/plugins/channelrx/demodpager/readme.md @@ -6,6 +6,8 @@ This plugin can be used to demodulate POCSAG pager messages.

Interface

+The top and bottom bars of the channel window are described [here](../../../sdrgui/channel/readme.md) + ![Pager Demodulator plugin GUI](../../../doc/img/PagerDemod_plugin.png)

1: Frequency shift from center frequency of reception

diff --git a/plugins/channelrx/demodradiosonde/readme.md b/plugins/channelrx/demodradiosonde/readme.md index 5254b227b..f9941962a 100644 --- a/plugins/channelrx/demodradiosonde/readme.md +++ b/plugins/channelrx/demodradiosonde/readme.md @@ -4,12 +4,14 @@ This plugin can be used to demodulate RS41 radiosonde weather balloon signals. Radiosondes typically transmit on 400-406MHz and are in the sky around the world for around 1 hour around 00:00 UTC. -RS41 radiosondes transmit data frames every second, containing position, velocity and PTU (Pressure, Temperature and Humidity) readings. The radios use GFSK modulation, with ±2.4kHz deviation at 4,800 baud. Reed Solomon encoding is used for ECC (Error Checking and Correction). +RS41 radiosondes transmit data frames every second, containing position, velocity and PTU (Pressure, Temperature and Humidity) readings. The radios use GFSK modulation, with �2.4kHz deviation at 4,800 baud. Reed Solomon encoding is used for ECC (Error Checking and Correction). The Radiosonde demodulator can forward received data to the [Radiosone feature](../../feature/radiosonde/readme.md), which can plot charts showing how altitude and PTU vary over time, and also plot the position of the radiosonde on the 2D and 3D maps.

Interface

+The top and bottom bars of the channel window are described [here](../../../sdrgui/channel/readme.md) + ![Radiosonde Demodulator plugin GUI](../../../doc/img/RadiosondeDemod_plugin.png)

1: Frequency shift from center frequency of reception

@@ -81,18 +83,18 @@ The received frames table displays information about each radiosonde frame recei * Serial - The serial number of the radiosonde. Double clicking on this column will search for the radiosone on https://sondehub.org/ * Frame - Frame number * Phase - Flight phase: On ground, Ascent and Descent. -* Lat (°) - Latitude in degrees, North positive. Double clicking on this column will search for the radiosonde on the Map. -* Lon (°) - Longitude in degrees, East positive. Double clicking on this column will search for the radiosonde on the Map. +* Lat (�) - Latitude in degrees, North positive. Double clicking on this column will search for the radiosonde on the Map. +* Lon (�) - Longitude in degrees, East positive. Double clicking on this column will search for the radiosonde on the Map. * Alt (m) - Altitude in metres. * Spd (km/h) - Speed over ground in kilometres per hour. * VR (m/s) - Vertical climb rate in metres per second. -* Hdg (°) - Heading in degrees. +* Hdg (�) - Heading in degrees. * P (hPA) - Air pressure in hectopascals. Not all RS41s include a pressure sensor. A value ending with 'U' indicates a uncalibrated estimate and may be inaccurate. -* T (°C) - Air temperature in degrees Celsius. A value ending with 'U' indicates a uncalibrated estimate and may be inaccurate. +* T (�C) - Air temperature in degrees Celsius. A value ending with 'U' indicates a uncalibrated estimate and may be inaccurate. * U (%) - Relative humidity in percent. A value ending with 'U' indicates a uncalibrated estimate and may be inaccurate. * Bat (V) - Battery voltage in Volts. * Bat - Battery status: OK or low. -* PCB (°C) - Temperature of PCB. +* PCB (�C) - Temperature of PCB. * PWM (%) - Humidity sensor heater PWM (Pulse Width Modulation) setting, in percent. * TX (%) - Transmit power in percent. * Max SF - Maximum subframe number. diff --git a/plugins/channelrx/demodssb/readme.md b/plugins/channelrx/demodssb/readme.md index ba07e5e46..8d8c86ab7 100644 --- a/plugins/channelrx/demodssb/readme.md +++ b/plugins/channelrx/demodssb/readme.md @@ -6,6 +6,8 @@ This plugin can be used to listen to a single sideband or double sidebands modul

Interface

+The top and bottom bars of the channel window are described [here](../../../sdrgui/channel/readme.md) + ![SSB Demodulator plugin GUI](../../../doc/img/SSBDemod_plugin.png) ☞ In order to toggle USB or LSB mode in SSB mode you have to set the "BW" in channel filter cutoff control (9) to a positive (USB) or negative (LSB) value. The above screenshot shows a LSB setup. See the (8) to (10) paragraphs below for details. diff --git a/plugins/channelrx/demodvor/readme.md b/plugins/channelrx/demodvor/readme.md index d037d5930..de46fb5e0 100644 --- a/plugins/channelrx/demodvor/readme.md +++ b/plugins/channelrx/demodvor/readme.md @@ -16,6 +16,8 @@ Note that for aircraft, there is typically a direct line-of-sight to the VOR. Th

Interface

+The top and bottom bars of the channel window are described [here](../../../sdrgui/channel/readme.md) + ![VOR Demodulator plugin GUI](../../../doc/img/VORDemod_plugin.png)

1: Level meter in dB

diff --git a/plugins/channelrx/demodvorsc/readme.md b/plugins/channelrx/demodvorsc/readme.md index d929eb957..7604ae07f 100644 --- a/plugins/channelrx/demodvorsc/readme.md +++ b/plugins/channelrx/demodvorsc/readme.md @@ -18,6 +18,8 @@ Several instances of this plugin can be created to monitor multiple VORs and col

Interface

+The top and bottom bars of the channel window are described [here](../../../sdrgui/channel/readme.md) + ![VOR Demodulator plugin GUI](../../../doc/img/VORDemodSC_plugin.png)

1: Frequency shift from center frequency of reception value

diff --git a/plugins/channelrx/demodwfm/readme.md b/plugins/channelrx/demodwfm/readme.md index 67ad663f4..27e3cbb82 100644 --- a/plugins/channelrx/demodwfm/readme.md +++ b/plugins/channelrx/demodwfm/readme.md @@ -6,6 +6,8 @@ This plugin can be used to listen to a wideband or narrowband FM modulated signa

Interface

+The top and bottom bars of the channel window are described [here](../../../sdrgui/channel/readme.md) + ![WFM Demodulator plugin GUI](../../../doc/img/WFMDemod_plugin.png)

1: Frequency shift from center frequency of reception

diff --git a/plugins/channelrx/filesink/readme.md b/plugins/channelrx/filesink/readme.md index e1c4b4e25..9e8089aeb 100644 --- a/plugins/channelrx/filesink/readme.md +++ b/plugins/channelrx/filesink/readme.md @@ -18,6 +18,8 @@ If a filename is given with an extension different of `.sdriq` or `.wav` then th

Interface

+The top and bottom bars of the channel window are described [here](../../../sdrgui/channel/readme.md) + ![File Sink plugin GUI](../../../doc/img/FileSink_plugin.png)

1: Frequency shift from center frequency of reception

diff --git a/plugins/channelrx/freqtracker/readme.md b/plugins/channelrx/freqtracker/readme.md index 6457b9bb3..fbca962d7 100644 --- a/plugins/channelrx/freqtracker/readme.md +++ b/plugins/channelrx/freqtracker/readme.md @@ -8,6 +8,8 @@ This plugin can be used to track the center frequency of a carrier. It will try

Interface

+The top and bottom bars of the channel window are described [here](../../../sdrgui/channel/readme.md) + ![Frequency Tracker plugin GUI](../../../doc/img/FreqTracker_plugin.png)

1: Frequency shift from center frequency of reception

diff --git a/plugins/channelrx/localsink/readme.md b/plugins/channelrx/localsink/readme.md index 9a161ef75..498cd2d75 100644 --- a/plugins/channelrx/localsink/readme.md +++ b/plugins/channelrx/localsink/readme.md @@ -16,6 +16,8 @@ Note that because it uses only the channelizer half band filter chain to achieve

Interface

+The top and bottom bars of the channel window are described [here](../../../sdrgui/channel/readme.md) + ![Local sink channel plugin GUI](../../../doc/img/LocalSink.png)

1: Decimation factor

diff --git a/plugins/channelrx/noisefigure/readme.md b/plugins/channelrx/noisefigure/readme.md index b204906e6..1bfb50e03 100644 --- a/plugins/channelrx/noisefigure/readme.md +++ b/plugins/channelrx/noisefigure/readme.md @@ -10,6 +10,8 @@ The noise figure will vary with frequency and gain settings. Typically, the nois

Interface

+The top and bottom bars of the channel window are described [here](../../../sdrgui/channel/readme.md) + ![Noise figure plugin GUI](../../../doc/img/NoiseFigure_plugin.png)

1: Measurement frequency shift from center

diff --git a/plugins/channelrx/radioastronomy/readme.md b/plugins/channelrx/radioastronomy/readme.md index f9756d2a5..a63583f95 100644 --- a/plugins/channelrx/radioastronomy/readme.md +++ b/plugins/channelrx/radioastronomy/readme.md @@ -90,6 +90,8 @@ Where:

1: Settings

+The top and bottom bars of the channel window are described [here](../../../sdrgui/channel/readme.md) + ![Settings GUI](../../../doc/img/RadioAstronomy_Settings.png)

1.1: Frequency shift from center frequency of reception

diff --git a/plugins/channelrx/radioclock/readme.md b/plugins/channelrx/radioclock/readme.md index 58c7f7755..987d03032 100644 --- a/plugins/channelrx/radioclock/readme.md +++ b/plugins/channelrx/radioclock/readme.md @@ -17,6 +17,8 @@ Although the atomic clocks used to transmit the timecode are extremely accurate,

Interface

+The top and bottom bars of the channel window are described [here](../../../sdrgui/channel/readme.md) + ![Radio clock plugin GUI](../../../doc/img/RadioClock_plugin.png)

1: Frequency shift from center frequency of reception

diff --git a/plugins/channelrx/remotesink/readme.md b/plugins/channelrx/remotesink/readme.md index aa7509aa7..4aeb8c8ce 100644 --- a/plugins/channelrx/remotesink/readme.md +++ b/plugins/channelrx/remotesink/readme.md @@ -10,6 +10,8 @@ The plugin will be built only if the [CM256cc library](https://github.com/f4exb/

Interface

+The top and bottom bars of the channel window are described [here](../../../sdrgui/channel/readme.md) + ![Remote sink channel plugin GUI](../../../doc/img/RemoteSink.png)

1: Decimation factor

diff --git a/plugins/channelrx/sigmffilesink/readme.md b/plugins/channelrx/sigmffilesink/readme.md index f5e1690ef..07aa90a39 100644 --- a/plugins/channelrx/sigmffilesink/readme.md +++ b/plugins/channelrx/sigmffilesink/readme.md @@ -19,6 +19,8 @@ It adds a dependency to the [libsigmf library](https://github.com/f4exb/libsigmf

Interface

+The top and bottom bars of the channel window are described [here](../../../sdrgui/channel/readme.md) + ![SigMF File Sink plugin GUI](../../../doc/img/SigMFFileSink_plugin.png)

1: Frequency shift from center frequency of reception

diff --git a/plugins/channelrx/udpsink/readme.md b/plugins/channelrx/udpsink/readme.md index b98dbe43b..921e6ae9c 100644 --- a/plugins/channelrx/udpsink/readme.md +++ b/plugins/channelrx/udpsink/readme.md @@ -12,6 +12,8 @@ This plugin is available for Linux and Mac O/S only.

Interface

+The top and bottom bars of the channel window are described [here](../../../sdrgui/channel/readme.md) + ![UDP Sink plugin GUI](../../../doc/img/UDPsink_plugin.png)

1: Frequency shift from center frequency of reception

@@ -30,7 +32,7 @@ Total power in dB relative to a +/- 1.0 amplitude signal sent over UDP. These parameters are set with the basic channel settings dialog. See: [here](https://github.com/f4exb/sdrangel/blob/master/sdrgui/readme.md#6-channels) -The display is in the format `address:audio port/data port` +The display is in the format `address:audio port/data port`

5: Signal sample rate

@@ -48,14 +50,14 @@ Left: combo box to specify the type of samples that are sent over UDP: - `LSB Mono`: AF of the LSB part of a SSB demodulated signal as "mono" (I+Q)*0.7 samples that is one sample per demodulator output sample. This can be used with software that accepts mono type of input. - `USB Mono`: AF of the USB part of a SSB demodulated signal as "mono" (I+Q)*0.7 samples that is one sample per demodulator output sample. This can be used with software that accepts mono type of input. - `AM Mono`: AF of the envelope demodulated signal i.e. channel magnitude or sqrt(I² + Q²) as "mono" samples that is one sample per demodulator output sample. This can be used with software that accepts mono type of input. - - `AM !DC Mono`: Same as above but with a DC block based on magnitude average over a 5 ms period - - `AM BPF Mono`: Same as AM Mono but raw magnitude signal is passed through a bandpass filter with lower cutoff at 300 Hz and higher cutoff at RF bandwidth frequency - + - `AM !DC Mono`: Same as above but with a DC block based on magnitude average over a 5 ms period + - `AM BPF Mono`: Same as AM Mono but raw magnitude signal is passed through a bandpass filter with lower cutoff at 300 Hz and higher cutoff at RF bandwidth frequency + Right: Sample size in bits: - `16 bits`: samples are 16 bit signed with little endian layout (S16LE) - `24 bits`: samples are 32 bit signed with little endian layout (S32LE) using only the 3 less significant bytes. This means that the range is -2²³ to 2²³ - 1 - +

7: Signal bandwidth

The signal is bandpass filtered to this bandwidth (zero frequency centered) before being sent out as raw I/Q samples or before being demodulated for SSB and FM outputs. Thus a 20000 Hz bandwidth for example means +/-10000 Hz around center channel frequency. @@ -64,7 +66,7 @@ When SSB formats are used only the lower half (LSB) or upper half (USB) of the b

8: FM deviation

-This is the maximum expected FM deviation in Hz for NFM demodulated samples. Therefore it is active only for `NFM` types of sample formats. A positive deviation of this amount from the central carrier will result in a sample output value of 32767 (0x7FFF) corresponding to a +1.0 real value. A negative deviation of this amount from the central carrier will result in a sample output value of -32768 (0x8000) corresponding to a -1.0 real value. +This is the maximum expected FM deviation in Hz for NFM demodulated samples. Therefore it is active only for `NFM` types of sample formats. A positive deviation of this amount from the central carrier will result in a sample output value of 32767 (0x7FFF) corresponding to a +1.0 real value. A negative deviation of this amount from the central carrier will result in a sample output value of -32768 (0x8000) corresponding to a -1.0 real value.

9: AGC and audio feedback control

@@ -72,7 +74,7 @@ This is the maximum expected FM deviation in Hz for NFM demodulated samples. The

9.1: Toggle AGC

-It is effective only for AM and SSB. Signal is normalized to +/- 0.5 times the maximum amplitude with a time constant (averaging) of 200 ms. When engaged the squelch gate is fixed at 50 ms. The release time controlled by (15.3) can be increased from the 50 ms default for SSB signals to prevent accidental signal drops due to drops in the voice. +It is effective only for AM and SSB. Signal is normalized to +/- 0.5 times the maximum amplitude with a time constant (averaging) of 200 ms. When engaged the squelch gate is fixed at 50 ms. The release time controlled by (15.3) can be increased from the 50 ms default for SSB signals to prevent accidental signal drops due to drops in the voice.

9.2: Toggle audio feedback

@@ -94,7 +96,7 @@ The changes in the following items only become effective when this button is pre - Audio port (9) - FM deviation (11) -When any item of these items is changed the button is lit in green until it is pressed. +When any item of these items is changed the button is lit in green until it is pressed.

11: Audio volume

@@ -120,11 +122,11 @@ Use the slider to set the squelch power threshold based on channel input power ( Sets the delay after which a signal constantly above the squelch threshold effectively opens the squelch. The same delay is used for squelch release except for SSB where the gate is fixed at 50 ms and this controls the release time only. -The delay in milliseconds is displayed at the right of the button. +The delay in milliseconds is displayed at the right of the button.

14: Spectrum display

-This is the spectrum display of the channel signal after bandpass filtering. Please refer to the Spectrum display description for details. +This is the spectrum display of the channel signal after bandpass filtering. Please refer to the Spectrum display description for details. -This spectrum is centered on the center frequency of the channel (center frequency of reception + channel shift) and is that of a complex signal i.e. there are positive and negative frequencies. The width of the spectrum is proportional of the sample rate. That is for a sample rate of S samples per seconds the spectrum spans from -S/2 to +S/2 Hz. +This spectrum is centered on the center frequency of the channel (center frequency of reception + channel shift) and is that of a complex signal i.e. there are positive and negative frequencies. The width of the spectrum is proportional of the sample rate. That is for a sample rate of S samples per seconds the spectrum spans from -S/2 to +S/2 Hz. diff --git a/plugins/channeltx/filesource/readme.md b/plugins/channeltx/filesource/readme.md index be7ee073d..1173322ec 100644 --- a/plugins/channeltx/filesource/readme.md +++ b/plugins/channeltx/filesource/readme.md @@ -50,6 +50,8 @@ The I/Q samples are copied to the baseband to be transmitted by the sink output

Interface

+The top and bottom bars of the channel window are described [here](../../../sdrgui/channel/readme.md) + ![File source channel plugin GUI](../../../doc/img/FileSource_plugin.png)

1: Interpolation factor

diff --git a/plugins/channeltx/localsource/readme.md b/plugins/channeltx/localsource/readme.md index 1259484da..83447c219 100644 --- a/plugins/channeltx/localsource/readme.md +++ b/plugins/channeltx/localsource/readme.md @@ -10,6 +10,8 @@ Note that because it uses only the channelizer half band filter chain to achieve

Interface

+The top and bottom bars of the channel window are described [here](../../../sdrgui/channel/readme.md) + ![Local source channel plugin GUI](../../../doc/img/LocalSource.png)

1: Interpolation factor

diff --git a/plugins/channeltx/mod802.15.4/readme.md b/plugins/channeltx/mod802.15.4/readme.md index d7deb8aa0..a518a39a2 100644 --- a/plugins/channeltx/mod802.15.4/readme.md +++ b/plugins/channeltx/mod802.15.4/readme.md @@ -6,6 +6,8 @@ This plugin can be used to transmit IEEE 802.15.4 frames. The 802.15.4 PHY & RF

Interface

+The top and bottom bars of the channel window are described [here](../../../sdrgui/channel/readme.md) + ![802.15.4 Modulator plugin GUI](../../../doc/img/IEEE_802_15_4_Mod_plugin.png)

1: Frequency shift from center frequency of transmission

diff --git a/plugins/channeltx/modais/readme.md b/plugins/channeltx/modais/readme.md index 106aa33af..2453ebb0d 100644 --- a/plugins/channeltx/modais/readme.md +++ b/plugins/channeltx/modais/readme.md @@ -8,6 +8,8 @@ You need an AIS license to transmit on the AIS VHF frequencies (161.975MHz and 1

Interface

+The top and bottom bars of the channel window are described [here](../../../sdrgui/channel/readme.md) + ![AIS Modulator plugin GUI](../../../doc/img/AISMod_plugin.png)

1: Frequency shift from center frequency of transmission

diff --git a/plugins/channeltx/modam/readme.md b/plugins/channeltx/modam/readme.md index 10d5e8b01..025cb858a 100644 --- a/plugins/channeltx/modam/readme.md +++ b/plugins/channeltx/modam/readme.md @@ -6,6 +6,8 @@ This plugin can be used to generate a narrowband amplitude modulated signal. "Na

Interface

+The top and bottom bars of the channel window are described [here](../../../sdrgui/channel/readme.md) + ![AM Modulator plugin GUI](../../../doc/img/AMMod_plugin.png)

1: Frequency shift from center frequency of transmission

diff --git a/plugins/channeltx/modatv/readme.md b/plugins/channeltx/modatv/readme.md index 653248cad..50592d913 100644 --- a/plugins/channeltx/modatv/readme.md +++ b/plugins/channeltx/modatv/readme.md @@ -10,6 +10,8 @@ In practice 4 MS/s with about 300 points per line is the lowest sample rate that

Interface

+The top and bottom bars of the channel window are described [here](../../../sdrgui/channel/readme.md) + ![ATV Modulator plugin GUI](../../../doc/img/ATVMod_plugin.png)

1: Frequency shift from center frequency of transmission

diff --git a/plugins/channeltx/modchirpchat/readme.md b/plugins/channeltx/modchirpchat/readme.md index 46dcfefd4..31c8e6698 100644 --- a/plugins/channeltx/modchirpchat/readme.md +++ b/plugins/channeltx/modchirpchat/readme.md @@ -19,6 +19,8 @@ Note: this plugin is officially supported since version 6.

Interface

+The top and bottom bars of the channel window are described [here](../../../sdrgui/channel/readme.md) + ![ChitpChat Modulator plugin GUI](../../../doc/img/ChirpChatMod_plugin.png)

1: Frequency shift from center frequency of reception

diff --git a/plugins/channeltx/moddatv/readme.md b/plugins/channeltx/moddatv/readme.md index 94b598861..1241dcebb 100644 --- a/plugins/channeltx/moddatv/readme.md +++ b/plugins/channeltx/moddatv/readme.md @@ -15,6 +15,8 @@ DVB-S2 includes: scrambling, BCH encoder, LDPC encoder, bit interleaver and QPSK

Interface

+The top and bottom bars of the channel window are described [here](../../../sdrgui/channel/readme.md) + ![DATV Modulator plugin GUI](../../../doc/img/DATVMod_plugin.png)

1: Frequency shift from center frequency of transmission

diff --git a/plugins/channeltx/modfreedv/readme.md b/plugins/channeltx/modfreedv/readme.md index f4ea2cb5d..52dae2126 100644 --- a/plugins/channeltx/modfreedv/readme.md +++ b/plugins/channeltx/modfreedv/readme.md @@ -8,6 +8,8 @@ This plugin can be used to generate a signal following the [FreeDV digital voice

Interface

+The top and bottom bars of the channel window are described [here](../../../sdrgui/channel/readme.md) + ![FreeDV Modulator plugin GUI](../../../doc/img/FreeDVMod_plugin.png)

1: Frequency shift from center frequency of transmission

diff --git a/plugins/channeltx/modnfm/readme.md b/plugins/channeltx/modnfm/readme.md index 3a317f1b5..83c599859 100644 --- a/plugins/channeltx/modnfm/readme.md +++ b/plugins/channeltx/modnfm/readme.md @@ -6,6 +6,8 @@ This plugin can be used to generate a narrowband frequency modulated signal. "Na

Interface

+The top and bottom bars of the channel window are described [here](../../../sdrgui/channel/readme.md) + ![NFM Modulator plugin GUI](../../../doc/img/NFMMod_plugin.png)

1: Frequency shift from center frequency of transmission

diff --git a/plugins/channeltx/modpacket/readme.md b/plugins/channeltx/modpacket/readme.md index f30f0570b..2ae2496fa 100644 --- a/plugins/channeltx/modpacket/readme.md +++ b/plugins/channeltx/modpacket/readme.md @@ -6,6 +6,8 @@ This plugin can be used to modulate packet radio (APRS/AX.25) data packets.

Interface

+The top and bottom bars of the channel window are described [here](../../../sdrgui/channel/readme.md) + ![Packet Modulator plugin GUI](../../../doc/img/PacketMod_plugin.png)

1: Frequency shift from center frequency of transmission

diff --git a/plugins/channeltx/modssb/readme.md b/plugins/channeltx/modssb/readme.md index 11a7c70ca..829618a75 100644 --- a/plugins/channeltx/modssb/readme.md +++ b/plugins/channeltx/modssb/readme.md @@ -6,6 +6,8 @@ This plugin can be used to generate a single sideband or double sidebands modula

Interface

+The top and bottom bars of the channel window are described [here](../../../sdrgui/channel/readme.md) + ![SSB Modulator plugin GUI](../../../doc/img/SSBModulator_plugin.png) ☞ In order to toggle USB or LSB mode in SSB mode you have to set the "BW" in channel filter cutoff control (8) to a positive (USB) or negative (LSB) value. The above screenshot shows a USB setup. See the (7) to (9) paragraphs below for details. diff --git a/plugins/channeltx/modwfm/readme.md b/plugins/channeltx/modwfm/readme.md index ad3f4b0ad..df61d4f61 100644 --- a/plugins/channeltx/modwfm/readme.md +++ b/plugins/channeltx/modwfm/readme.md @@ -6,6 +6,8 @@ This plugin can be used to generate a wideband frequency modulated signal. "Wide

Interface

+The top and bottom bars of the channel window are described [here](../../../sdrgui/channel/readme.md) + ![WFM Modulator plugin GUI](../../../doc/img/WFMMod_plugin.png)

1: Frequency shift from center frequency of transmission

diff --git a/plugins/channeltx/remotesource/readme.md b/plugins/channeltx/remotesource/readme.md index 63b1a246d..ea86b51a8 100644 --- a/plugins/channeltx/remotesource/readme.md +++ b/plugins/channeltx/remotesource/readme.md @@ -10,6 +10,8 @@ The plugin will be built only if the [CM256cc library](https://github.com/f4exb/

Interface

+The top and bottom bars of the channel window are described [here](../../../sdrgui/channel/readme.md) + ![Remote source channel plugin GUI](../../../doc/img/RemoteSource.png)

1: Data local address

diff --git a/plugins/channeltx/udpsource/readme.md b/plugins/channeltx/udpsource/readme.md index 5dbcaedc3..3975fd111 100644 --- a/plugins/channeltx/udpsource/readme.md +++ b/plugins/channeltx/udpsource/readme.md @@ -10,6 +10,8 @@ This plugin is available for Linux and Mac O/S only.

Interface

+The top and bottom bars of the channel window are described [here](../../../sdrgui/channel/readme.md) + ![UDP Source plugin GUI](../../../doc/img/UDPsource_plugin.png)

1: Frequency shift from center frequency of reception

@@ -30,7 +32,7 @@ Use this button to switch off the RF on the channel. The background of the butto

5: UDP address and port

-Enter the network interface address and listening port. The display is in the format `address:data port` +Enter the network interface address and listening port. The display is in the format `address:data port`

6: Join multicast group

@@ -57,7 +59,7 @@ Combo box to specify the type of samples that are received and sent in the chann

10: Mono/Stereo input

This toggles switches between 1 channel (mono) and 2 channels (stereo) input samples format. - +

11: Output signal bandwidth

The signal is bandpass filtered to this bandwidth (zero frequency centered) before being sent out in the channel. In SSB modes only half of the filter is used (LSB: lower, USB: upper). Thus to send a signal with 3000 Hz bandwidth a bandwidth of 6000 Hz must be selected. In addition in SSB modes a 300 Hz highpass filter is applied. @@ -72,9 +74,9 @@ this is the AM percentage modulation when a +/- 1.0 amplitude modulating signal

14: Squelch

-The slider sets the squelch power threshold based on channel input power (2). At the right of the slider the value in dB is displayed. +The slider sets the squelch power threshold based on channel input power (2). At the right of the slider the value in dB is displayed. -The button sets the delay after which a signal constantly above the squelch threshold effectively opens the squelch. The same delay is used for squelch release. The delay in milliseconds is displayed at the right of the button. +The button sets the delay after which a signal constantly above the squelch threshold effectively opens the squelch. The same delay is used for squelch release. The delay in milliseconds is displayed at the right of the button.

15: Input and output Gains

@@ -94,7 +96,7 @@ This gauge shows the percentage of deviation from a R/W pointer distance of half There is an automatic correction to try to maintain the half buffer distance between read and write pointers. This adjust the sample rate and therefore some wiggling around the nominal sample rate can occur. This should be hardly noticeable for most modulations but can be problematic with very narrowband modulations like WSPR. -The buffer consists in 512 bytes frames so that a normalized UDP block can be placed in one frame. Half the number of frames is calculated as the sample rate divided by 375. This results in a fixed average delay 0f 341 ms for sample rates of 48 kS/s and above. +The buffer consists in 512 bytes frames so that a normalized UDP block can be placed in one frame. Half the number of frames is calculated as the sample rate divided by 375. This results in a fixed average delay 0f 341 ms for sample rates of 48 kS/s and above.

18: Reset input buffer R/W pointers

@@ -117,10 +119,10 @@ The changes in the following items only become effective when this button is pre - FM deviation (12) - AM percentage (13) -When any item of these items is changed the button is lit in green until it is pressed. +When any item of these items is changed the button is lit in green until it is pressed.

21: Spectrum display

-This is the spectrum display of the channel signal before filtering. Please refer to the Spectrum display description for details. +This is the spectrum display of the channel signal before filtering. Please refer to the Spectrum display description for details. -This spectrum is centered on the center frequency of the channel (center frequency of reception + channel shift) and is that of a complex signal i.e. there are positive and negative frequencies. The width of the spectrum is proportional of the sample rate. That is for a sample rate of S samples per seconds the spectrum spans from -S/2 to +S/2 Hz. +This spectrum is centered on the center frequency of the channel (center frequency of reception + channel shift) and is that of a complex signal i.e. there are positive and negative frequencies. The width of the spectrum is proportional of the sample rate. That is for a sample rate of S samples per seconds the spectrum spans from -S/2 to +S/2 Hz. diff --git a/sdrgui/channel/channelgui.cpp b/sdrgui/channel/channelgui.cpp index 7b274bf85..50a83d37e 100644 --- a/sdrgui/channel/channelgui.cpp +++ b/sdrgui/channel/channelgui.cpp @@ -329,9 +329,14 @@ void ChannelGUI::setDeviceType(DeviceType type) updateIndexLabel(); } +void ChannelGUI::setDisplayedame(const QString& name) +{ + m_displayedName = name; +} + void ChannelGUI::setToolTip(const QString& tooltip) { - m_indexLabel->setToolTip(tooltip); + m_indexLabel->setToolTip(tr("%1 / %2").arg(tooltip).arg(m_displayedName)); } void ChannelGUI::setIndex(int index) diff --git a/sdrgui/channel/channelgui.h b/sdrgui/channel/channelgui.h index 28a8b7434..6a78c8734 100644 --- a/sdrgui/channel/channelgui.h +++ b/sdrgui/channel/channelgui.h @@ -75,6 +75,7 @@ public: void setTitle(const QString& title); void setTitleColor(const QColor& c); void setDeviceType(DeviceType type); + void setDisplayedame(const QString& name); DeviceType getDeviceType() const { return m_deviceType; } void setToolTip(const QString& tooltip); void setIndex(int index); @@ -97,6 +98,7 @@ protected: QString m_helpURL; RollupContents* m_rollupContents; ContextMenuType m_contextMenuType; + QString m_displayedName; protected slots: void shrinkWindow(); diff --git a/sdrgui/channel/readme.md b/sdrgui/channel/readme.md new file mode 100644 index 000000000..339b2c71c --- /dev/null +++ b/sdrgui/channel/readme.md @@ -0,0 +1,165 @@ +

Channel common controls

+ +The channel windows have common top and bottom bars + +![Channel window](../../doc/img/ChannelWindow.png) + +

A: Top bar

+ +![Channel window](../../doc/img/ChannelWindow_top.png) + +

A.1: Index

+ +The format is: + + - Device reference to which the channel is attached. This is the device type and its index. See [device window documentation](../device/readme.md) + - Semicolon separator + - Channel index in device set + +Aditionally when the channel is a single stream channel and attached to a MIMO device: + + - Dot separator + - Stream index + +The tooltip shows the device type to which the channel is attached, its sequence number and serial number of the device if it exists. It also displays the channel type separated by a slash. For example: `HackRF[0] 88869dc3878a51 / SSB Demodulator` + +You may click on this area and drag the window with the mouse. + +

A.2: Common channel settings

+ +Opens a dialog to update the common channel settings + +![Basic channel settings](../../doc/img/BasicChannelSettings.png) + +

A.2.1: Window title

+ +Changes the channel window title + +

A.2.2: Channel color

+ +Changes the color of the window title bar and spectrum overlay. To change the color click on the color square to open a color chooser dialog. The hex rgb value is displayed next to the color square. + +

A.2.3: Frequency scale display type

+ +When the mouse is over the channel window or over the central line in the spectrum a channel parameter is displayed on the frequency scale. This parameter can be: + + - Freq: channel absolute center frequency + - Title: channel window title + - AdSnd: UDP address and send port + - AdRcv: UDP address and receive port + +

A.2.4: Toggle reverse API feature

+ +Use this checkbox to toggle on/off the reverse API feature. With reverse API engaged the changes in the channel settings are forwarded to an API endpoint given by address (5.1.5), port (5.1.6), device index (5.1.7) and channel index (5.1.8) in the same format as the SDRangel REST API channel settings endpoint. With the values of the screenshot the API URL is: `http://127.0.0.1:8888/sdrangel/deviceset/0/channel/0/settings` The JSON payload follows the same format as the SDRangel REST API channel settings. Using the same example this would be: + +``` +{ + "SSBDemodSettings": { + "agc": 0, + "agcClamping": 0, + "agcPowerThreshold": -40, + "agcThresholdGate": 4, + "agcTimeLog2": 7, + "audioBinaural": 0, + "audioDeviceName": "System default device", + "audioFlipChannels": 0, + "audioMute": 0, + "dsb": 0, + "inputFrequencyOffset": 0, + "lowCutoff": 300, + "rfBandwidth": 3000, + "rgbColor": -16711936, + "spanLog2": 3, + "title": "SSB Demodulator", + "volume": 3 + }, + "channelType": "SSBDemod", + "tx": 0 +} +``` +Note that the PATCH method is used. The full set of parameters is sent only when the reverse API is toggled on or a full settings update is done. + +More details on this feature can be found on the corresponding Wiki page. + +

A.2.5: API address

+ +This is the IP address of the API endpoint + +

A.2.6: API port

+ +This is the IP port of the API endpoint + +

A.2.7: Device index

+ +This is the targeted device index + +

A.2.8: Channel index

+ +This is the targeted channel index + +

A.2.9: Cancel changes and exit dialog

+ +Do not make any changes and exit dialog + +

A.2.10: Validate and exit dialog

+ +Validates the data (saves it in the channel marker object) and exits the dialog + +

A.3: Title

+ +This is the default channel title or as set with (A.2.1). + +You may click on this area and drag the window with the mouse. + +

A.4: Help

+ +Clicking on this button opens the documentation about the channel controls in github in the browser. + +

A.5: Move to another workspace

+ +Opens a dialog to choose a destination workspace to move the channel window to. Nothing happens if the same workspace is selected. + +

A.6: Shrink window

+ +Click this button to reduce the window to its minimum size + +

A.7: Hide window

+ +Click this button to hide the channel window + +

A.8: Close window

+ +Click this button to close the window and removes the channel from the device set. + +

A.9: Top size grip

+ +You can drag this gray square to resize the window + +

B: Bottom bar

+ +![Channel window](../../doc/img/ChannelWindow_bottom.png) + +

B.1: Duplicate channel

+ +Creates a new channel with the same settings + +

B.2: Attach channel to another device

+ +Opens a dialog to select the destination device. Nothing happens if the destination device is the same as the current device. + +

B.3: Absolute channel center frequency

+ +This is the absolute frequency in Hz of the channel center. It is the sum of the device center frequency and the channel shift. + +You may click on this area and drag the window with the mouse. + +

B.4: Status message

+ +The status messages appear here if any. + +You may click on this area and drag the window with the mouse. + +

B.5: Bottom size grip

+ +You can drag this gray square to resize the window + diff --git a/sdrgui/device/deviceuiset.cpp b/sdrgui/device/deviceuiset.cpp index 1adb6df0f..4530d1cfd 100644 --- a/sdrgui/device/deviceuiset.cpp +++ b/sdrgui/device/deviceuiset.cpp @@ -298,8 +298,10 @@ void DeviceUISet::loadRxChannelSettings(const Preset *preset, PluginAPI *pluginA qPrintable((*channelRegistrations)[i].m_channelIdURI), qPrintable(channelConfig.m_channelIdURI)); BasebandSampleSink *rxChannel; - (*channelRegistrations)[i].m_plugin->createRxChannel(m_deviceAPI, &rxChannel, &channelAPI); - rxChannelGUI = (*channelRegistrations)[i].m_plugin->createRxChannelGUI(this, rxChannel); + PluginInterface *pluginInterface = (*channelRegistrations)[i].m_plugin; + pluginInterface->createRxChannel(m_deviceAPI, &rxChannel, &channelAPI); + rxChannelGUI = pluginInterface->createRxChannelGUI(this, rxChannel); + rxChannelGUI->setDisplayedame(pluginInterface->getPluginDescriptor().displayedName); registerRxChannelInstance(channelAPI, rxChannelGUI); QObject::connect( rxChannelGUI, @@ -424,8 +426,10 @@ void DeviceUISet::loadTxChannelSettings(const Preset *preset, PluginAPI *pluginA qPrintable((*channelRegistrations)[i].m_channelIdURI), qPrintable(channelConfig.m_channelIdURI)); BasebandSampleSource *txChannel; - (*channelRegistrations)[i].m_plugin->createTxChannel(m_deviceAPI, &txChannel, &channelAPI); - txChannelGUI = (*channelRegistrations)[i].m_plugin->createTxChannelGUI(this, txChannel); + PluginInterface *pluginInterface = (*channelRegistrations)[i].m_plugin; + pluginInterface->createTxChannel(m_deviceAPI, &txChannel, &channelAPI); + txChannelGUI = pluginInterface->createTxChannelGUI(this, txChannel); + txChannelGUI->setDisplayedame(pluginInterface->getPluginDescriptor().displayedName); registerTxChannelInstance(channelAPI, txChannelGUI); QObject::connect( txChannelGUI, @@ -551,9 +555,10 @@ void DeviceUISet::loadMIMOChannelSettings(const Preset *preset, PluginAPI *plugi qPrintable((*channelRegistrations)[i].m_channelIdURI), qPrintable(channelConfig.m_channelIdURI)); MIMOChannel *mimoChannel; - (*channelRegistrations)[i].m_plugin->createMIMOChannel(m_deviceAPI, &mimoChannel, &channelAPI); - mimoChannelGUI = (*channelRegistrations)[i].m_plugin->createMIMOChannelGUI(this, mimoChannel); - (*channelRegistrations)[i].m_plugin->createMIMOChannel(m_deviceAPI, &mimoChannel, &channelAPI); + PluginInterface *pluginInterface = (*channelRegistrations)[i].m_plugin; + pluginInterface->createMIMOChannel(m_deviceAPI, &mimoChannel, &channelAPI); + mimoChannelGUI = pluginInterface->createMIMOChannelGUI(this, mimoChannel); + mimoChannelGUI->setDisplayedame(pluginInterface->getPluginDescriptor().displayedName); registerChannelInstance(channelAPI, mimoChannelGUI); QObject::connect( mimoChannelGUI, diff --git a/sdrgui/device/readme.md b/sdrgui/device/readme.md index d07c77aef..0cddbbcaf 100644 --- a/sdrgui/device/readme.md +++ b/sdrgui/device/readme.md @@ -2,14 +2,13 @@ The device windows have common top and bottom bars - ![Device window](../../doc/img/DeviceWindow.png)

A: Top bar

![Device window top bar](../../doc/img/DeviceWindow_top.png) -

A.1 Devcice type and index

+

A.1 Device type and index

The format is: - Device type: @@ -19,7 +18,7 @@ The format is: - Semicolon separator - Device set index. A "device set" is the set of a device and its corresponding channels -THe tooltip shows the device type, sequence number and serial number of the device if it exists. +The tooltip shows the device type, sequence number and serial number of the device if it exists. You may click on this area and drag the window with the mouse. @@ -185,7 +184,7 @@ You may click on this area and drag the window with the mouse. Clicking on this button opens the documentation about the device controls in github in the browser. -

A.9: Move to a workspace

+

A.9: Move to another workspace

Opens a dialog to choose a destination workspace to move the device window to. Nothing happens if the same workspace is selected. @@ -195,13 +194,13 @@ Click this button to reduce the window to its minimum size

A.11: Close window

-Click this button to close the window and removes the channel from the device set. +Click this button to close the window and removes the device set i.e. the device and all its channels.

A.12: Top size grip

You can drag this gray square to resize the window -

B: Botom bar

+

B: Bottom bar

![Device window bottom bar](../../doc/img/DeviceWindow_bottom.png) diff --git a/sdrgui/mainwindow.cpp b/sdrgui/mainwindow.cpp index 99d0495e2..2f663149b 100644 --- a/sdrgui/mainwindow.cpp +++ b/sdrgui/mainwindow.cpp @@ -2362,6 +2362,7 @@ void MainWindow::channelAddClicked(Workspace *workspace, int deviceSetIndex, int deviceUI->registerRxChannelInstance(channelAPI, gui); gui->setDeviceType(ChannelGUI::DeviceRx); gui->setIndex(channelAPI->getIndexInDeviceSet()); + gui->setDisplayedame(pluginInterface->getPluginDescriptor().displayedName); } else if (deviceUI->m_deviceSinkEngine) // sink device => Tx channels { @@ -2374,6 +2375,7 @@ void MainWindow::channelAddClicked(Workspace *workspace, int deviceSetIndex, int deviceUI->registerTxChannelInstance(channelAPI, gui); gui->setDeviceType(ChannelGUI::DeviceTx); gui->setIndex(channelAPI->getIndexInDeviceSet()); + gui->setDisplayedame(pluginInterface->getPluginDescriptor().displayedName); } else if (deviceUI->m_deviceMIMOEngine) // MIMO device => all possible channels. Depends on index range { @@ -2393,6 +2395,7 @@ void MainWindow::channelAddClicked(Workspace *workspace, int deviceSetIndex, int gui = pluginInterface->createMIMOChannelGUI(deviceUI, mimoChannel); deviceUI->registerChannelInstance(channelAPI, gui); gui->setIndex(channelAPI->getIndexInDeviceSet()); + gui->setDisplayedame(pluginInterface->getPluginDescriptor().displayedName); } else if (channelPluginIndex < nbMIMOChannels + nbRxChannels) // Rx { @@ -2404,6 +2407,7 @@ void MainWindow::channelAddClicked(Workspace *workspace, int deviceSetIndex, int gui = pluginInterface->createRxChannelGUI(deviceUI, rxChannel); deviceUI->registerRxChannelInstance(channelAPI, gui); gui->setIndex(channelAPI->getIndexInDeviceSet()); + gui->setDisplayedame(pluginInterface->getPluginDescriptor().displayedName); } else if (channelPluginIndex < nbMIMOChannels + nbRxChannels + nbTxChannels) { @@ -2415,6 +2419,7 @@ void MainWindow::channelAddClicked(Workspace *workspace, int deviceSetIndex, int gui = pluginInterface->createTxChannelGUI(deviceUI, txChannel); deviceUI->registerTxChannelInstance(channelAPI, gui); gui->setIndex(channelAPI->getIndexInDeviceSet()); + gui->setDisplayedame(pluginInterface->getPluginDescriptor().displayedName); } gui->setDeviceType(ChannelGUI::DeviceMIMO); From 50a12b3a3faab68603cf0b79acf52520c81376f7 Mon Sep 17 00:00:00 2001 From: f4exb Date: Sun, 17 Apr 2022 23:18:56 +0200 Subject: [PATCH 033/115] Massive UI revamping (v7): DeviceUISet: fixed load MIMO channel settings --- sdrgui/device/deviceuiset.cpp | 135 +++++++++++++++++++++++----------- 1 file changed, 93 insertions(+), 42 deletions(-) diff --git a/sdrgui/device/deviceuiset.cpp b/sdrgui/device/deviceuiset.cpp index 4530d1cfd..6714974cc 100644 --- a/sdrgui/device/deviceuiset.cpp +++ b/sdrgui/device/deviceuiset.cpp @@ -268,7 +268,7 @@ void DeviceUISet::loadRxChannelSettings(const Preset *preset, PluginAPI *pluginA PluginAPI::ChannelRegistrations *channelRegistrations = pluginAPI->getRxChannelRegistrations(); // clear list - for(int i = 0; i < m_channelInstanceRegistrations.count(); i++) + for (int i = 0; i < m_channelInstanceRegistrations.count(); i++) { qDebug("DeviceUISet::loadRxChannelSettings: destroying old channel [%s]", qPrintable(m_channelInstanceRegistrations[i].m_channelAPI->getURI())); @@ -289,7 +289,7 @@ void DeviceUISet::loadRxChannelSettings(const Preset *preset, PluginAPI *pluginA // create channel instance - for(int i = 0; i < channelRegistrations->count(); i++) + for (int i = 0; i < channelRegistrations->count(); i++) { //if((*channelRegistrations)[i].m_channelIdURI == channelConfig.m_channelIdURI) if (ChannelUtils::compareChannelURIs((*channelRegistrations)[i].m_channelIdURI, channelConfig.m_channelIdURI)) @@ -397,7 +397,7 @@ void DeviceUISet::loadTxChannelSettings(const Preset *preset, PluginAPI *pluginA PluginAPI::ChannelRegistrations *channelRegistrations = pluginAPI->getTxChannelRegistrations(); // clear list - for(int i = 0; i < m_channelInstanceRegistrations.count(); i++) + for (int i = 0; i < m_channelInstanceRegistrations.count(); i++) { qDebug("DeviceUISet::loadTxChannelSettings: destroying old channel [%s]", qPrintable(m_channelInstanceRegistrations[i].m_channelAPI->getURI())); @@ -410,7 +410,7 @@ void DeviceUISet::loadTxChannelSettings(const Preset *preset, PluginAPI *pluginA m_deviceSet->clearChannels(); qDebug("DeviceUISet::loadTxChannelSettings: %d channel(s) in preset", preset->getChannelCount()); - for(int i = 0; i < preset->getChannelCount(); i++) + for (int i = 0; i < preset->getChannelCount(); i++) { const Preset::ChannelConfig& channelConfig = preset->getChannelConfig(i); ChannelGUI *txChannelGUI = nullptr; @@ -418,7 +418,7 @@ void DeviceUISet::loadTxChannelSettings(const Preset *preset, PluginAPI *pluginA // create channel instance - for(int i = 0; i < channelRegistrations->count(); i++) + for (int i = 0; i < channelRegistrations->count(); i++) { if ((*channelRegistrations)[i].m_channelIdURI == channelConfig.m_channelIdURI) { @@ -522,11 +522,8 @@ void DeviceUISet::loadMIMOChannelSettings(const Preset *preset, PluginAPI *plugi qDebug("DeviceUISet::loadMIMOChannelSettings: Loading preset [%s | %s]", qPrintable(preset->getGroup()), qPrintable(preset->getDescription())); - // Available channel plugins - PluginAPI::ChannelRegistrations *channelRegistrations = pluginAPI->getMIMOChannelRegistrations(); - // clear list - for(int i = 0; i < m_channelInstanceRegistrations.count(); i++) + for (int i = 0; i < m_channelInstanceRegistrations.count(); i++) { qDebug("DeviceUISet::loadMIMOChannelSettings: destroying old channel [%s]", qPrintable(m_channelInstanceRegistrations[i].m_channelAPI->getURI())); @@ -541,79 +538,133 @@ void DeviceUISet::loadMIMOChannelSettings(const Preset *preset, PluginAPI *plugi for (int i = 0; i < preset->getChannelCount(); i++) { const Preset::ChannelConfig& channelConfig = preset->getChannelConfig(i); - ChannelGUI *mimoChannelGUI = nullptr; + ChannelGUI *channelGUI = nullptr; ChannelAPI *channelAPI = nullptr; - // create channel instance - - for(int i = 0; i < channelRegistrations->count(); i++) + // Available MIMO channel plugins + PluginAPI::ChannelRegistrations *channelMIMORegistrations = pluginAPI->getMIMOChannelRegistrations(); + // create MIMO channel instance + for (int i = 0; i < channelMIMORegistrations->count(); i++) { - //if((*channelRegistrations)[i].m_channelIdURI == channelConfig.m_channelIdURI) - if (ChannelUtils::compareChannelURIs((*channelRegistrations)[i].m_channelIdURI, channelConfig.m_channelIdURI)) + if (ChannelUtils::compareChannelURIs((*channelMIMORegistrations)[i].m_channelIdURI, channelConfig.m_channelIdURI)) { - qDebug("DeviceUISet::loadMIMOChannelSettings: creating new channel [%s] from config [%s]", - qPrintable((*channelRegistrations)[i].m_channelIdURI), + qDebug("DeviceUISet::loadMIMOChannelSettings: creating new MIMO channel [%s] from config [%s]", + qPrintable((*channelMIMORegistrations)[i].m_channelIdURI), qPrintable(channelConfig.m_channelIdURI)); MIMOChannel *mimoChannel; - PluginInterface *pluginInterface = (*channelRegistrations)[i].m_plugin; + PluginInterface *pluginInterface = (*channelMIMORegistrations)[i].m_plugin; pluginInterface->createMIMOChannel(m_deviceAPI, &mimoChannel, &channelAPI); - mimoChannelGUI = pluginInterface->createMIMOChannelGUI(this, mimoChannel); - mimoChannelGUI->setDisplayedame(pluginInterface->getPluginDescriptor().displayedName); - registerChannelInstance(channelAPI, mimoChannelGUI); + channelGUI = pluginInterface->createMIMOChannelGUI(this, mimoChannel); + channelGUI->setDisplayedame(pluginInterface->getPluginDescriptor().displayedName); + registerChannelInstance(channelAPI, channelGUI); QObject::connect( - mimoChannelGUI, + channelGUI, &ChannelGUI::closing, this, - [=](){ this->handleChannelGUIClosing(mimoChannelGUI); }, + [=](){ this->handleChannelGUIClosing(channelGUI); }, Qt::QueuedConnection ); break; } } - if (mimoChannelGUI && channelAPI) + // Available Rx channel plugins + PluginAPI::ChannelRegistrations *channelRxRegistrations = pluginAPI->getRxChannelRegistrations(); + // create Rx channel instance + for (int i = 0; i < channelRxRegistrations->count(); i++) + { + if (ChannelUtils::compareChannelURIs((*channelRxRegistrations)[i].m_channelIdURI, channelConfig.m_channelIdURI)) + { + qDebug("DeviceUISet::loadMIMOChannelSettings: creating new Rx channel [%s] from config [%s]", + qPrintable((*channelRxRegistrations)[i].m_channelIdURI), + qPrintable(channelConfig.m_channelIdURI)); + BasebandSampleSink *rxChannel; + PluginInterface *pluginInterface = (*channelRxRegistrations)[i].m_plugin; + pluginInterface->createRxChannel(m_deviceAPI, &rxChannel, &channelAPI); + channelGUI = pluginInterface->createRxChannelGUI(this, rxChannel); + channelGUI->setDisplayedame(pluginInterface->getPluginDescriptor().displayedName); + registerRxChannelInstance(channelAPI, channelGUI); + QObject::connect( + channelGUI, + &ChannelGUI::closing, + this, + [=](){ this->handleChannelGUIClosing(channelGUI); }, + Qt::QueuedConnection + ); + break; + } + } + + // Available Tx channel plugins + PluginAPI::ChannelRegistrations *channelTxRegistrations = pluginAPI->getTxChannelRegistrations(); + // create Tx channel instance + for (int i = 0; i < channelTxRegistrations->count(); i++) + { + if (ChannelUtils::compareChannelURIs((*channelTxRegistrations)[i].m_channelIdURI, channelConfig.m_channelIdURI)) + { + qDebug("DeviceUISet::loadMIMOChannelSettings: creating new Tx channel [%s] from config [%s]", + qPrintable((*channelTxRegistrations)[i].m_channelIdURI), + qPrintable(channelConfig.m_channelIdURI)); + BasebandSampleSource *txChannel; + PluginInterface *pluginInterface = (*channelTxRegistrations)[i].m_plugin; + pluginInterface->createTxChannel(m_deviceAPI, &txChannel, &channelAPI); + channelGUI = pluginInterface->createTxChannelGUI(this, txChannel); + channelGUI->setDisplayedame(pluginInterface->getPluginDescriptor().displayedName); + registerTxChannelInstance(channelAPI, channelGUI); + break; + } + } + + if (channelGUI && channelAPI) { qDebug("DeviceUISet::loadMIMOChannelSettings: deserializing channel [%s]", qPrintable(channelConfig.m_channelIdURI)); - mimoChannelGUI->deserialize(channelConfig.m_config); - int originalWorkspaceIndex = mimoChannelGUI->getWorkspaceIndex(); + channelGUI->deserialize(channelConfig.m_config); + int originalWorkspaceIndex = channelGUI->getWorkspaceIndex(); if (workspaces && (workspaces->size() > 0) && (originalWorkspaceIndex < workspaces->size())) // restore in original workspace { - (*workspaces)[originalWorkspaceIndex]->addToMdiArea((QMdiSubWindow*) mimoChannelGUI); + (*workspaces)[originalWorkspaceIndex]->addToMdiArea((QMdiSubWindow*) channelGUI); } else if (currentWorkspace) // restore in current workspace { - mimoChannelGUI->setWorkspaceIndex(currentWorkspace->getIndex()); - currentWorkspace->addToMdiArea((QMdiSubWindow*) mimoChannelGUI); + channelGUI->setWorkspaceIndex(currentWorkspace->getIndex()); + currentWorkspace->addToMdiArea((QMdiSubWindow*) channelGUI); } - if (mimoChannelGUI->getHidden()) { - mimoChannelGUI->hide(); + if (channelGUI->getHidden()) { + channelGUI->hide(); } - mimoChannelGUI->restoreGeometry(mimoChannelGUI->getGeometryBytes()); - mimoChannelGUI->setDeviceType(ChannelGUI::DeviceRx); - mimoChannelGUI->setDeviceSetIndex(m_deviceSetIndex); - mimoChannelGUI->setIndex(channelAPI->getIndexInDeviceSet()); - mimoChannelGUI->setToolTip(m_deviceAPI->getSamplingDeviceDisplayName()); + channelGUI->restoreGeometry(channelGUI->getGeometryBytes()); + channelGUI->setDeviceType(ChannelGUI::DeviceRx); + channelGUI->setDeviceSetIndex(m_deviceSetIndex); + channelGUI->setIndex(channelAPI->getIndexInDeviceSet()); + channelGUI->setToolTip(m_deviceAPI->getSamplingDeviceDisplayName()); QObject::connect( - mimoChannelGUI, + channelGUI, + &ChannelGUI::closing, + this, + [=](){ this->handleChannelGUIClosing(channelGUI); }, + Qt::QueuedConnection + ); + QObject::connect( + channelGUI, &ChannelGUI::moveToWorkspace, this, - [=](int wsIndexDest){ MainWindow::getInstance()->channelMove(mimoChannelGUI, wsIndexDest); } + [=](int wsIndexDest){ MainWindow::getInstance()->channelMove(channelGUI, wsIndexDest); } ); QObject::connect( - mimoChannelGUI, + channelGUI, &ChannelGUI::duplicateChannelEmitted, this, - [=](){ MainWindow::getInstance()->channelDuplicate(mimoChannelGUI); } + [=](){ MainWindow::getInstance()->channelDuplicate(channelGUI); } ); QObject::connect( - mimoChannelGUI, + channelGUI, &ChannelGUI::moveToDeviceSet, this, - [=](int dsIndexDest){ MainWindow::getInstance()->channelMoveToDeviceSet(mimoChannelGUI, dsIndexDest); } + [=](int dsIndexDest){ MainWindow::getInstance()->channelMoveToDeviceSet(channelGUI, dsIndexDest); } ); } } From 018b97e17dc65c79735936f32bc958fe0c8fbc56 Mon Sep 17 00:00:00 2001 From: f4exb Date: Mon, 18 Apr 2022 01:42:03 +0200 Subject: [PATCH 034/115] Massive UI revamping (v7): updated basic channel settings and indicate stream index for MIMO device and single stream channel --- doc/img/BasicChannelSettings.png | Bin 21094 -> 22134 bytes doc/img/BasicChannelSettings.xcf | Bin 73801 -> 79906 bytes .../beamsteeringcwmodgui.cpp | 4 +- .../beamsteeringcwmod/beamsteeringcwmodgui.h | 2 + .../interferometer/interferometergui.cpp | 2 +- .../interferometer/interferometergui.h | 2 + plugins/channelrx/chanalyzer/chanalyzer.cpp | 5 ++ plugins/channelrx/chanalyzer/chanalyzer.h | 1 + .../channelrx/chanalyzer/chanalyzergui.cpp | 16 +++++ plugins/channelrx/chanalyzer/chanalyzergui.h | 2 + plugins/channelrx/demodadsb/adsbdemodgui.cpp | 39 +++++------- plugins/channelrx/demodadsb/adsbdemodgui.h | 3 +- plugins/channelrx/demodais/aisdemodgui.cpp | 39 +++++------- plugins/channelrx/demodais/aisdemodgui.h | 3 +- plugins/channelrx/demodam/amdemodgui.cpp | 39 +++++------- plugins/channelrx/demodam/amdemodgui.h | 3 +- plugins/channelrx/demodapt/aptdemodgui.cpp | 39 +++++------- plugins/channelrx/demodapt/aptdemodgui.h | 3 +- plugins/channelrx/demodatv/atvdemod.cpp | 5 ++ plugins/channelrx/demodatv/atvdemod.h | 1 + plugins/channelrx/demodatv/atvdemodgui.cpp | 60 +++++++++++++++--- plugins/channelrx/demodatv/atvdemodgui.h | 4 +- plugins/channelrx/demodbfm/bfmdemodgui.cpp | 38 +++++------ plugins/channelrx/demodbfm/bfmdemodgui.h | 3 +- .../demodchirpchat/chirpchatdemodgui.cpp | 38 +++++------ .../demodchirpchat/chirpchatdemodgui.h | 3 +- plugins/channelrx/demoddab/dabdemodgui.cpp | 39 +++++------- plugins/channelrx/demoddab/dabdemodgui.h | 3 +- plugins/channelrx/demoddatv/datvdemodgui.cpp | 38 +++++------ plugins/channelrx/demoddatv/datvdemodgui.h | 4 +- plugins/channelrx/demoddsd/dsddemodgui.cpp | 38 +++++------ plugins/channelrx/demoddsd/dsddemodgui.h | 3 +- .../channelrx/demodfreedv/freedvdemodgui.cpp | 38 +++++------ .../channelrx/demodfreedv/freedvdemodgui.h | 3 +- plugins/channelrx/demodnfm/nfmdemodgui.cpp | 39 +++++------- plugins/channelrx/demodnfm/nfmdemodgui.h | 3 +- .../channelrx/demodpacket/packetdemodgui.cpp | 39 +++++------- .../channelrx/demodpacket/packetdemodgui.h | 3 +- .../channelrx/demodpager/pagerdemodgui.cpp | 39 +++++------- plugins/channelrx/demodpager/pagerdemodgui.h | 3 +- .../demodradiosonde/radiosondedemodgui.cpp | 39 +++++------- .../demodradiosonde/radiosondedemodgui.h | 3 +- plugins/channelrx/demodssb/ssbdemodgui.cpp | 38 +++++------ plugins/channelrx/demodssb/ssbdemodgui.h | 3 +- plugins/channelrx/demodvor/vordemodgui.cpp | 39 +++++------- plugins/channelrx/demodvor/vordemodgui.h | 3 +- .../channelrx/demodvorsc/vordemodscgui.cpp | 39 +++++------- plugins/channelrx/demodvorsc/vordemodscgui.h | 3 +- plugins/channelrx/demodwfm/wfmdemodgui.cpp | 38 +++++------ plugins/channelrx/demodwfm/wfmdemodgui.h | 3 +- plugins/channelrx/filesink/filesinkgui.cpp | 38 +++++------ plugins/channelrx/filesink/filesinkgui.h | 3 +- .../channelrx/freqtracker/freqtrackergui.cpp | 39 +++++------- .../channelrx/freqtracker/freqtrackergui.h | 3 +- plugins/channelrx/localsink/localsinkgui.cpp | 38 +++++------ plugins/channelrx/localsink/localsinkgui.h | 3 +- .../channelrx/noisefigure/noisefiguregui.cpp | 39 +++++------- .../channelrx/noisefigure/noisefiguregui.h | 3 +- .../radioastronomy/radioastronomygui.cpp | 39 +++++------- .../radioastronomy/radioastronomygui.h | 3 +- .../channelrx/radioclock/radioclockgui.cpp | 39 +++++------- plugins/channelrx/radioclock/radioclockgui.h | 3 +- .../channelrx/remotesink/remotesinkgui.cpp | 38 +++++------ plugins/channelrx/remotesink/remotesinkgui.h | 3 +- .../sigmffilesink/sigmffilesinkgui.cpp | 38 +++++------ .../sigmffilesink/sigmffilesinkgui.h | 3 +- plugins/channelrx/udpsink/udpsinkgui.cpp | 40 +++++------- plugins/channelrx/udpsink/udpsinkgui.h | 3 +- .../channeltx/filesource/filesourcegui.cpp | 38 +++++------ plugins/channeltx/filesource/filesourcegui.h | 3 +- .../channeltx/localsource/localsourcegui.cpp | 38 +++++------ .../channeltx/localsource/localsourcegui.h | 3 +- .../mod802.15.4/ieee_802_15_4_modgui.cpp | 39 +++++------- .../mod802.15.4/ieee_802_15_4_modgui.h | 3 +- plugins/channeltx/modais/aismodgui.cpp | 39 +++++------- plugins/channeltx/modais/aismodgui.h | 3 +- plugins/channeltx/modam/ammodgui.cpp | 39 +++++------- plugins/channeltx/modam/ammodgui.h | 3 +- plugins/channeltx/modatv/atvmodgui.cpp | 38 +++++------ plugins/channeltx/modatv/atvmodgui.h | 3 +- .../modchirpchat/chirpchatmodgui.cpp | 39 +++++------- .../channeltx/modchirpchat/chirpchatmodgui.h | 3 +- plugins/channeltx/moddatv/datvmodgui.cpp | 38 +++++------ plugins/channeltx/moddatv/datvmodgui.h | 3 +- plugins/channeltx/modfreedv/freedvmodgui.cpp | 38 +++++------ plugins/channeltx/modfreedv/freedvmodgui.h | 3 +- plugins/channeltx/modnfm/nfmmodgui.cpp | 39 +++++------- plugins/channeltx/modnfm/nfmmodgui.h | 3 +- plugins/channeltx/modpacket/packetmodgui.cpp | 39 +++++------- plugins/channeltx/modpacket/packetmodgui.h | 3 +- plugins/channeltx/modssb/ssbmodgui.cpp | 38 +++++------ plugins/channeltx/modssb/ssbmodgui.h | 3 +- plugins/channeltx/modwfm/wfmmodgui.cpp | 38 +++++------ plugins/channeltx/modwfm/wfmmodgui.h | 3 +- .../remotesource/remotesourcegui.cpp | 38 +++++------ .../channeltx/remotesource/remotesourcegui.h | 3 +- plugins/channeltx/udpsource/udpsourcegui.cpp | 38 +++++------ plugins/channeltx/udpsource/udpsourcegui.h | 3 +- sdrgui/channel/channelgui.cpp | 12 ++-- sdrgui/channel/channelgui.h | 8 +-- sdrgui/channel/readme.md | 26 +++++--- sdrgui/gui/basicchannelsettingsdialog.cpp | 22 +++++++ sdrgui/gui/basicchannelsettingsdialog.h | 8 +++ sdrgui/gui/basicchannelsettingsdialog.ui | 46 +++++++++++++- sdrgui/mainwindow.cpp | 6 +- 105 files changed, 934 insertions(+), 1044 deletions(-) diff --git a/doc/img/BasicChannelSettings.png b/doc/img/BasicChannelSettings.png index ec2065606325e4ccaf54e01090305872ff0f64de..99c2c01d3e889adf894580c36e1fd03df42b26e3 100644 GIT binary patch literal 22134 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 literal 21094 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

A.2.2: Channel color

+

A.2.2: Reset title to default

+ +Resets the title to the channel type name. + +

A.2.3: Channel color

Changes the color of the window title bar and spectrum overlay. To change the color click on the color square to open a color chooser dialog. The hex rgb value is displayed next to the color square. -

A.2.3: Frequency scale display type

+

A.2.4: Frequency scale display type

When the mouse is over the channel window or over the central line in the spectrum a channel parameter is displayed on the frequency scale. This parameter can be: @@ -48,7 +52,11 @@ When the mouse is over the channel window or over the central line in the spectr - AdSnd: UDP address and send port - AdRcv: UDP address and receive port -

A.2.4: Toggle reverse API feature

+

A.2.5: Device stream assignment

+ +If the device is a MIMO device and the channel is single stream based i.e. not MIMO as well this sets the MIMO stream the channel will take its I/Q input from. It is disabled in other cases. + +

A.2.6: Toggle reverse API feature

Use this checkbox to toggle on/off the reverse API feature. With reverse API engaged the changes in the channel settings are forwarded to an API endpoint given by address (5.1.5), port (5.1.6), device index (5.1.7) and channel index (5.1.8) in the same format as the SDRangel REST API channel settings endpoint. With the values of the screenshot the API URL is: `http://127.0.0.1:8888/sdrangel/deviceset/0/channel/0/settings` The JSON payload follows the same format as the SDRangel REST API channel settings. Using the same example this would be: @@ -81,27 +89,27 @@ Note that the PATCH method is used. The full set of parameters is sent only when More details on this feature can be found on the corresponding Wiki page. -

A.2.5: API address

+

A.2.7: API address

This is the IP address of the API endpoint -

A.2.6: API port

+

A.2.8: API port

This is the IP port of the API endpoint -

A.2.7: Device index

+

A.2.9: Device index

This is the targeted device index -

A.2.8: Channel index

+

A.2.10: Channel index

This is the targeted channel index -

A.2.9: Cancel changes and exit dialog

+

A.2.11: Cancel changes and exit dialog

Do not make any changes and exit dialog -

A.2.10: Validate and exit dialog

+

A.2.12: Validate and exit dialog

Validates the data (saves it in the channel marker object) and exits the dialog diff --git a/sdrgui/gui/basicchannelsettingsdialog.cpp b/sdrgui/gui/basicchannelsettingsdialog.cpp index f80345289..f252b5094 100644 --- a/sdrgui/gui/basicchannelsettingsdialog.cpp +++ b/sdrgui/gui/basicchannelsettingsdialog.cpp @@ -63,6 +63,18 @@ void BasicChannelSettingsDialog::setReverseAPIChannelIndex(uint16_t channelIndex ui->reverseAPIChannelIndex->setText(tr("%1").arg(m_reverseAPIChannelIndex)); } +void BasicChannelSettingsDialog::setNumberOfStreams(int numberOfStreams) +{ + ui->streamIndex->setMaximum(numberOfStreams - 1); + ui->streamIndex->setEnabled(true); +} + +void BasicChannelSettingsDialog::setStreamIndex(int index) +{ + m_streamIndex = index; + ui->streamIndex->setValue(index); +} + void BasicChannelSettingsDialog::paintColor() { QPixmap pm(24, 24); @@ -132,6 +144,16 @@ void BasicChannelSettingsDialog::on_reverseAPIChannelIndex_editingFinished() } } +void BasicChannelSettingsDialog::on_streamIndex_valueChanged(int value) +{ + m_streamIndex = value; +} + +void BasicChannelSettingsDialog::on_titleReset_clicked() +{ + ui->title->setText(m_defaultTitle); +} + void BasicChannelSettingsDialog::accept() { m_channelMarker->blockSignals(true); diff --git a/sdrgui/gui/basicchannelsettingsdialog.h b/sdrgui/gui/basicchannelsettingsdialog.h index ffe8c7d36..c7274ad1f 100644 --- a/sdrgui/gui/basicchannelsettingsdialog.h +++ b/sdrgui/gui/basicchannelsettingsdialog.h @@ -24,19 +24,25 @@ public: uint16_t getReverseAPIPort() const { return m_reverseAPIPort; } uint16_t getReverseAPIDeviceIndex() const { return m_reverseAPIDeviceIndex; } uint16_t getReverseAPIChannelIndex() const { return m_reverseAPIChannelIndex; } + int getSelectedStreamIndex() const { return m_streamIndex; } void setUseReverseAPI(bool useReverseAPI); void setReverseAPIAddress(const QString& address); void setReverseAPIPort(uint16_t port); void setReverseAPIDeviceIndex(uint16_t deviceIndex); void setReverseAPIChannelIndex(uint16_t channelIndex); + void setNumberOfStreams(int numberOfStreams); + void setStreamIndex(int index); + void setDefaultTitle(const QString& title) { m_defaultTitle = title; } private slots: + void on_titleReset_clicked(); void on_colorBtn_clicked(); void on_reverseAPI_toggled(bool checked); void on_reverseAPIAddress_editingFinished(); void on_reverseAPIPort_editingFinished(); void on_reverseAPIDeviceIndex_editingFinished(); void on_reverseAPIChannelIndex_editingFinished(); + void on_streamIndex_valueChanged(int value); void accept(); private: @@ -48,6 +54,8 @@ private: uint16_t m_reverseAPIPort; uint16_t m_reverseAPIDeviceIndex; uint16_t m_reverseAPIChannelIndex; + QString m_defaultTitle; + int m_streamIndex; bool m_hasChanged; void paintColor(); diff --git a/sdrgui/gui/basicchannelsettingsdialog.ui b/sdrgui/gui/basicchannelsettingsdialog.ui index 51d5e6116..90f4418f1 100644 --- a/sdrgui/gui/basicchannelsettingsdialog.ui +++ b/sdrgui/gui/basicchannelsettingsdialog.ui @@ -7,7 +7,7 @@ 0 0 400 - 135 + 158 @@ -42,6 +42,26 @@ + + + + + 24 + 24 + + + + Reset title to channel type name + + + + + + + :/recycle.png:/recycle.png + + + @@ -97,6 +117,26 @@ + + + + Stream + + + + + + + false + + + Stream index for MIMO device and single stream channel + + + 0 + + + @@ -272,7 +312,9 @@ - + + + buttonBox diff --git a/sdrgui/mainwindow.cpp b/sdrgui/mainwindow.cpp index 2f663149b..a6c504b53 100644 --- a/sdrgui/mainwindow.cpp +++ b/sdrgui/mainwindow.cpp @@ -872,8 +872,12 @@ void MainWindow::sampleMIMOCreate( deviceGUI->setToolTip(samplingDevice->displayedName); deviceGUI->setTitle(samplingDevice->displayedName.split(" ")[0]); deviceGUI->setCurrentDeviceIndex(selectedDeviceIndex); - QStringList channelNames; + QStringList channelNames, tmpChannelNames; m_pluginManager->listMIMOChannels(channelNames); + m_pluginManager->listRxChannels(tmpChannelNames); + channelNames.append(tmpChannelNames); + m_pluginManager->listTxChannels(tmpChannelNames); + channelNames.append(tmpChannelNames); deviceGUI->setChannelNames(channelNames); MainSpectrumGUI *spectrumGUI = deviceUISet->m_mainSpectrumGUI; spectrumGUI->setDeviceType(MainSpectrumGUI::DeviceMIMO); From 8f48e987fa3d85ac60d928bee59af4c3d74b67db Mon Sep 17 00:00:00 2001 From: f4exb Date: Mon, 18 Apr 2022 05:25:31 +0200 Subject: [PATCH 035/115] Massive UI revamping (v7): correct ValueDialZ incorrectly zeroing on set value range --- sdrgui/gui/valuedialz.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/sdrgui/gui/valuedialz.cpp b/sdrgui/gui/valuedialz.cpp index e255039bc..32c3fa91e 100644 --- a/sdrgui/gui/valuedialz.cpp +++ b/sdrgui/gui/valuedialz.cpp @@ -153,11 +153,10 @@ void ValueDialZ::setValueRange(bool positiveOnly, uint numDigits, qint64 min, qi { setValue(m_valueMax); } - else if (m_value == 0) + else if ((m_value == 0) && (m_valueNew == 0)) { m_text = formatText(0); m_textNew = m_text; - m_valueNew = m_value; update(); } } From e8046ef6d78301902e950309fd89e31caf38bba2 Mon Sep 17 00:00:00 2001 From: f4exb Date: Mon, 18 Apr 2022 10:20:52 +0200 Subject: [PATCH 036/115] Massive UI revamping (v7): main spectrum window documentation --- doc/img/MainSpectrum.png | Bin 0 -> 57910 bytes doc/img/MainSpectrum.xcf | Bin 0 -> 152027 bytes sdrgui/mainspectrum/readme.md | 59 ++++++++++++++++++++++++++++++++++ 3 files changed, 59 insertions(+) create mode 100644 doc/img/MainSpectrum.png create mode 100644 doc/img/MainSpectrum.xcf create mode 100644 sdrgui/mainspectrum/readme.md diff --git a/doc/img/MainSpectrum.png b/doc/img/MainSpectrum.png new file mode 100644 index 0000000000000000000000000000000000000000..8fb1ba3091af415ae19900d7181da3e56345342c GIT binary patch literal 57910 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 spectrum window + +This page details the top and bottom bars of the window and not the spectrum nor the spectrum controls. Documentation on the spectrum display and controls can be found here (TBD) + +![Channel window](../../doc/img/MainSpectrum.png) + +

Top bar

+ +

1: Device index

+ +Index of the device for which the spectrum is displayed. It is the same as in the corresponding device window. + +The format is the same as in the device window: + - Device type: + - R: receiver + - T: transmitter + - M: MIMO + - Semicolon separator + - Device set index. A "device set" is the set of a device and its corresponding channels + +The tooltip shows the device type, sequence number and serial number of the device if it exists. + +You may click on this area and drag the window with the mouse. + +

3: Title

+ +The window title shows the device type and a sequence number for which the spectrum is displayed. It is the same as in the corresponding device window. + +You may click on this area and drag the window with the mouse. + +

3: Help

+ +Displays this page in a browser. + +

4: Move to another workspace

+ +Opens a dialog to choose a destination workspace to move the device window to. Nothing happens if the same workspace is selected. + +

5: Shrink window

+ +Click this button to reduce the window to its minimum size. + +

6: Hide window

+ +Click this button to hide the main spectrum window + +

7: Top size grip

+ +You can drag this gray square to resize the window + +

Bottom bar

+ +

8. Window drag area

+ +You may click on this area and drag the window with the mouse. + +

9: Bottom size grip

+ +You can drag this gray square to resize the window From c638beaddb94683bd92c2fbec09870b28af5f3d0 Mon Sep 17 00:00:00 2001 From: f4exb Date: Mon, 18 Apr 2022 10:21:47 +0200 Subject: [PATCH 037/115] Massive UI revamping (v7): raise windows on the 'show' commands so that they will also be put on top --- sdrgui/mainwindow.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/sdrgui/mainwindow.cpp b/sdrgui/mainwindow.cpp index a6c504b53..16057c890 100644 --- a/sdrgui/mainwindow.cpp +++ b/sdrgui/mainwindow.cpp @@ -2544,14 +2544,17 @@ void MainWindow::mainSpectrumMove(MainSpectrumGUI *gui, int wsIndexDestnation) void MainWindow::mainSpectrumShow(MainSpectrumGUI *gui) { gui->show(); + gui->raise(); } void MainWindow::showAllChannels(int deviceSetIndex) { DeviceUISet *deviceUISet = m_deviceUIs[deviceSetIndex]; - for (int i = 0; i < deviceUISet->getNumberOfChannels(); i++) { + for (int i = 0; i < deviceUISet->getNumberOfChannels(); i++) + { deviceUISet->getChannelGUIAt(i)->show(); + deviceUISet->getChannelGUIAt(i)->raise(); } } From e8a2c8b9477a5cdab0ef21528b0148164bb5bdbd Mon Sep 17 00:00:00 2001 From: f4exb Date: Mon, 18 Apr 2022 12:07:23 +0200 Subject: [PATCH 038/115] Massive UI revamping (v7): added feature window documentation --- doc/img/FeatureWindow.png | Bin 0 -> 33838 bytes doc/img/FeatureWindow.xcf | Bin 0 -> 103916 bytes doc/img/Features_basic_dialog.png | Bin 18708 -> 17698 bytes doc/img/Features_basic_dialog.xcf | Bin 64112 -> 62604 bytes sdrgui/feature/readme.md | 115 ++++++++++++++++++++++++++++++ 5 files changed, 115 insertions(+) create mode 100644 doc/img/FeatureWindow.png create mode 100644 doc/img/FeatureWindow.xcf create mode 100644 sdrgui/feature/readme.md diff --git a/doc/img/FeatureWindow.png b/doc/img/FeatureWindow.png new file mode 100644 index 0000000000000000000000000000000000000000..0aec5138b325ba8b74c3f2aae471331e9d6c3969 GIT binary patch literal 33838 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

Top bar

+ +

1: Feature index

+ +Displays the index of the feature in the list of features as "F" followed by semicolon and index number. + +The tooltip displays the feature type name. + +You may click on this area and drag the window with the mouse. + +

2: Common feature settings

+ +Opens a dialog to update the common feature settings + +![Feature window](../../doc/img/Features_basic_dialog.png) + +

2.1: Window title

+ +Changes the feature window title + +

2.2: Reset title to default

+ +Resets the title to the feature type name. + +

2.3: Toggle reverse API feature

+ +Use this checkbox to toggle on/off the reverse API feature. With reverse API engaged the changes in the feature settings are forwarded to an API endpoint given by address (8.2.4), port (8.2.5), feature set index (8.2.6) and feature index (8.2.7) in the same format as the SDRangel REST API feature settings endpoint. With the values of the screenshot the API URL is: `http://127.0.0.1:8888/sdrangel/featureeset/0/feature/0/settings` The JSON payload follows the same format as the SDRangel REST API feature settings. Using the same example this would be: + +``` +{ + "SimplePTTSettings": { + "reverseAPIAddress": "127.0.0.1", + "reverseAPIChannelIndex": 0, + "reverseAPIDeviceIndex": 0, + "reverseAPIPort": 8888, + "rgbColor": -65536, + "rx2TxDelayMs": 200, + "rxDeviceSetIndex": 0, + "title": "Simple PTT", + "tx2RxDelayMs": 200, + "txDeviceSetIndex": 1, + "useReverseAPI": 0 + }, + "featureType": "SimplePTT" +} +``` +Note that the PATCH method is used. The full set of parameters is sent with the PUT method only when the reverse API is toggled on or a full settings update is done. + +

2.4: API address

+ +This is the IP address of the API endpoint + +

2.5: API port

+ +This is the IP port of the API endpoint + +

2.6: Feature set index

+ +This is the targeted feature set index + +

2.7: Feature index

+ +This is the targeted feature index + +

2.8: Cancel changes and exit dialog

+ +Do not make any changes and exit dialog + +

2.9: Validate and exit dialog

+ +Validates the data and exits the dialog + +

3: Title

+ +This is the default feature title or as set with (2.1). + +You may click on this area and drag the window with the mouse. + +

4: Help

+ +Clicking on this button opens the documentation about the feature controls in github in the browser. + +

5: Move to another workspace

+ +Opens a dialog to choose a destination workspace to move the feature window to. Nothing happens if the same workspace is selected. + +

6: Shrink window

+ +Click this button to reduce the window to its minimum size + +

7: Close window

+ +Closes the window and deletes the feature + +

8: Top size grip

+ +You can drag this gray square to resize the window + +

Bottom bar

+ +

9: Status

+ +Status messages if any appear here. + +You may click on this area and drag the window with the mouse. + +

10: Bottom size grip

+ +You can drag this gray square to resize the window From 560d61abcb899cb9181b6da1606d7a7dc65f44a6 Mon Sep 17 00:00:00 2001 From: f4exb Date: Mon, 18 Apr 2022 12:08:33 +0200 Subject: [PATCH 039/115] Massive UI revamping (v7): feature window basic feature settings dialog: implemented reset to default title. Some cosmetic changes --- plugins/feature/afc/afcgui.cpp | 3 ++- plugins/feature/ais/aisgui.cpp | 3 ++- .../feature/antennatools/antennatoolsgui.cpp | 3 ++- plugins/feature/aprs/aprsgui.cpp | 3 ++- .../demodanalyzer/demodanalyzergui.cpp | 3 ++- .../gs232controller/gs232controllergui.cpp | 3 ++- .../jogdialcontrollergui.cpp | 3 ++- plugins/feature/map/mapgui.cpp | 3 ++- plugins/feature/pertester/pertestergui.cpp | 3 ++- plugins/feature/radiosonde/radiosondegui.cpp | 3 ++- .../feature/rigctlserver/rigctlservergui.cpp | 3 ++- .../satellitetracker/satellitetrackergui.cpp | 3 ++- plugins/feature/simpleptt/simplepttgui.cpp | 3 ++- .../feature/startracker/startrackergui.cpp | 3 ++- .../feature/vorlocalizer/vorlocalizergui.cpp | 3 ++- sdrgui/channel/channelgui.cpp | 4 ++-- sdrgui/channel/channelgui.h | 2 +- sdrgui/device/devicegui.cpp | 2 +- sdrgui/device/deviceuiset.cpp | 6 ++--- sdrgui/feature/featuregui.cpp | 7 +++++- sdrgui/feature/featuregui.h | 5 ++-- sdrgui/feature/featureuiset.cpp | 6 +++-- sdrgui/gui/basicfeaturesettingsdialog.cpp | 6 +++++ sdrgui/gui/basicfeaturesettingsdialog.h | 3 +++ sdrgui/gui/basicfeaturesettingsdialog.ui | 24 ++++++++++++++++++- sdrgui/mainspectrum/mainspectrumgui.cpp | 2 +- sdrgui/mainwindow.cpp | 7 +++--- 27 files changed, 87 insertions(+), 32 deletions(-) diff --git a/plugins/feature/afc/afcgui.cpp b/plugins/feature/afc/afcgui.cpp index e6e4d606c..a5924e9ea 100644 --- a/plugins/feature/afc/afcgui.cpp +++ b/plugins/feature/afc/afcgui.cpp @@ -274,6 +274,7 @@ void AFCGUI::onMenuDialogCalled(const QPoint &p) dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); dialog.setReverseAPIFeatureSetIndex(m_settings.m_reverseAPIFeatureSetIndex); dialog.setReverseAPIFeatureIndex(m_settings.m_reverseAPIFeatureIndex); + dialog.setDefaultTitle(m_displayedName); dialog.move(p); dialog.exec(); @@ -285,7 +286,7 @@ void AFCGUI::onMenuDialogCalled(const QPoint &p) m_settings.m_reverseAPIFeatureSetIndex = dialog.getReverseAPIFeatureSetIndex(); m_settings.m_reverseAPIFeatureIndex = dialog.getReverseAPIFeatureIndex(); - setWindowTitle(m_settings.m_title); + setTitle(m_settings.m_title); setTitleColor(m_settings.m_rgbColor); applySettings(); diff --git a/plugins/feature/ais/aisgui.cpp b/plugins/feature/ais/aisgui.cpp index 5bbdd19d8..79a6656cc 100644 --- a/plugins/feature/ais/aisgui.cpp +++ b/plugins/feature/ais/aisgui.cpp @@ -276,6 +276,7 @@ void AISGUI::onMenuDialogCalled(const QPoint &p) dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); dialog.setReverseAPIFeatureSetIndex(m_settings.m_reverseAPIFeatureSetIndex); dialog.setReverseAPIFeatureIndex(m_settings.m_reverseAPIFeatureIndex); + dialog.setDefaultTitle(m_displayedName); dialog.move(p); dialog.exec(); @@ -287,7 +288,7 @@ void AISGUI::onMenuDialogCalled(const QPoint &p) m_settings.m_reverseAPIFeatureSetIndex = dialog.getReverseAPIFeatureSetIndex(); m_settings.m_reverseAPIFeatureIndex = dialog.getReverseAPIFeatureIndex(); - setWindowTitle(m_settings.m_title); + setTitle(m_settings.m_title); setTitleColor(m_settings.m_rgbColor); applySettings(); diff --git a/plugins/feature/antennatools/antennatoolsgui.cpp b/plugins/feature/antennatools/antennatoolsgui.cpp index add2c2cec..b1e55847f 100644 --- a/plugins/feature/antennatools/antennatoolsgui.cpp +++ b/plugins/feature/antennatools/antennatoolsgui.cpp @@ -209,6 +209,7 @@ void AntennaToolsGUI::onMenuDialogCalled(const QPoint &p) dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); dialog.setReverseAPIFeatureSetIndex(m_settings.m_reverseAPIFeatureSetIndex); dialog.setReverseAPIFeatureIndex(m_settings.m_reverseAPIFeatureIndex); + dialog.setDefaultTitle(m_displayedName); dialog.move(p); dialog.exec(); @@ -220,7 +221,7 @@ void AntennaToolsGUI::onMenuDialogCalled(const QPoint &p) m_settings.m_reverseAPIFeatureSetIndex = dialog.getReverseAPIFeatureSetIndex(); m_settings.m_reverseAPIFeatureIndex = dialog.getReverseAPIFeatureIndex(); - setWindowTitle(m_settings.m_title); + setTitle(m_settings.m_title); setTitleColor(m_settings.m_rgbColor); applySettings(); diff --git a/plugins/feature/aprs/aprsgui.cpp b/plugins/feature/aprs/aprsgui.cpp index 2c4430d05..f83e50f60 100644 --- a/plugins/feature/aprs/aprsgui.cpp +++ b/plugins/feature/aprs/aprsgui.cpp @@ -688,6 +688,7 @@ void APRSGUI::onMenuDialogCalled(const QPoint &p) dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); dialog.setReverseAPIFeatureSetIndex(m_settings.m_reverseAPIFeatureSetIndex); dialog.setReverseAPIFeatureIndex(m_settings.m_reverseAPIFeatureIndex); + dialog.setDefaultTitle(m_displayedName); dialog.move(p); dialog.exec(); @@ -699,7 +700,7 @@ void APRSGUI::onMenuDialogCalled(const QPoint &p) m_settings.m_reverseAPIFeatureSetIndex = dialog.getReverseAPIFeatureSetIndex(); m_settings.m_reverseAPIFeatureIndex = dialog.getReverseAPIFeatureIndex(); - setWindowTitle(m_settings.m_title); + setTitle(m_settings.m_title); setTitleColor(m_settings.m_rgbColor); applySettings(); diff --git a/plugins/feature/demodanalyzer/demodanalyzergui.cpp b/plugins/feature/demodanalyzer/demodanalyzergui.cpp index 7846641bd..0fefd371e 100644 --- a/plugins/feature/demodanalyzer/demodanalyzergui.cpp +++ b/plugins/feature/demodanalyzer/demodanalyzergui.cpp @@ -258,6 +258,7 @@ void DemodAnalyzerGUI::onMenuDialogCalled(const QPoint &p) dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); dialog.setReverseAPIFeatureSetIndex(m_settings.m_reverseAPIFeatureSetIndex); dialog.setReverseAPIFeatureIndex(m_settings.m_reverseAPIFeatureIndex); + dialog.setDefaultTitle(m_displayedName); dialog.move(p); dialog.exec(); @@ -269,7 +270,7 @@ void DemodAnalyzerGUI::onMenuDialogCalled(const QPoint &p) m_settings.m_reverseAPIFeatureSetIndex = dialog.getReverseAPIFeatureSetIndex(); m_settings.m_reverseAPIFeatureIndex = dialog.getReverseAPIFeatureIndex(); - setWindowTitle(m_settings.m_title); + setTitle(m_settings.m_title); setTitleColor(m_settings.m_rgbColor); applySettings(); diff --git a/plugins/feature/gs232controller/gs232controllergui.cpp b/plugins/feature/gs232controller/gs232controllergui.cpp index a7aebbdb5..e777ee052 100644 --- a/plugins/feature/gs232controller/gs232controllergui.cpp +++ b/plugins/feature/gs232controller/gs232controllergui.cpp @@ -303,6 +303,7 @@ void GS232ControllerGUI::onMenuDialogCalled(const QPoint &p) dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); dialog.setReverseAPIFeatureSetIndex(m_settings.m_reverseAPIFeatureSetIndex); dialog.setReverseAPIFeatureIndex(m_settings.m_reverseAPIFeatureIndex); + dialog.setDefaultTitle(m_displayedName); dialog.move(p); dialog.exec(); @@ -314,7 +315,7 @@ void GS232ControllerGUI::onMenuDialogCalled(const QPoint &p) m_settings.m_reverseAPIFeatureSetIndex = dialog.getReverseAPIFeatureSetIndex(); m_settings.m_reverseAPIFeatureIndex = dialog.getReverseAPIFeatureIndex(); - setWindowTitle(m_settings.m_title); + setTitle(m_settings.m_title); setTitleColor(m_settings.m_rgbColor); applySettings(); diff --git a/plugins/feature/jogdialcontroller/jogdialcontrollergui.cpp b/plugins/feature/jogdialcontroller/jogdialcontrollergui.cpp index 9757e4919..3db3ca647 100644 --- a/plugins/feature/jogdialcontroller/jogdialcontrollergui.cpp +++ b/plugins/feature/jogdialcontroller/jogdialcontrollergui.cpp @@ -248,6 +248,7 @@ void JogdialControllerGUI::onMenuDialogCalled(const QPoint &p) dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); dialog.setReverseAPIFeatureSetIndex(m_settings.m_reverseAPIFeatureSetIndex); dialog.setReverseAPIFeatureIndex(m_settings.m_reverseAPIFeatureIndex); + dialog.setDefaultTitle(m_displayedName); dialog.move(p); dialog.exec(); @@ -259,7 +260,7 @@ void JogdialControllerGUI::onMenuDialogCalled(const QPoint &p) m_settings.m_reverseAPIFeatureSetIndex = dialog.getReverseAPIFeatureSetIndex(); m_settings.m_reverseAPIFeatureIndex = dialog.getReverseAPIFeatureIndex(); - setWindowTitle(m_settings.m_title); + setTitle(m_settings.m_title); setTitleColor(m_settings.m_rgbColor); applySettings(); diff --git a/plugins/feature/map/mapgui.cpp b/plugins/feature/map/mapgui.cpp index fb4e1cce9..41fdfa947 100644 --- a/plugins/feature/map/mapgui.cpp +++ b/plugins/feature/map/mapgui.cpp @@ -882,6 +882,7 @@ void MapGUI::onMenuDialogCalled(const QPoint &p) dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); dialog.setReverseAPIFeatureSetIndex(m_settings.m_reverseAPIFeatureSetIndex); dialog.setReverseAPIFeatureIndex(m_settings.m_reverseAPIFeatureIndex); + dialog.setDefaultTitle(m_displayedName); dialog.move(p); dialog.exec(); @@ -893,7 +894,7 @@ void MapGUI::onMenuDialogCalled(const QPoint &p) m_settings.m_reverseAPIFeatureSetIndex = dialog.getReverseAPIFeatureSetIndex(); m_settings.m_reverseAPIFeatureIndex = dialog.getReverseAPIFeatureIndex(); - setWindowTitle(m_settings.m_title); + setTitle(m_settings.m_title); setTitleColor(m_settings.m_rgbColor); applySettings(); diff --git a/plugins/feature/pertester/pertestergui.cpp b/plugins/feature/pertester/pertestergui.cpp index 87f22b3a7..55e9be8d6 100644 --- a/plugins/feature/pertester/pertestergui.cpp +++ b/plugins/feature/pertester/pertestergui.cpp @@ -200,6 +200,7 @@ void PERTesterGUI::onMenuDialogCalled(const QPoint &p) dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); dialog.setReverseAPIFeatureSetIndex(m_settings.m_reverseAPIFeatureSetIndex); dialog.setReverseAPIFeatureIndex(m_settings.m_reverseAPIFeatureIndex); + dialog.setDefaultTitle(m_displayedName); dialog.move(p); dialog.exec(); @@ -211,7 +212,7 @@ void PERTesterGUI::onMenuDialogCalled(const QPoint &p) m_settings.m_reverseAPIFeatureSetIndex = dialog.getReverseAPIFeatureSetIndex(); m_settings.m_reverseAPIFeatureIndex = dialog.getReverseAPIFeatureIndex(); - setWindowTitle(m_settings.m_title); + setTitle(m_settings.m_title); setTitleColor(m_settings.m_rgbColor); applySettings(); diff --git a/plugins/feature/radiosonde/radiosondegui.cpp b/plugins/feature/radiosonde/radiosondegui.cpp index 235bd15d4..ba68f6b28 100644 --- a/plugins/feature/radiosonde/radiosondegui.cpp +++ b/plugins/feature/radiosonde/radiosondegui.cpp @@ -241,6 +241,7 @@ void RadiosondeGUI::onMenuDialogCalled(const QPoint &p) dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); dialog.setReverseAPIFeatureSetIndex(m_settings.m_reverseAPIFeatureSetIndex); dialog.setReverseAPIFeatureIndex(m_settings.m_reverseAPIFeatureIndex); + dialog.setDefaultTitle(m_displayedName); dialog.move(p); dialog.exec(); @@ -252,7 +253,7 @@ void RadiosondeGUI::onMenuDialogCalled(const QPoint &p) m_settings.m_reverseAPIFeatureSetIndex = dialog.getReverseAPIFeatureSetIndex(); m_settings.m_reverseAPIFeatureIndex = dialog.getReverseAPIFeatureIndex(); - setWindowTitle(m_settings.m_title); + setTitle(m_settings.m_title); setTitleColor(m_settings.m_rgbColor); applySettings(); diff --git a/plugins/feature/rigctlserver/rigctlservergui.cpp b/plugins/feature/rigctlserver/rigctlservergui.cpp index 0ba6772b7..352a36076 100644 --- a/plugins/feature/rigctlserver/rigctlservergui.cpp +++ b/plugins/feature/rigctlserver/rigctlservergui.cpp @@ -287,6 +287,7 @@ void RigCtlServerGUI::onMenuDialogCalled(const QPoint &p) dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); dialog.setReverseAPIFeatureSetIndex(m_settings.m_reverseAPIFeatureSetIndex); dialog.setReverseAPIFeatureIndex(m_settings.m_reverseAPIFeatureIndex); + dialog.setDefaultTitle(m_displayedName); dialog.move(p); dialog.exec(); @@ -298,7 +299,7 @@ void RigCtlServerGUI::onMenuDialogCalled(const QPoint &p) m_settings.m_reverseAPIFeatureSetIndex = dialog.getReverseAPIFeatureSetIndex(); m_settings.m_reverseAPIFeatureIndex = dialog.getReverseAPIFeatureIndex(); - setWindowTitle(m_settings.m_title); + setTitle(m_settings.m_title); setTitleColor(m_settings.m_rgbColor); applySettings(); diff --git a/plugins/feature/satellitetracker/satellitetrackergui.cpp b/plugins/feature/satellitetracker/satellitetrackergui.cpp index 3a84b41ab..fa0c4418f 100644 --- a/plugins/feature/satellitetracker/satellitetrackergui.cpp +++ b/plugins/feature/satellitetracker/satellitetrackergui.cpp @@ -362,6 +362,7 @@ void SatelliteTrackerGUI::onMenuDialogCalled(const QPoint &p) dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); dialog.setReverseAPIFeatureSetIndex(m_settings.m_reverseAPIFeatureSetIndex); dialog.setReverseAPIFeatureIndex(m_settings.m_reverseAPIFeatureIndex); + dialog.setDefaultTitle(m_displayedName); dialog.move(p); dialog.exec(); @@ -373,7 +374,7 @@ void SatelliteTrackerGUI::onMenuDialogCalled(const QPoint &p) m_settings.m_reverseAPIFeatureSetIndex = dialog.getReverseAPIFeatureSetIndex(); m_settings.m_reverseAPIFeatureIndex = dialog.getReverseAPIFeatureIndex(); - setWindowTitle(m_settings.m_title); + setTitle(m_settings.m_title); setTitleColor(m_settings.m_rgbColor); applySettings(); diff --git a/plugins/feature/simpleptt/simplepttgui.cpp b/plugins/feature/simpleptt/simplepttgui.cpp index e92a2c7b4..163094290 100644 --- a/plugins/feature/simpleptt/simplepttgui.cpp +++ b/plugins/feature/simpleptt/simplepttgui.cpp @@ -307,6 +307,7 @@ void SimplePTTGUI::onMenuDialogCalled(const QPoint &p) dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); dialog.setReverseAPIFeatureSetIndex(m_settings.m_reverseAPIFeatureSetIndex); dialog.setReverseAPIFeatureIndex(m_settings.m_reverseAPIFeatureIndex); + dialog.setDefaultTitle(m_displayedName); dialog.move(p); dialog.exec(); @@ -318,7 +319,7 @@ void SimplePTTGUI::onMenuDialogCalled(const QPoint &p) m_settings.m_reverseAPIFeatureSetIndex = dialog.getReverseAPIFeatureSetIndex(); m_settings.m_reverseAPIFeatureIndex = dialog.getReverseAPIFeatureIndex(); - setWindowTitle(m_settings.m_title); + setTitle(m_settings.m_title); setTitleColor(m_settings.m_rgbColor); applySettings(); diff --git a/plugins/feature/startracker/startrackergui.cpp b/plugins/feature/startracker/startrackergui.cpp index 243bb41c4..33db89876 100644 --- a/plugins/feature/startracker/startrackergui.cpp +++ b/plugins/feature/startracker/startrackergui.cpp @@ -470,6 +470,7 @@ void StarTrackerGUI::onMenuDialogCalled(const QPoint &p) dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); dialog.setReverseAPIFeatureSetIndex(m_settings.m_reverseAPIFeatureSetIndex); dialog.setReverseAPIFeatureIndex(m_settings.m_reverseAPIFeatureIndex); + dialog.setDefaultTitle(m_displayedName); dialog.move(p); dialog.exec(); @@ -481,7 +482,7 @@ void StarTrackerGUI::onMenuDialogCalled(const QPoint &p) m_settings.m_reverseAPIFeatureSetIndex = dialog.getReverseAPIFeatureSetIndex(); m_settings.m_reverseAPIFeatureIndex = dialog.getReverseAPIFeatureIndex(); - setWindowTitle(m_settings.m_title); + setTitle(m_settings.m_title); setTitleColor(m_settings.m_rgbColor); applySettings(); diff --git a/plugins/feature/vorlocalizer/vorlocalizergui.cpp b/plugins/feature/vorlocalizer/vorlocalizergui.cpp index 16f2948bb..63648aa2e 100644 --- a/plugins/feature/vorlocalizer/vorlocalizergui.cpp +++ b/plugins/feature/vorlocalizer/vorlocalizergui.cpp @@ -1189,6 +1189,7 @@ void VORLocalizerGUI::onMenuDialogCalled(const QPoint &p) dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); dialog.setReverseAPIFeatureSetIndex(m_settings.m_reverseAPIFeatureSetIndex); dialog.setReverseAPIFeatureIndex(m_settings.m_reverseAPIFeatureIndex); + dialog.setDefaultTitle(m_displayedName); dialog.move(p); dialog.exec(); @@ -1200,7 +1201,7 @@ void VORLocalizerGUI::onMenuDialogCalled(const QPoint &p) m_settings.m_reverseAPIFeatureSetIndex = dialog.getReverseAPIFeatureSetIndex(); m_settings.m_reverseAPIFeatureIndex = dialog.getReverseAPIFeatureIndex(); - setWindowTitle(m_settings.m_title); + setTitle(m_settings.m_title); setTitleColor(m_settings.m_rgbColor); applySettings(); diff --git a/sdrgui/channel/channelgui.cpp b/sdrgui/channel/channelgui.cpp index f04055bcc..63297c098 100644 --- a/sdrgui/channel/channelgui.cpp +++ b/sdrgui/channel/channelgui.cpp @@ -72,7 +72,7 @@ ChannelGUI::ChannelGUI(QWidget *parent) : m_moveButton->setFixedSize(20, 20); QIcon moveIcon(":/exit.png"); m_moveButton->setIcon(moveIcon); - m_moveButton->setToolTip("Move to workspace"); + m_moveButton->setToolTip("Move to another workspace"); m_shrinkButton = new QPushButton(); m_shrinkButton->setFixedSize(20, 20); @@ -329,7 +329,7 @@ void ChannelGUI::setDisplayedame(const QString& name) m_displayedName = name; } -void ChannelGUI::setToolTip(const QString& tooltip) +void ChannelGUI::setIndexToolTip(const QString& tooltip) { m_indexLabel->setToolTip(tr("%1 / %2").arg(tooltip).arg(m_displayedName)); } diff --git a/sdrgui/channel/channelgui.h b/sdrgui/channel/channelgui.h index 049bd21d3..a933ac339 100644 --- a/sdrgui/channel/channelgui.h +++ b/sdrgui/channel/channelgui.h @@ -78,7 +78,7 @@ public: void setDeviceType(DeviceType type); void setDisplayedame(const QString& name); DeviceType getDeviceType() const { return m_deviceType; } - void setToolTip(const QString& tooltip); + void setIndexToolTip(const QString& tooltip); void setIndex(int index); int getIndex() const { return m_channelIndex; } void setDeviceSetIndex(int index); diff --git a/sdrgui/device/devicegui.cpp b/sdrgui/device/devicegui.cpp index 231ee3a4f..6bcb2c671 100644 --- a/sdrgui/device/devicegui.cpp +++ b/sdrgui/device/devicegui.cpp @@ -94,7 +94,7 @@ DeviceGUI::DeviceGUI(QWidget *parent) : m_moveButton->setFixedSize(20, 20); QIcon moveIcon(":/exit.png"); m_moveButton->setIcon(moveIcon); - m_moveButton->setToolTip("Move to workspace"); + m_moveButton->setToolTip("Move to another workspace"); m_shrinkButton = new QPushButton(); m_shrinkButton->setFixedSize(20, 20); diff --git a/sdrgui/device/deviceuiset.cpp b/sdrgui/device/deviceuiset.cpp index 6714974cc..7ca335251 100644 --- a/sdrgui/device/deviceuiset.cpp +++ b/sdrgui/device/deviceuiset.cpp @@ -338,7 +338,7 @@ void DeviceUISet::loadRxChannelSettings(const Preset *preset, PluginAPI *pluginA rxChannelGUI->setDeviceType(ChannelGUI::DeviceRx); rxChannelGUI->setDeviceSetIndex(m_deviceSetIndex); rxChannelGUI->setIndex(channelAPI->getIndexInDeviceSet()); - rxChannelGUI->setToolTip(m_deviceAPI->getSamplingDeviceDisplayName()); + rxChannelGUI->setIndexToolTip(m_deviceAPI->getSamplingDeviceDisplayName()); QObject::connect( rxChannelGUI, @@ -466,7 +466,7 @@ void DeviceUISet::loadTxChannelSettings(const Preset *preset, PluginAPI *pluginA txChannelGUI->setDeviceType(ChannelGUI::DeviceRx); txChannelGUI->setDeviceSetIndex(m_deviceSetIndex); txChannelGUI->setIndex(channelAPI->getIndexInDeviceSet()); - txChannelGUI->setToolTip(m_deviceAPI->getSamplingDeviceDisplayName()); + txChannelGUI->setIndexToolTip(m_deviceAPI->getSamplingDeviceDisplayName()); QObject::connect( txChannelGUI, @@ -639,7 +639,7 @@ void DeviceUISet::loadMIMOChannelSettings(const Preset *preset, PluginAPI *plugi channelGUI->setDeviceType(ChannelGUI::DeviceRx); channelGUI->setDeviceSetIndex(m_deviceSetIndex); channelGUI->setIndex(channelAPI->getIndexInDeviceSet()); - channelGUI->setToolTip(m_deviceAPI->getSamplingDeviceDisplayName()); + channelGUI->setIndexToolTip(m_deviceAPI->getSamplingDeviceDisplayName()); QObject::connect( channelGUI, diff --git a/sdrgui/feature/featuregui.cpp b/sdrgui/feature/featuregui.cpp index b9ff810e7..b4d587b8e 100644 --- a/sdrgui/feature/featuregui.cpp +++ b/sdrgui/feature/featuregui.cpp @@ -67,7 +67,7 @@ FeatureGUI::FeatureGUI(QWidget *parent) : m_moveButton->setFixedSize(20, 20); QIcon moveIcon(":/exit.png"); m_moveButton->setIcon(moveIcon); - m_moveButton->setToolTip("Move to workspace"); + m_moveButton->setToolTip("Move to another workspace"); m_shrinkButton = new QPushButton(); m_shrinkButton->setFixedSize(20, 20); @@ -260,3 +260,8 @@ void FeatureGUI::setIndex(int index) m_indexLabel->setText(tr("F:%1").arg(m_featureIndex)); } +void FeatureGUI::setDisplayedame(const QString& name) +{ + m_displayedName = name; + m_indexLabel->setToolTip(tr("%1").arg(m_displayedName)); +} diff --git a/sdrgui/feature/featuregui.h b/sdrgui/feature/featuregui.h index ca92b4209..1c7f3e894 100644 --- a/sdrgui/feature/featuregui.h +++ b/sdrgui/feature/featuregui.h @@ -39,8 +39,7 @@ public: enum ContextMenuType { ContextMenuNone, - ContextMenuChannelSettings, - ContextMenuStreamSettings + ContextMenuChannelSettings }; FeatureGUI(QWidget *parent = nullptr); @@ -63,6 +62,7 @@ public: void setTitle(const QString& title); void setIndex(int index); int getIndex() const { return m_featureIndex; } + void setDisplayedame(const QString& name); protected: void closeEvent(QCloseEvent *event); @@ -74,6 +74,7 @@ protected: QString m_helpURL; RollupContents m_rollupContents; ContextMenuType m_contextMenuType; + QString m_displayedName; protected slots: void shrinkWindow(); diff --git a/sdrgui/feature/featureuiset.cpp b/sdrgui/feature/featureuiset.cpp index 54a290ec2..0667053c3 100644 --- a/sdrgui/feature/featureuiset.cpp +++ b/sdrgui/feature/featureuiset.cpp @@ -178,9 +178,11 @@ void FeatureUISet::loadFeatureSetSettings( qPrintable((*featureRegistrations)[i].m_featureIdURI), qPrintable(featureConfig.m_featureIdURI) ); - feature = (*featureRegistrations)[i].m_plugin->createFeature(apiAdapter); - featureGUI = (*featureRegistrations)[i].m_plugin->createFeatureGUI(this, feature); + PluginInterface *pluginInterface = (*featureRegistrations)[i].m_plugin; + feature = pluginInterface->createFeature(apiAdapter); + featureGUI = pluginInterface->createFeatureGUI(this, feature); registerFeatureInstance(featureGUI, feature); + featureGUI->setDisplayedame(pluginInterface->getPluginDescriptor().displayedName); break; } } diff --git a/sdrgui/gui/basicfeaturesettingsdialog.cpp b/sdrgui/gui/basicfeaturesettingsdialog.cpp index f7695af78..8ff5b51d6 100644 --- a/sdrgui/gui/basicfeaturesettingsdialog.cpp +++ b/sdrgui/gui/basicfeaturesettingsdialog.cpp @@ -25,6 +25,12 @@ void BasicFeatureSettingsDialog::setTitle(const QString& title) ui->title->blockSignals(false); } +void BasicFeatureSettingsDialog::on_titleReset_clicked() +{ + ui->title->setText(m_defaultTitle); + m_title = ui->title->text(); +} + void BasicFeatureSettingsDialog::on_title_editingFinished() { m_title = ui->title->text(); diff --git a/sdrgui/gui/basicfeaturesettingsdialog.h b/sdrgui/gui/basicfeaturesettingsdialog.h index 4891212e0..20617ef3c 100644 --- a/sdrgui/gui/basicfeaturesettingsdialog.h +++ b/sdrgui/gui/basicfeaturesettingsdialog.h @@ -45,10 +45,12 @@ public: void setReverseAPIPort(uint16_t port); void setReverseAPIFeatureSetIndex(uint16_t featureSetIndex); void setReverseAPIFeatureIndex(uint16_t featureIndex); + void setDefaultTitle(const QString& title) { m_defaultTitle = title; } bool hasChanged() const { return m_hasChanged; } private slots: void on_title_editingFinished(); + void on_titleReset_clicked(); void on_reverseAPI_toggled(bool checked); void on_reverseAPIAddress_editingFinished(); void on_reverseAPIPort_editingFinished(); @@ -64,6 +66,7 @@ private: uint16_t m_reverseAPIPort; uint16_t m_reverseAPIFeatureSetIndex; uint16_t m_reverseAPIFeatureIndex; + QString m_defaultTitle; bool m_hasChanged; }; diff --git a/sdrgui/gui/basicfeaturesettingsdialog.ui b/sdrgui/gui/basicfeaturesettingsdialog.ui index 0e72cd7fc..99349f362 100644 --- a/sdrgui/gui/basicfeaturesettingsdialog.ui +++ b/sdrgui/gui/basicfeaturesettingsdialog.ui @@ -48,6 +48,26 @@ + + + + + 24 + 24 + + + + Reset title to feature type name + + + + + + + :/recycle.png:/recycle.png + + + @@ -192,7 +212,9 @@ - + + + buttonBox diff --git a/sdrgui/mainspectrum/mainspectrumgui.cpp b/sdrgui/mainspectrum/mainspectrumgui.cpp index ef98a195a..a4d082977 100644 --- a/sdrgui/mainspectrum/mainspectrumgui.cpp +++ b/sdrgui/mainspectrum/mainspectrumgui.cpp @@ -67,7 +67,7 @@ MainSpectrumGUI::MainSpectrumGUI(GLSpectrum *spectrum, GLSpectrumGUI *spectrumGU m_moveButton->setFixedSize(20, 20); QIcon moveIcon(":/exit.png"); m_moveButton->setIcon(moveIcon); - m_moveButton->setToolTip("Move to workspace"); + m_moveButton->setToolTip("Move to another workspace"); m_shrinkButton = new QPushButton(); m_shrinkButton->setFixedSize(20, 20); diff --git a/sdrgui/mainwindow.cpp b/sdrgui/mainwindow.cpp index 16057c890..93abdaa1c 100644 --- a/sdrgui/mainwindow.cpp +++ b/sdrgui/mainwindow.cpp @@ -2153,7 +2153,7 @@ void MainWindow::channelMoveToDeviceSet(ChannelGUI *gui, int dsIndexDestination) gui->setIndex(channelAPI->getIndexInDeviceSet()); gui->setDeviceSetIndex(dsIndexDestination); DeviceAPI *destDeviceAPI = destDeviceUI->m_deviceAPI; - gui->setToolTip(destDeviceAPI->getSamplingDeviceDisplayName()); + gui->setIndexToolTip(destDeviceAPI->getSamplingDeviceDisplayName()); channelAPI->setDeviceAPI(destDeviceAPI); deviceUI->removeChannelMarker(&gui->getChannelMarker()); destDeviceUI->addChannelMarker(&gui->getChannelMarker()); @@ -2338,7 +2338,7 @@ void MainWindow::channelDuplicateToDeviceSet(ChannelGUI *sourceChannelGUI, int d ); destChannelGUI->setDeviceSetIndex(dsIndexDestination); - destChannelGUI->setToolTip(destDeviceAPI->getSamplingDeviceDisplayName()); + destChannelGUI->setIndexToolTip(destDeviceAPI->getSamplingDeviceDisplayName()); destChannelGUI->setWorkspaceIndex(workspace->getIndex()); qDebug("MainWindow::channelDuplicate: adding %s to workspace #%d", qPrintable(destChannelGUI->getTitle()), workspace->getIndex()); @@ -2451,7 +2451,7 @@ void MainWindow::channelAddClicked(Workspace *workspace, int deviceSetIndex, int ); gui->setDeviceSetIndex(deviceSetIndex); - gui->setToolTip(deviceAPI->getSamplingDeviceDisplayName()); + gui->setIndexToolTip(deviceAPI->getSamplingDeviceDisplayName()); gui->setWorkspaceIndex(workspace->getIndex()); qDebug("MainWindow::channelAddClicked: adding %s to workspace #%d", qPrintable(gui->getTitle()), workspace->getIndex()); @@ -2475,6 +2475,7 @@ void MainWindow::featureAddClicked(Workspace *workspace, int featureIndex) featureUISet->registerFeatureInstance(gui, feature); gui->setIndex(feature->getIndexInFeatureSet()); gui->setWorkspaceIndex(workspace->getIndex()); + gui->setDisplayedame(pluginInterface->getPluginDescriptor().displayedName); workspace->addToMdiArea((QMdiSubWindow*) gui); QObject::connect( From 0779ab0c70305c182592850b7fd935fbaa8b705a Mon Sep 17 00:00:00 2001 From: f4exb Date: Mon, 18 Apr 2022 18:33:37 +0200 Subject: [PATCH 040/115] Massive UI revamping (v7): spectrum documentation --- doc/img/MainWindow_spectrum_gui.png | Bin 18558 -> 14822 bytes doc/img/MainWindow_spectrum_gui.xcf | Bin 53068 -> 47511 bytes doc/img/MainWindow_spectrum_gui_A.png | Bin 5974 -> 5216 bytes doc/img/MainWindow_spectrum_gui_A.xcf | Bin 25836 -> 23800 bytes doc/img/MainWindow_spectrum_gui_B.png | Bin 8330 -> 5094 bytes doc/img/MainWindow_spectrum_gui_B.xcf | Bin 35474 -> 23721 bytes doc/img/MainWindow_spectrum_gui_D.png | Bin 0 -> 4357 bytes doc/img/MainWindow_spectrum_gui_D.xcf | Bin 0 -> 15610 bytes doc/img/MainWindow_spectrum_gui_narrow.png | Bin 0 -> 73422 bytes doc/img/MainWindow_spectrum_gui_wide.png | Bin 0 -> 134305 bytes plugins/channelmimo/interferometer/readme.md | 2 +- plugins/channelrx/chanalyzer/readme.md | 2 +- plugins/channelrx/demodbfm/readme.md | 4 + plugins/channelrx/demodchirpchat/readme.md | 2 +- plugins/channelrx/demodssb/readme.md | 2 +- plugins/channelrx/filesink/readme.md | 2 +- plugins/channelrx/freqtracker/readme.md | 2 +- plugins/channelrx/sigmffilesink/readme.md | 2 +- plugins/channelrx/udpsink/readme.md | 2 +- plugins/channeltx/modfreedv/readme.md | 2 +- plugins/channeltx/modssb/readme.md | 2 +- plugins/channeltx/udpsource/readme.md | 2 +- plugins/feature/demodanalyzer/readme.md | 2 +- plugins/samplemimo/testmosync/readme.md | 2 +- plugins/samplesink/testsink/readme.md | 2 +- sdrgui/gui/spectrum.md | 315 +++++++++++++++++++ sdrgui/{ => gui}/spectrumcalibration.md | 0 sdrgui/{ => gui}/spectrummarkers.md | 0 sdrgui/mainspectrum/readme.md | 2 +- 29 files changed, 334 insertions(+), 15 deletions(-) create mode 100644 doc/img/MainWindow_spectrum_gui_D.png create mode 100644 doc/img/MainWindow_spectrum_gui_D.xcf create mode 100644 doc/img/MainWindow_spectrum_gui_narrow.png create mode 100644 doc/img/MainWindow_spectrum_gui_wide.png create mode 100644 sdrgui/gui/spectrum.md rename sdrgui/{ => gui}/spectrumcalibration.md (100%) rename sdrgui/{ => gui}/spectrummarkers.md (100%) diff --git a/doc/img/MainWindow_spectrum_gui.png b/doc/img/MainWindow_spectrum_gui.png index f55f68dff9c9e538bbf4ff21105fb2f92f43faa5..274c9e8fbe07b979b592cd0dfb6b5b8a4d01e001 100644 GIT binary patch literal 14822 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 MrTDs3!Z6@}0mc+Tvj6}9 literal 18558 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 diff --git a/doc/img/MainWindow_spectrum_gui.xcf b/doc/img/MainWindow_spectrum_gui.xcf index 58bb3a34a687e06b150337dffa7a07982cc22401..b3a130d2bf12dafe8c723d5995dfcfe8d160616f 100644 GIT binary patch literal 47511 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 literal 53068 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 uzsnwc=*5R^`OupWeYgi!?AoW+m; diff --git a/doc/img/MainWindow_spectrum_gui_A.png b/doc/img/MainWindow_spectrum_gui_A.png index aa3ed9bfe1c86be469e0ecfb4810c18427005df4..82c25a13a84e2e0f276294154c9b6c3556ab17e8 100644 GIT binary patch delta 4898 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 U8oEy^qW}N^07*qoM6N<$f+b|V$^ZZW delta 5649 zcmZu#cT^M2+eJY@Is(#*q97fmx1e+dL{O0;O^_ljfHVm#(wl~+NDZRWd+)tU3A{n7 z^j?xcD1kuWx1RI;`OP_d&dkov?#^?c=iYl}@!o`Zydt2a)Afuw;xhN0rWw~R-*WtS zMN5JQ^oBtA z&HFs^f8m!|mrzVY@Myl|@HPj9^qeeeAne<4NzC_Glu^&fNBN}&za{--hOO*mZQsKz zH>Ut|2u4HT;3nzS+xwzYdg*?lejiY>@>nKq)5pfq$ml*jj`)*=otQrNj978%W{2^i zpBvFXy5CfUexb9!`f^y+4iM+Tgc zubNtV=kr0}uKX|)-wDm@EmxrC$`CBk(%rd&KO5kzuXl6)%>!*9rGc~hb8_2&T}h=SNcawjpW)pZb_Q`oZo?9G3dXuZ89Z9B1wLor?JL5*%1i| z3Hcjc8f^`*1GZE3CLpQe3J=xVRobhJd`{i4w4~`Pf&h#7V~s~I{ASQ|{(j8fS$+Gy z-iq2A(yY;*l+lKdJQ7egrV|O!p(q+mKV?i^9AB&we1|0Vwql$kIlalV1b+rCO+!O= zO0|cj+{`-i zW#G*Y&er!;U~fsx2EthpWMkaQGe5Bhd}jf86-y(EO@^}QFaf5VJx2V%U_|NwCZJh0 zXnlWg;DRTS*Lz_l@Hp*SXP>U%4Tw_evy-SOfg4$CRY}*w;U_;wLYLMfZ&k1YVLc%T z?%%PCi%(51xa=;wJoVq9$aCkZN!>zfD;T)Q?yf=kSl0ovgOq~Z+f=;!#Yz`6(GJ|q z^ziV~(C@bNe*W^SFt-8$;-veMs|o0PNI%7ghPq!Jft-BkR zCDlO$KW+tJPkF!K;AbeXCbWm7DRVN*CsnKoFbik`IU@8Uo`2apw%zNwJt*gdf^GCf?x2=p%WjR*T~2Ug zxUSlCK=-sIiftc8A-(4=TL=AdkAl8ORDco5!&;1pERu5VorRCgrUp&XJ};|#k#t+_ zJK)RqU-vcQ%e=);zbsAqkgW3V4o$AY=f?(LFTz4;fwHaw4wIBCR(5K7^5+CAo9>4j zgdwbS91G-jNqg0S%@BGNZ5BmGp~}geqM%yuc_CQ7h@H_gzQIF2R~G(JPR0_yVt9gr zx$=j@O_0gXEv1hUF7M~NmCm^Uj#71ud(rzmx_t;i z|BI!3!l+&68GCTSFy9-)STpF!yt`kq>H6_a*w{{i+mAhvY!E-0=rlA|6AxnWFe8D7 z@R3&2cK%vsPvVF8mz300q9#o~+MA56Ji@@u6BptH>ruHK8rG17;C{Lko}{9D(C6jT z{@-IN#6-5X1Ka4VT-Qcb2+!Y4Ig(}Qe6exU36hOrCB5VMtF~+2M+XrVoBT)TidAxK za?bcQhfy0jM#ix$B+`3x^N-!7b(eIJuCZn{*2?0l}RHDvv(3$#*CL% zaO8*WTZz-EQ{O5j6U8kW7oqZ!G(cAom3^833k%EcbX6ofJ##1fq&WCv(XUm1y{v1& zoVwS3im0+IZdF=cobE9syLQbqm!}WT>#*4VQGB7TvNEwH0FUciYN-C?S}$H~txCkP znino)6=ls!-_%E~md3lx^!<`CV@1mK_sP^2NkCxw(`#7fw`b z%yxBkH9WWzANVQemA;=}{o1yn$tHodKP)1`6J0a;CXIr#r0Kz((L`cx!-o&ECem@a z3%~O7Wv)|EEl*Y0x)B2aO-)S=ZSCh~W>3aXF|gf${z0DwUbw-#{n$}+WmsifFEL2$SEcp8akWhpur*8$#hzNrVM>eYVM8{GBNSSZGQgy z!%3eeuANN(RrETBskAtYPP|BIDB-QQu#_Fr^I2FS?n~`N0&o^fhH)Pc{4pIZ?X~Rc z>hTTd4Qk?;^zcomJUMqIcm$GHtFw&INjv!vCY}6M-Q;p&ma$key9DKfa&~6q;P{>W zD3nA;SJ#>O;qYYNZ{oxup~BHp10h2bnw`yeets^gpM=AC>Y=(NX+lCn@6&{)rEv(0 zh?x5*SzL+$^=Ke_?r4!RH0FkUJ@v%}*RDC%d?{&^+|s|(tcoEVu7m&vAwiy=59n3T zC1Y77?0aHZjt0vb);V}!dyi&nU3E1zuZg@WwVSSdOVZuji|glyLwi_WJu^2Sd}S5b zlcO0c@4cNoSVKum`*v@!BP=Y8MAC21+PKVejxQKUE6enDXdG5?E#rvYaMbof$3;im zOqJVcXlfe1eA$zDS6AM1mC|*l+Ge#sfsBmonTbh1Zf#KE!=dBg?&zh1x+&s%sSkR*kZsq_CcfRb-JB@lq@b6DJy(^N|5ork zuhIu?rUmC2j$@Z%Q}DbqY2nLF!X%)_Cny-H%^R59#|vbHg^{_n5q(n5%yo1q)gx#M z=y|d`Di0?q^@6+ZN^Y%NS2dvMI(T5l`CogG*$BI^ERaGh= z>uT0D;#OA$T~eDUF{gO=^eG($J0c1r8tU}9pIbK^qg8RX^XZzd{Ve*JqiFycl?MlCNd1BAsZQJ+7r z{}w5;!(--1cK)4ly$Kl&o^w| z@8zU@$Hm6hU+=LJ6&2O)R(H_DYPGkOhxqVQb$QuFF0$*9re=5_eUHU^C5gTA*>F*> zqgK|{!qyRlDdLh@xg-oQ2t%I6@Gvo{o0)OU*1Gb8S>}JRUSZQkURYGLHD16ZBqa30 z*cdlu3m*d!mvx`tt=(vJot4!?{WL&8K7BE56@4`w?q9 zwghEhV)}>nV?RIn)YR1GGfdO?`1tGp2v)C^h7rUx)ZeA*o79NGr+}mNh|jlrp3-g7 zW0HG9!NA;(b?XM5!s$>JylcsNzS*A^vw_9Jozhm<*6urw@{!YV^@C*xmFGHg$Xi`s zpTKT0v#@}?w|#61#%p%l!PZ-|60v@Kp{A~$s^~3zuz}p@PvB2EJ`Ioz0`X6k zcDex;_S@k!nIj1tDp%hY>=m0-Tm`_^3+F|G0Dc3N7e7^0RAgFieZ{sn&IY$S zaI}+{ms_zhmbZ7%8MEKCBlk@e2zJ`>iGb+8m+*mqh#(HkA+3 zsuw5xpxpIH(m+lYLkVZ&6BDv0s$$+Q$3stn){hwR{r!EHzx4?=T@eYXsi#$Hf0&S$ zZ|>Z^d*2^tXOGMpp0oli+LQ$ZBEY%@iE4(xdZ3>!dq3#hFQw~_kWh4adAR`gxslN_ z&ZZ&vUp=n^8yiyA?ZI9X0~Sm_z7}pI@N0AEy{~ndHUc^$1?!74OS8D3rA*h&?2viu zhpA~E(dBMDKLN@hx=mlXzlTbj=enDoBD<^i-aE#1buA69AdyW^Pm7wA2LsVef)CzrKKm+RH7}e6=w7F!T#OdX zvL4A$pbeqO;7$0a%nPl7rgd)I5jLZF2E3l0p0DTU!NA*Zj($I4{Wc*aCU#dw<|u<$ z-VhXE517|OZ%Nq?wM)|Ip;l+z+kQDumfG3bEn=|-Mn*SuzlwU8U%EtJOqE%wHu|6e zGk|%ym(%^f78@00yOA(y) z`1yc;dv5}LlDK&3SgNt}MDbD?Komi$;JNT~zP$Weao}F*xa8ZN%Uaew#hyHQqNA_B zHev4Wx_6xd6lND_Zd%dgX~vv8H5C<@$SYX^m;!9wN*~-|rW$Y}BPXwPnXW_}1sUj$ zAM9S#gCMyz?`v85ud(Q%?7-L;o7McrOrlS zAyYU;#MH0QwflcXfkeHNQfArGc(L$2SFhj_pYGl|EsWnC`E&eFBWyau1hDC;siv1X z{>Qf-10tOF-)L0rM9}lj?>2&7a-uEIv!dpnxVV6f%$M)#kyqo>(q=Z9onGN#F)=Y5 zf84hI`z318Sbc$KwStWq*;W)5#!Oc^m>+#v%Q=fEQk}!_x~pRup>twT*C`Q!K!O^1orY!=G>^>8%+*6#$a!g3Bx4&ZX(}NA@z=`!?E61LScmy!};?^%xu6RUaN@#fBq4l2YjC=ozt78Qw2x~G@qrF_yvxe%Vc)72+z3#4{*bWkB;6UIAE(LXS-X0QD!?KgGm#>U1; zO-=KB^72!sLNv!ZBV4$Iiy-T|IzlZ41%<7_yJqDjoLbgCf&QDy^^7}9xL&Ik3!ZeH zMuo3QH4{J@ntX<)@C_3u{Lis~6krwSlq+{STw?6?*2TLD?H(Opl5VHS_4%Mpn**_W zer)I)ZBQ9>@;*uxKWW)y-nkZjzJ@n^IJ%Wy_uz4Tb9{?~s8F09-@AS*(J>sZKTK$cthNo)%CC=`bYd4Z zlV7QLf8;5`bZ5=?P;FJWXVf&zHCF%IKdEn8C|~udD%yz>MBmd7VG?>KKOi`ZaG&i^ z*DTL&*WETvrdNfKu~0WRx;5#(<9oU?xzXj&XSpC|-Repq6cqo`ahSFIF^NLEkyXj! zotj?u7|hg_Oy$EmFCV`i0qMQc9ioOt_tpRBq%LWPi7u&=>DEPFdtS@B$0$Y7*rD8vR)OD zbrBIk4@55_D7}L00U~;l5fN5zf=D8IQC1RB(v$t~|NCC_q8AAs{P(%<{r>;scMrRf zL2+zA?5z=9eYLWv3<-R*RFjS+q-&6r?~}O=WT2j$b)AenBj?%4iefUphfLOz#&6Rv zV~?3F2K*~$BM9@M_(EHO#b9`c^uv__E6(~f)v-r_yG(-hsk@C?MX>Bt_`Jw`^|bBE@8 z9>ZZWtDltj!(-^#qlJ9|iA*w1rZULKY*3TPcT)jC%Mmj-Q5?$r9ZD$TrH6z3>P;%Wkhv8;iAC zq&e?-yU{7U-Dp5Nlz1meF(+>~hHI}d)4~cD%;UES)En zHRzOOzQq&99@97tt}4FzA2oI;zS%ggSgM4-#<+T_`n!_AUDYRv3)zpC4V*G@O+P9K zNk5E0{Au;{XARp=yr=r2M`SI&84XR8SG*tvK@s+?_*Ge29@6BRI=|Fd#~)N<7uB1d z!#D5~e^8Cz5N%Lzm2FM>ozSg>^{^SX!!CFLo`e_S4fue)SlL>5eV-U@zQrz4e=)n+ z&>tAvnn%eL9&l@3iY+~n=0g85S;@#%1CCk00aJrH0RR91 delta 3156 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 literal 8330 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 I3FDxD0o)L0ApigX diff --git a/doc/img/MainWindow_spectrum_gui_B.xcf b/doc/img/MainWindow_spectrum_gui_B.xcf index cb7e2e2c8ef09ac908f3831be0e7f2f9153620c9..42f30055a837d9046cdc5c807b377da025a79331 100644 GIT binary patch delta 3036 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 g%kZV|Um%~+_h)%_W&!*$j~TL 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 literal 0 HcmV?d00001 diff --git a/doc/img/MainWindow_spectrum_gui_wide.png b/doc/img/MainWindow_spectrum_gui_wide.png new file mode 100644 index 0000000000000000000000000000000000000000..2e5ac34fd6db49f96703b7f397f433ad52ae398c GIT binary patch literal 134305 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 Z4&194{ef^K5y83QpACe|B&dxcmctA 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 z z74-W!|9@aUlka;TPZpf`x>m+L|ypcrk36X^aK zdLfq6IltFCUYhnZb%6D%2NNwpwEg_|Y`xav4cQJ5kZvb+3)-WiujE_+L!1!&%TU)3 zA_XX|)rW_NPyuU~*+0pitt%$K7pnkjRJLBef-2YW1fe#Ao+n|jRPBK5Sw=3keAs#N`22BLG}A zoQU1-px5B4&>-CImnMpeQDdfTX=Bv!fFUcmySrb1G2I%Sc8)j$LwvH*AQ#7OyLNI}e2LmRKfP5anuYicUk|$+r}|Yx8G3WrZ7Vj4 zx!!Is`zLh{jHc`=ISr8?+dGKJ`q`dsYas37;NXCa4bFcHLM}U7ICqK=Fvh(!+k!J) z(NM^N6YqB9DoR)!<&YJp?c&cI;AVsJ9wIt1fSy9y@L37%_u;3T+)S+#Lhj|XIW}#i zhKLU^Eq+!}?-9$wP>C3iJheoS6*#ePdlp&@$M)!lV-pk0VzAAAz5vReRm{OR4r_w} zWj3J!z=oUIoW4}Ak^W8kD@BCD%XEfIN4>NYAb>{7)Q8qGx!}4N#ata z=2EB4VF>!eUat~FDB z6_*xJH3a^-HCPrE6*i#s<#X3Vg2SvYKY*{Psj2jXHNT?b6l5h};N;UZq!6iK133w@ zBd_b}FQC*GOlyKHiPOgRZantEHH39xVRgkmaO8!=-kgH^W{))+rBg_Wo(HU%zxUH2z(Z2*!9i51aFm+Jhi3|L@7; z8=(G?%FhYfj&R;esBUsAf?RC7pu)2`9fzA=La!I}?lXFcNo_bSO!LC)bm(DU1YcM| zgkTsRVOUZq_|BlfT)OUyd%IJV;3(g6Xu^HDV338#c3hyB*u2BQ-}xZOer~6 zd;uR`{d7DZIY)4pz)_Qd&A`>Cxc0?1wvLKf>RBjgyqVXbm@mj~x&u=!>Isr$Fq#X% zdKhyXoA%?1mhNb9uWvfRdKG)qz-7B8{}R}oy5@0Q4tL2HnW7Ho3!rv@*pp$)gx9tA z&?y+5=nzSP*AJk7$SW%1qgw$1pc;YwLW3H0Xj-I4FyRYamRVp4{PTqK`I4F1?&1zF z21wBL>cGfC-jw0IOWf23@LR2XJ94k`MR+74$u|ZuiCs!r4kv?x{Y?9fy=o`Ry?x%@OS-X4I$B{ z`H&if$0*yxR6$Y4Gjs zRfZ+s*E7)9(F)mlx&6Z>6erZ*QQk4Wy7QIa)RnVX8CJZSlL-BjWHDNbZSA@Vf790y zXT_1al4j^ChjIJ#G1C=bzJNePCi_RWKn@WAwrl1-h(>ZFG_526l)}mNd$}46uW^Cx zZ_{=&&+&C$O;Z!FI4(ZEzkm)|Q$iQKpbUIZWN(VOruf-#MVx{7ANO(}~tPWa!?O0X3zn2tDx_qJO}#Bbtiyxf|E zCAWcHs|mY;w9qq};d~$w@!NJZNb+?uf(Mhf=;%tjfI*02P6X>SJhD4>WU%M;0rUp2 zaci?!w+JQ8umqq?hvKN{ly-sUnHqrrt3SP>G8yXVEztjfi>nsMBY)HM0!J(zm^0)o z?;Kv9)?Ze)g22*g#fn(W)9{i65)@ow$l4{K-LBx0k?m`Bd@teR8Z)qJ37y)y--(jm_ew zOG|^*(ll_RfzN*T^t{;5}T=FCzo9$-^LT1E}LS==KxG^a^uSCc`Hy#L71{wKrZtU(_sl=IN ziDOHMMAFdGw(@Nxo2XAYAPw5R%QlA?|K{}c_8;U{XG*14pNVus?;9zVNyVs$TR#)} z_Q39Y{uA%eq!VdcqY6n|jUfoVSCJxt7F7ngVJI7CJ}-Rb?!2m1*He^rfu8VsjSWER zsN>6cIn*w2u(SVOZ2{4-a&|itmDt?e3`!;zb;|(4f`fxYuf)jh}lEniW-g zmGz*y43#kgK?+5E#~J8D$5v2e$I8amrhm587m>`U!)Y;{N0aGh1e8x2r?mrb1^|%@ z9FcIG7Khzotr}A>-KWex_pdWNRkgKwflmB)no99lVYL!JfyrG zR}Z4caaigXVW+B)id#R}HsV9pn__4K{vE!0c!`T?EQu+bhDzP0%Rei&XF&WL8yk~i zWbaV~n%8Lubdio+PUp+%G2vPhU+^p8_RBJqKp_4YqAT(z*b*IYT{z4J`h2*H(6U_HzSsb|L2|ec1l!=&gCR z@x|Ao@-AkQzW52UU{0bKkwmZocXZ_tg70>3B|*5$(O6v1*qifoa?#)c^f@ViZ@)j+eh3u#14QgOZrpXi61` zV6b4QPNUDh4>krMeew2xL=(-@8hk5@5Z!v+D``mO-^s%um<(+KW&c;iJrfrWbOCKX z-L5m!(-R1Jd)e4jNag|q96}4^`5PKVtHqyPn|8`ISfUVIav4&3P!UvzXy*fS#j`+u z30A&Q%^UN1O5-OqGZa(>q98oRs{8$u&q%C(7JrZg8&hH;YJ%5`kngujsLoa$U%lAm zLe2oPst8lbvcD(rt-{Z@X^NdP&SJtL)Ox+Un=iOuk2c{pdWPgl-x&0~{td<83xDVR zDIdj6nE$2mt&zHmlMVM5!0I;B$huuiE?SOM4e|3c#{6rZvlDpv5qh3JIZ_0-pBF~0DQiO5egz69 z_rGf>m_}=F_dH3NH$`TV&oDO zWst}Kr*u_OQ2{2}`tzR#H{%FZX`P4f#=-Dm%+b&5PZF-nR`noI|7G_4%FD}3 zkl5iw&fWN(HmIRQK{Z=$5Q$HuG!N)7`Cws~An2_wasZ40_NHn}UBns_eckH)(!tv}!6S1x$& zjrK)f=Ot?X>#(8Lop8%5nP_Cw(Ji-BbuKc}v^i$-4_e}We-jA)zH039?W-ni{ictQK_k&^MFX%{0* z^g;D;-^Q&X&77(72$wKjjD{8b{pVLOoFCW+@`A19Eqe+alwzB#}R<5x!0}5;wDugp(*Eh&3e9I{b zePX`Z&-#ttSpq@x$OFPt!Z^|{{YLj`ulH6nQ~#esRHbk5_o>&8e0nWx^! zf7*GWl3nLG|I=zqQTH96jk;^>>eB literal 0 HcmV?d00001 diff --git a/plugins/channelmimo/interferometer/readme.md b/plugins/channelmimo/interferometer/readme.md index ec8a67c3b..18727bbfe 100644 --- a/plugins/channelmimo/interferometer/readme.md +++ b/plugins/channelmimo/interferometer/readme.md @@ -12,7 +12,7 @@ The top and bottom bars of the channel window are described [here](../../../sdrg The interface is divided in 3 sections that will be detailed next: - A: settings. These are the plugin controls - - B: spectrum (frequency domain). This is a spectrum display analogous to other spectrum displays. Its input varies depending on the correlation function selected + - B: spectrum (frequency domain). This is a spectrum display analogous to other spectrum displays. Its input varies depending on the correlation function selected. Details on the spectrum view and controls can be found [here](../../../sdrgui/gui/spectrum.md) - C: scope (time domain). This is a scope display analogous to other scope displays. Its input varies depending on the correlation function selected. For FFT type correlation this is not a time domain but a frequency domain display transposed to time analogous to a frequency sweep.

A. Settings section

diff --git a/plugins/channelrx/chanalyzer/readme.md b/plugins/channelrx/chanalyzer/readme.md index d46aafd7c..b92fa6296 100644 --- a/plugins/channelrx/chanalyzer/readme.md +++ b/plugins/channelrx/chanalyzer/readme.md @@ -38,7 +38,7 @@ The interface is essentially divided in the following sections Note 1: the scope trace is updated continuously for sweep times of 1 second or more else the display is refreshed only when the trace finishes. -Note 2: the spectrum view (Channel spectrum) is not presented here. +Note 2: details on the spectrum view and controls can be found [here](../../../sdrgui/gui/spectrum.md)

C. Channel controls

diff --git a/plugins/channelrx/demodbfm/readme.md b/plugins/channelrx/demodbfm/readme.md index 90b055e0b..df994dde1 100644 --- a/plugins/channelrx/demodbfm/readme.md +++ b/plugins/channelrx/demodbfm/readme.md @@ -70,6 +70,10 @@ This is the relative AF volume from 0 to 10. Adjust squelch in dB. +

B: Spectrum

+ +Details on the spectrum view and controls can be found [here](../../../sdrgui/gui/spectrum.md) +

C: RDS display

![BFM Demodulator plugin RDS 1](../../../doc/img/BFMDemod_plugin_rds.png) diff --git a/plugins/channelrx/demodchirpchat/readme.md b/plugins/channelrx/demodchirpchat/readme.md index 80916e765..b3adb1322 100644 --- a/plugins/channelrx/demodchirpchat/readme.md +++ b/plugins/channelrx/demodchirpchat/readme.md @@ -268,7 +268,7 @@ This is the UDP address and port to where the decoded message is sent when (12)

B: De-chirped spectrum

-This is the spectrum of the de-chirped signal when a ChirpChat signal can be decoded. +This is the spectrum of the de-chirped signal when a ChirpChat signal can be decoded. Details on the spectrum view and controls can be found [here](../../../sdrgui/gui/spectrum.md) The frequency span corresponds to the bandwidth of the ChirpChat signal (3). Default FFT size is 2SF where SF is the spread factor (7). diff --git a/plugins/channelrx/demodssb/readme.md b/plugins/channelrx/demodssb/readme.md index 8d8c86ab7..7be8ff834 100644 --- a/plugins/channelrx/demodssb/readme.md +++ b/plugins/channelrx/demodssb/readme.md @@ -154,4 +154,4 @@ If you right click on it a dialog will open to select the audio output device. S

14: Spectrum display

-This is the spectrum display of the demodulated signal (SSB) or translated signal (DSB). Controls on the bottom of the panel are identical to the ones of the main spectrum display. +This is the spectrum display of the demodulated signal (SSB) or translated signal (DSB). Controls on the bottom of the panel are identical to the ones of the main spectrum display. Details on the spectrum view and controls can be found [here](../../../sdrgui/gui/spectrum.md) diff --git a/plugins/channelrx/filesink/readme.md b/plugins/channelrx/filesink/readme.md index 9e8089aeb..0b62906eb 100644 --- a/plugins/channelrx/filesink/readme.md +++ b/plugins/channelrx/filesink/readme.md @@ -101,4 +101,4 @@ The file path currently being written (or last closed) appears at the right of t

15: Channel spectrum

-This is the spectrum display of the IQ stream seen by the channel. It is the same as all spectrum displays in the program and is identical to the [main window](../../../sdrgui/readme.md#) spectrum display. +This is the spectrum display of the IQ stream seen by the channel. Details on the spectrum view and controls can be found [here](../../../sdrgui/gui/spectrum.md) diff --git a/plugins/channelrx/freqtracker/readme.md b/plugins/channelrx/freqtracker/readme.md index fbca962d7..a6832d6aa 100644 --- a/plugins/channelrx/freqtracker/readme.md +++ b/plugins/channelrx/freqtracker/readme.md @@ -108,4 +108,4 @@ The channel signal is decimated by a power of two before being applied to the ch

12: Channel spectrum

-This is the spectrum display of the tracker channel. When the tracker is locked to the signal the center of the channel should fall almost in the middle of the signal spectrum (ideally in the middle when the tracker error is zero). Thus the locking can be followed dynamically and it can be more reliable than the lock indicator. A channel marker shows the tracker offset from the channel center frequency (tracker error). Its width is the tracker error tolerance and is ±1/1000th of the channel width. Controls on the bottom of the panel are identical to the ones of the main spectrum display. +This is the spectrum display of the tracker channel. When the tracker is locked to the signal the center of the channel should fall almost in the middle of the signal spectrum (ideally in the middle when the tracker error is zero). Thus the locking can be followed dynamically and it can be more reliable than the lock indicator. A channel marker shows the tracker offset from the channel center frequency (tracker error). Its width is the tracker error tolerance and is ±1/1000th of the channel width. Details on the spectrum view and controls can be found [here](../../../sdrgui/gui/spectrum.md) diff --git a/plugins/channelrx/sigmffilesink/readme.md b/plugins/channelrx/sigmffilesink/readme.md index 07aa90a39..2759e780b 100644 --- a/plugins/channelrx/sigmffilesink/readme.md +++ b/plugins/channelrx/sigmffilesink/readme.md @@ -102,4 +102,4 @@ The path of the selected meta file appears at the right of the button. If it is

15: Channel spectrum

-This is the spectrum display of the IQ stream seen by the channel. It is the same as all spectrum displays in the program and is identical to the [main window](../../../sdrgui/readme.md#) spectrum display. +This is the spectrum display of the IQ stream seen by the channel. Details on the spectrum view and controls can be found [here](../../../sdrgui/gui/spectrum.md) diff --git a/plugins/channelrx/udpsink/readme.md b/plugins/channelrx/udpsink/readme.md index 921e6ae9c..0e4edd359 100644 --- a/plugins/channelrx/udpsink/readme.md +++ b/plugins/channelrx/udpsink/readme.md @@ -126,7 +126,7 @@ The delay in milliseconds is displayed at the right of the button.

14: Spectrum display

-This is the spectrum display of the channel signal after bandpass filtering. Please refer to the Spectrum display description for details. +This is the spectrum display of the channel signal after bandpass filtering. Details on the spectrum view and controls can be found [here](../../../sdrgui/gui/spectrum.md) This spectrum is centered on the center frequency of the channel (center frequency of reception + channel shift) and is that of a complex signal i.e. there are positive and negative frequencies. The width of the spectrum is proportional of the sample rate. That is for a sample rate of S samples per seconds the spectrum spans from -S/2 to +S/2 Hz. diff --git a/plugins/channeltx/modfreedv/readme.md b/plugins/channeltx/modfreedv/readme.md index 52dae2126..b79c11b56 100644 --- a/plugins/channeltx/modfreedv/readme.md +++ b/plugins/channeltx/modfreedv/readme.md @@ -179,4 +179,4 @@ The transmitted signal is further decimated by a power of two before being appli

17: Channel spectrum display

-This is the channel spectrum display. Controls at the bottom of the panel are the same as with the central spectrum display. +This is the channel spectrum display. Details on the spectrum view and controls can be found [here](../../../sdrgui/gui/spectrum.md) diff --git a/plugins/channeltx/modssb/readme.md b/plugins/channeltx/modssb/readme.md index 829618a75..1489c7642 100644 --- a/plugins/channeltx/modssb/readme.md +++ b/plugins/channeltx/modssb/readme.md @@ -249,4 +249,4 @@ This slider can be used to randomly set the current position in the file when fi

22: Channel spectrum display

-This is the channel spectrum display. Controls at the bottom of the panel are the same as with the central spectrum display. +This is the channel spectrum display. Details on the spectrum view and controls can be found [here](../../../sdrgui/gui/spectrum.md) diff --git a/plugins/channeltx/udpsource/readme.md b/plugins/channeltx/udpsource/readme.md index 3975fd111..9bb601d2c 100644 --- a/plugins/channeltx/udpsource/readme.md +++ b/plugins/channeltx/udpsource/readme.md @@ -123,6 +123,6 @@ When any item of these items is changed the button is lit in green until it is p

21: Spectrum display

-This is the spectrum display of the channel signal before filtering. Please refer to the Spectrum display description for details. +This is the spectrum display of the channel signal before filtering. Details on the spectrum view and controls can be found [here](../../../sdrgui/gui/spectrum.md) This spectrum is centered on the center frequency of the channel (center frequency of reception + channel shift) and is that of a complex signal i.e. there are positive and negative frequencies. The width of the spectrum is proportional of the sample rate. That is for a sample rate of S samples per seconds the spectrum spans from -S/2 to +S/2 Hz. diff --git a/plugins/feature/demodanalyzer/readme.md b/plugins/feature/demodanalyzer/readme.md index 76ede3c98..5aac2bd84 100644 --- a/plugins/feature/demodanalyzer/readme.md +++ b/plugins/feature/demodanalyzer/readme.md @@ -75,7 +75,7 @@ Average total power in dB relative to a +/- 1.0 amplitude signal received in the

B. Spectrum view

-This is the same display as with the channel analyzer spectrum view. This is the spectrum of a real signal so it is symmetrical around zero frequency. See Channel Analyzer plugin documentation for details. +This is the same display as with the channel analyzer spectrum view. This is the spectrum of a real signal so it is symmetrical around zero frequency. Details on the spectrum view and controls can be found [here](../../../sdrgui/gui/spectrum.md)

C. Scope view

diff --git a/plugins/samplemimo/testmosync/readme.md b/plugins/samplemimo/testmosync/readme.md index e805cc17b..2343a8199 100644 --- a/plugins/samplemimo/testmosync/readme.md +++ b/plugins/samplemimo/testmosync/readme.md @@ -46,4 +46,4 @@ Use the wheels to adjust the sample rate. Left click on a digit sets the cursor

7: Spectrum display

-This is the final output stream spectrum display after interpolation (5). This would be sent to the hardware device. Controls on the bottom of the panel are identical to the ones of the main spectrum display. +This is the final output stream spectrum display after interpolation (5). This would be sent to the hardware device. Details on the spectrum view and controls can be found [here](../../../sdrgui/gui/spectrum.md) diff --git a/plugins/samplesink/testsink/readme.md b/plugins/samplesink/testsink/readme.md index 919202ace..63158a909 100644 --- a/plugins/samplesink/testsink/readme.md +++ b/plugins/samplesink/testsink/readme.md @@ -42,4 +42,4 @@ Use the wheels to adjust the sample rate. Left click on a digit sets the cursor

6: Spectrum display

-This is the final output stream spectrum display after interpolation (4). This would be sent to the hardware device. Controls on the bottom of the panel are identical to the ones of the main spectrum display. +This is the final output stream spectrum display after interpolation (4). This would be sent to the hardware device. Details on the spectrum view and controls can be found [here](../../../sdrgui/gui/spectrum.md) diff --git a/sdrgui/gui/spectrum.md b/sdrgui/gui/spectrum.md new file mode 100644 index 000000000..b49bda2e7 --- /dev/null +++ b/sdrgui/gui/spectrum.md @@ -0,0 +1,315 @@ +

Spectrum component

+ +This page details the spectrum component that takes part of the main spectrum display and is also used in some channel and feature plugins. + +For example: + - Channel Analyzer + - Demod Analyzer + - Interferometer + - Broadcast FM demodulator + - ChirpChat (LoRa) demodulator + - File sink + - FreeDV demodulator and modulator + - Frequency tracker + - SigMF file sink + - SSB demodulator and modulator + - UDP source and sink + +It comprises the spectrum display itself and the controls generally placed at the bottom of the spectrum display + +

A. Spectrum display

+ +

Status line

+ +![Spectrum Statuss](../../doc/img/Spectrum_Status.png) + +A status line is displayed at the left of the top margin. It displays the following items from left to right: + + - if frequency zooming is active the zooming factor + - `CF:` followed by the Center Frequency of the displayed spectrum possibly with multiplier suffix (G, M, k) + - `SP:` followed by the frequency SPan of the displayed spectrum possibly with multiplier suffix (M, k) + +

Spectrum markers

+ +![Spectrum Markers](../../doc/img/Spectrum_Markers.png) + +Note that spectrum markers appear only when spectrum display is active (shows data) + +The spectrum markers can be set either with the mouse as explained next or with the [spectrum markers dialog](spectrummarkers.md) that can be opened with the markers button (B.4.3). + +Use Shift and mouse left click to set a new marker. There is a maximum of two markers with a different status: + - The first marker will display frequency (2) and power (1) or time (5) on the scale side of the view. Frequency units are the same as displayed in the frequency scale. + - The second marker will display frequency difference (3 or 6) and power difference (4) or time difference (7) from the first marker on the opposite side of the scales. Difference values may be suffixed with a multiplier character. + +Base units are Hz for frequency difference and seconds for time. Power is expressed either in dB or plain value depending on the linear or log setting for the spectrum display. + +Values may be suffixed by a multiplier character: + - **p**: pico (times 1e-12) + - **n**: nano (times 1e-9) + - **u**: micro (times 1e-6) + - **m**: milli (times 1e-3) + - no character: no change (times one) + - **k**: kilo (times 1e3) + - **M**: mega (times 1e6) + - **G**: giga (times 1e9) + +Use mouse right click anywhere in the view to remove the last entered marker. Use shift and mouse right click to remove all markers. + +

Mouse scroll wheel

+ +![Spectrum Mousewheel](../../doc/img/MainWindow_spectrum_mousewheel.png) + +

A: Channel moving

+ +When the mouse is over the center line of a channel: + + - scrolling will move the channel by +/- 10 Hz at each scroll up/down respectively + - combined with Ctrl it will move the channel by +/- 100 Hz + - combined with Shift it will move the channel by +/- 1 kHz + +

B: Frequency zooming

+ +When the mouse is in the spectrum or waterfall area but not over the center line of a channel it will zoom in/out along X (frequency) axis by a 0.5 step at each scroll up/down respectively between 1x (no zoom) and 10x. Note that in order to zoom on the center line of a channel you may move the mouse pointer in the top margin (center line moving is not active there but zooming is). + +When frequency zooming is active use Alt + left click to move the center frequency to the clicked point. + +

C: Power zooming

+ +When the mouse is inside the power scale (spectrum) the power range is decreased by 2 (zoom in) or increased by 2 (zoom in) at each wheel step forward or backward respectively. The behavior of the reference level depends on where in the scale is the mouse pointer: + + - in the top third: the reference level is maintained thus the reference level at the top stays the same + - in the middle third: the reference level is decreased by 1 (zoom in) or increased by 1 (zoom out) at each wheel step forward or backward thus the level in the middle stays the same + - in the bottom third: the reference level is decreased by 2 (zoom in) or increased by 2 (zoom out) at each wheel step forward or backward thus the level at the bottom stays the same + +

D: Time zooming

+ +When the mouse is inside the time scale (waterfall) the overlap is increased by 1 (zoom in) or decreased by 1 (zoom out) at each wheel step forward or backward respectively. Overlap is bounded by 0 and half of the FFT size minus one. + +

B. Spectrum controls

+ +Controls are organized in 4 blocks arranged in a flow layout so that the size of the control area can adapt to the width of the spectrum arranging the blocks from 3 to 1 line as the spectrum widens. The buttons and various controls in each block remain at the same place. + +Narrow (3 lines): + +![Spectrum GUI](../../doc/img/MainWindow_spectrum_gui_narrow.png) + +Wide (1 line): + +![Spectrum GUI](../../doc/img/MainWindow_spectrum_gui_wide.png) + +The 4 blocks are detailed next: + +![Spectrum GUI](../../doc/img/MainWindow_spectrum_gui.png) + +

B.1: Spectrum display control line 1

+ +![Spectrum GUI C](../../doc/img/MainWindow_spectrum_gui_C.png) + +

B.1.1: Clear spectrum

+ +This resets the maximum spectrum trace and phosphor remanence + +

B.1.2: Phosphor display

+ +Toggles the phosphor display on the spectrum + +

B.1.3: Phosphor display stroke and max hold decay

+ +This controls the decay rate of the stroke when phosphor display is engaged (B.1.2). The histogram pixel value is diminished by this value each time a new FFT is produced. A value of zero means no decay and thus phosphor history and max hold (red line) will be kept until the clear button (B.1.1) is pressed. + +

B.1.4: Phosphor display stroke and max hold decay divisor

+ +When phosphor display is engaged (B.1.2) and stroke decay is 1 (B.1.3) this divides the unit decay by this value by diminishing histogram pixel value by one each time a number of FFTs equal to this number have been produced. Thus the actual decay rate is 1 over this value. This allow setting a slower decay rate than one unit for each new FFT. + +

B.1.5: Phosphor display stroke strength

+ +This controls the stroke strength when phosphor display is engaged (B.1.2). The histogram value is incremented by this value at each new FFT until the maximum (red) is reached. + +

B.1.6: Maximum hold trace

+ +Toggles the maximum hold trace display (red trace) on the spectrum + +

B.1.7: Current trace

+ +Toggles the current trace display (yellow trace) on the spectrum + +

B.1.8: Trace intensity

+ +This controls the intensity of the maximum (B.1.6) and current (B.1.7) spectrum trace + +

B.1.9: Waterfall/spectrum placement

+ +Toggles the spectrum on top or on bottom versus waterfall + +

B.1.10: Waterfall

+ +Toggles the waterfall display + +

B.1.11: Grid

+ +Toggles the grid display + +

B.1.12: Grid intensity

+ +Controls the intensity of the grid display + +

B.2: Spectrum display control block #2

+ +![Spectrum GUI A](../../doc/img/MainWindow_spectrum_gui_A.png) + + +

B.2.1. FFT window selector

+ +Use this combo box to select which window is applied to the FFT: + - **Bart**: Bartlett + - **B-H**: Blackmann-Harris + - **FT**: Flat top + - **Ham**: Hamming + - **Han**: Hanning (default) + - **Rec**: Rectangular (no window) + - **Kai**: Kaiser with alpha = 2.15 (beta = 6.76) gives sidelobes < -70dB + +

B.2.2. FFT size

+ +Select the size of the FFT window among these values: + - 128 + - 256 + - 512 + - 1k = 1024 (default) + - 2k = 2048 + - 4k = 4096 + +

B.2.3: FFT Overlap

+ +FFT Overlap in number of samples over half of the FFT size. The percentage of overlap appears in the tooltip. Ranges from 0 (no overlap) to half the FFT size minus one (maximum overlap). + +Example with a FFT of 1k (1024) and an overlap of 128 the overlap percentage is 128 ÷ 512 = 25% + +

B.2.4: Averaging mode

+ +Use this combo to select which averaging mode is applied: + - **No**: no averaging. Disables averaging regardless of the number of averaged samples (B.2.5). This is the default option + - **Mov**: moving average. This is a sliding average over the amount of samples specified next (B.2.5). There is one complete FFT line produced at every FFT sampling period + - **Fix**: fixed average. Average is done over the amount of samples specified next (B.2.5) and a result is produced at the end of the corresponding period then the next block of averaged samples is processed. There is one complete FFT line produced every FFT sampling period multiplied by the number of averaged samples (4.6). The time scale on the waterfall display is updated accordingly. + - **Max**: this is not an averaging but a max hold. It will retain the maximum value over the amount of samples specified next (B.2.5). Similarly to the fixed average a result is produced at the end of the corresponding period which results in slowing down the waterfall display. The point of this mode is to make outlying short bursts within the "averaging" period stand out. With averaging they would only cause a modest increase and could be missed out. + +

B.2.5: Number of averaged samples

+ +Each FFT bin (squared magnitude) is averaged or max'ed over a number of samples. This combo allows selecting the number of samples between these values: 1 (no averaging), 2, 5, 10, 20, 50, 100, 200, 500, 1k (1000) for all modes and in addition 2k, 5k, 10k, 20k, 50k, 1e5 (100000), 2e5, 5e5, 1M (1000000) for "fixed" and "max" modes. Averaging reduces the noise variance and can be used to better detect weak continuous signals. The fixed averaging mode allows long time monitoring on the waterfall. The max mode helps showing short bursts that may appear during the "averaging" period. + +The resulting spectrum refresh period appears in the tooltip taking sample rate, FFT size (B.2.2), average size (B.2.5) and overlap (B.2.3) into consideration. Averaging size adjustment is valid for fixed average and max modes only: + +Period = ((((FFT_size ÷ 2) - overlap) × 2) ÷ sample_rate) × averaging_size + +

B.3: Spectrum display control block #3

+ +![Spectrum GUI B](../../doc/img/MainWindow_spectrum_gui_B.png) + +

B.3.1: Autoscale

+ +Scales spectrum by setting reference level and range automatically based on maximum and minimum levels. Takes the average of FFT size ÷ 32 minima for the minimum and 10 dB over maximum for the maximum. + +

B.3.2: Reference level

+ +This is the level in dB at the top of the display range. You can select values between 0 and -110 in 1 dB steps + +

B.3.3: Range

+ +This is the range of display in dB. You can select values between 1 and 100 in 1 dB steps + +

B.3.4: FPS capping

+ +The refresh rate of the spectrum is capped by this value in FPS i.e the refresh period in seconds is 1 ÷ FPS. The default value is 20 and corresponds to general usage. You may use a lower value to limit GPU usage and power consumption. You may also use a higher value for an even more reactive display. "NL" corresponds to "No Limit". With "No Limit" the spectrum update will be triggered immediately when a new FFT is calculated. Note that actual refresh rate will be limited by other factors related to hardware and graphics drivers. + +The refresh period is limited anyway by the FFT period which is the FFT size divided by the baseband sampling rate and multiplied by the fixed average or max size (3A.5) in case these features are engaged (3A.4). Setting a resulting FFT refresh time above the refresh rate will make sure that a short burst is not missed particularly when using the max mode. + +Example with a FFT size of 1k (1024) and no overlap, a baseband rate of 48 kS/s and an averaging size of 5 the refresh period is: + +(1024 ÷ 48000) × 5 ≈ 107 ms + +Thus if the FPS capping is 20 (50 ms) the refresh period will be in fact 107 ms (≈ 9 FPS) anyway. + +

B.3.5: Logarithmic/linear scale

+ +Use this toggle button to switch between spectrum logarithmic and linear scale display. The face of the button will change to represent either a logaritmic or linear curve. + +When in linear mode the range control (B.3.3) has no effect because the actual range is between 0 and the reference level. The reference level in dB (B.3.2) still applies but is translated to a linear value e.g -40 dB is 1e-4. In linear mode the scale numbers are formatted using scientific notation so that they always occupy the same space. + +

B.4: Spectrum display control block #4

+ +![Spectrum GUI D](../../doc/img/MainWindow_spectrum_gui_D.png) + +

B.4.1: Play/Pause spectrum

+ +Use this button to freeze the spectrum update. Useful when making measurements with the markers. + +

B.4.2: Spectrum server control

+ +A websockets based server can be used to send spectrum data to clients. An example of such client can be found in the [SDRangelSpectrum](https://github.com/f4exb/sdrangelspectrum) project. + + - Left button: toggles server on/off + - Right button: opens a secondary dialog that lets you choose the server listening (local) address and port. + +The server only sends data. Control including FFT details is done via the REST API. FFT frames are formatted as follows (in bytes): + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
OffsetLengthValue
08Center frequency in Hz as 64 bit integer
88Effective FFT time in milliseconds as 64 bit integer
168Unix timestamp in milliseconds as 64 bit integer
244FFT size as 32 bit integer
284FFT bandwidth in Hz as 32 bit integer
324 + Indicators as 32 bit integer LSB to MSB: +
    +
  • bit 0: Linear (1) / log (0) spectrum indicator
  • +
  • bit 1: SSB (1) / DSB (0) spectrum indicator
  • +
  • bit 2: USB (1) / LSB (0) spectrum indicator
  • +
+
36N*4Vector of N = FFT size 32 bit floating point spectrum power values either log (dB) or linear
+ +

B.4.3: Spectrum markers dialog

+ +Opens the [spectrum markers dialog](spectrummarkers.md) + +

B.4.4: Spectrum calibration

+ +Use the toggle button to switch between relative and calibrated power readings. + +Right click to open the [calibration management dialog](spectrumcalibration.md) + diff --git a/sdrgui/spectrumcalibration.md b/sdrgui/gui/spectrumcalibration.md similarity index 100% rename from sdrgui/spectrumcalibration.md rename to sdrgui/gui/spectrumcalibration.md diff --git a/sdrgui/spectrummarkers.md b/sdrgui/gui/spectrummarkers.md similarity index 100% rename from sdrgui/spectrummarkers.md rename to sdrgui/gui/spectrummarkers.md diff --git a/sdrgui/mainspectrum/readme.md b/sdrgui/mainspectrum/readme.md index 85956b646..1ed0118dd 100644 --- a/sdrgui/mainspectrum/readme.md +++ b/sdrgui/mainspectrum/readme.md @@ -1,6 +1,6 @@

Main spectrum window

-This page details the top and bottom bars of the window and not the spectrum nor the spectrum controls. Documentation on the spectrum display and controls can be found here (TBD) +This page details the top and bottom bars of the window and not the spectrum nor the spectrum controls. Documentation on the spectrum display and controls can be found [here](../gui/spectrum.md) ![Channel window](../../doc/img/MainSpectrum.png) From f9985ad061bf5d8b568ea3206bc7e2e42ad31b9b Mon Sep 17 00:00:00 2001 From: f4exb Date: Tue, 19 Apr 2022 07:03:28 +0200 Subject: [PATCH 041/115] Massive UI revamping (v7): main window documentation --- CMakeLists.txt | 8 +- doc/img/Configurations.png | Bin 0 -> 26402 bytes doc/img/Configurations.xcf | Bin 0 -> 94059 bytes doc/img/MainWindow.png | Bin 0 -> 17455 bytes doc/img/Workspace_create_feature.png | Bin 0 -> 7776 bytes doc/img/Workspace_create_rx.png | Bin 0 -> 11689 bytes doc/img/Workspace_top.png | Bin 0 -> 9124 bytes doc/img/Workspace_top.xcf | Bin 0 -> 38063 bytes doc/img/Workspaces.png | Bin 0 -> 54229 bytes doc/img/Workspaces.xcf | Bin 0 -> 137042 bytes sdrbase/maincore.h | 1 + sdrgui/configurations.md | 45 + sdrgui/gui/configurationsdialog.cpp | 96 ++ sdrgui/gui/configurationsdialog.h | 2 + sdrgui/gui/configurationsdialog.ui | 28 + sdrgui/mainspectrum/mainspectrumgui.cpp | 3 +- sdrgui/mainwindow.cpp | 4 +- sdrgui/readme.md | 1097 ++++------------------- 18 files changed, 375 insertions(+), 909 deletions(-) create mode 100644 doc/img/Configurations.png create mode 100644 doc/img/Configurations.xcf create mode 100644 doc/img/MainWindow.png create mode 100644 doc/img/Workspace_create_feature.png create mode 100644 doc/img/Workspace_create_rx.png create mode 100644 doc/img/Workspace_top.png create mode 100644 doc/img/Workspace_top.xcf create mode 100644 doc/img/Workspaces.png create mode 100644 doc/img/Workspaces.xcf create mode 100644 sdrgui/configurations.md diff --git a/CMakeLists.txt b/CMakeLists.txt index fe7c567b5..d1b8b396d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,10 +14,10 @@ set(CMAKE_CXX_EXTENSIONS OFF) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) # configure version -set(sdrangel_VERSION_MAJOR "6") -set(sdrangel_VERSION_MINOR "20") -set(sdrangel_VERSION_PATCH "2") -set(sdrangel_VERSION_SUFFIX "") +set(sdrangel_VERSION_MAJOR "7") +set(sdrangel_VERSION_MINOR "0") +set(sdrangel_VERSION_PATCH "0") +set(sdrangel_VERSION_SUFFIX "alpha.1") # SDRAngel cmake options option(DEBUG_OUTPUT "Print debug messages" OFF) diff --git a/doc/img/Configurations.png b/doc/img/Configurations.png new file mode 100644 index 0000000000000000000000000000000000000000..e56c544305a537216f18a84bfd3fabd717d385c0 GIT binary patch literal 26402 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
  • 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 KdE~BpzCQu}!~ul> diff --git a/sdrgui/channel/readme.md b/sdrgui/channel/readme.md index d99ad4ed2..d70816819 100644 --- a/sdrgui/channel/readme.md +++ b/sdrgui/channel/readme.md @@ -139,10 +139,6 @@ Click this button to hide the channel window Click this button to close the window and removes the channel from the device set. -

    A.9: Top size grip

    - -You can drag this gray square to resize the window -

    B: Bottom bar

    ![Channel window](../../doc/img/ChannelWindow_bottom.png) @@ -167,7 +163,3 @@ The status messages appear here if any. You may click on this area and drag the window with the mouse. -

    B.5: Bottom size grip

    - -You can drag this gray square to resize the window - diff --git a/sdrgui/device/readme.md b/sdrgui/device/readme.md index ee0f66265..4d5e8575a 100644 --- a/sdrgui/device/readme.md +++ b/sdrgui/device/readme.md @@ -102,56 +102,38 @@ Use the `Cancel` button to exit the dialog without any change This button activates a close/open sequence to recycle the device. It may be useful when the device is not streaming anymore or in an attempt to clear possible errors. Make sure the streaming is stopped first. -

    A.5: Add channels

    - -Opens a dialog to add one or more channels connected to the device. - -![Add channels dialog](../../doc/img/AddChannels_dialog.png) - -

    A.5.1: Channel selection

    - -Use this combo to select which channel type to add - -

    A.5.2: Close dialog

    - -Use this button to dismiss the dialog - -

    A.5.3: Add channel

    - -Add a new channel by clicking on the `Apply` button. You may click it several times to add more channels. The dialog can be dismissed with the `Close` button or the closing window icon `X` on the top bar. - -

    A.6: Device set presets

    +

    A.5: Device set presets

    Opens a dialog to manage device set presets. Device set presets store configurations related to a single device set ![Presets dialog](../../doc/img/MainWindow_presets_view.png) -

    A.6.1: Preset selection

    +

    A.5.1: Preset selection

    You select a preset or a preset group by clicking on its line in the tree view. All actions (6) will be done relative to this preset or preset group. -

    A.6.2: Group

    +

    A.5.2: Group

    You can organize your presets into groups. Groups can be collapsed or expanded by using the caret icon on the left. -

    A.6.3: Center frequency

    +

    A.5.3: Center frequency

    The center frequency used in this preset is displayed here. -

    A.6.4: Rx/Tx indicator

    +

    A.5.4: Rx/Tx indicator

    "R" is displayed for a Rx device set and "T" for a Tx device set -

    A.6.5: Preset name

    +

    A.5.5: Preset name

    You can give a name to your preset. Names need not to be unique. -

    A.6.6: Preset control or actions

    +

    A.5.6: Preset control or actions

    ![Presets controls](../../doc/img/MainWindow_presets.png) -
    A.6.6.1: New preset
    +
    A.5.6.1: New preset
    Click on this icon to create a new preset with the current values in the selected sample device tab (Main window: 2). -
    A.6.6.2: Update preset
    +
    A.5.6.2: Update preset
    Click on this icon to create a update the selected preset with the current values in the selected sample device tab (Main window: 2). Please note that this does not save the preset immediately on disk to save presets immediately you need to use the save button (4). -
    A.6.6.3: Edit preset
    +
    A.5.6.3: Edit preset
    Opens a new window where you can change the group name and description. - for group items you can rename the group or merge all group presets into an existing group by selecting this existing group @@ -161,19 +143,37 @@ Opens a new window where you can change the group name and description. - assign this preset to a new group by typing in this new group - change the description -
    A.6.6.4: Export preset
    +
    A.5.6.4: Export preset
    Using the previous icon presets are saved globally in a system dependent place. Using this icon you can export a specific preset in a single file that can be imported on another machine possibly with a different O/S. The preset binary data (BLOB) is saved in Base-64 format. -
    A.6.6.5: Import preset
    +
    A.5.6.5: Import preset
    This is the opposite of the previous operation. This will create a new preset in the selected group or the same group as the preset being selected. -
    A.6.6.6: Delete preset
    +
    A.5.6.6: Delete preset
    on a preset item: deletes the selected preset. on a preset group: deletes the group and all its presets. -
    A.6.6.7: Load preset
    +
    A.5.6.7: Load preset
    Applies the selected preset to the current device set (source and channel plugins). +

    A.6: Add channels

    + +Opens a dialog to add one or more channels connected to the device. + +![Add channels dialog](../../doc/img/AddChannels_dialog.png) + +

    A.6.1: Channel selection

    + +Use this combo to select which channel type to add + +

    A.6.2: Close dialog

    + +Use this button to dismiss the dialog + +

    A.6.3: Add channel

    + +Add a new channel by clicking on the `Apply` button. You may click it several times to add more channels. The dialog can be dismissed with the `Close` button or the closing window icon `X` on the top bar. +

    A.7: Title

    The window title shows the device type and a sequence number. @@ -196,10 +196,6 @@ Click this button to reduce the window to its minimum size Click this button to close the window and removes the device set i.e. the device and all its channels. -

    A.12: Top size grip

    - -You can drag this gray square to resize the window -

    B: Bottom bar

    ![Device window bottom bar](../../doc/img/DeviceWindow_bottom.png) @@ -217,8 +213,3 @@ This will show all hidden channel windows if any. It has no effects on channel w The status messages appear here if any. You may click on this area and drag the window with the mouse. - -

    A.4: Bottom size grip

    - -You can drag this gray square to resize the window - diff --git a/sdrgui/feature/readme.md b/sdrgui/feature/readme.md index 49d68444f..d8a9693c3 100644 --- a/sdrgui/feature/readme.md +++ b/sdrgui/feature/readme.md @@ -98,18 +98,9 @@ Click this button to reduce the window to its minimum size Closes the window and deletes the feature -

    8: Top size grip

    - -You can drag this gray square to resize the window - -

    Bottom bar

    - -

    9: Status

    +

    8: Status

    Status messages if any appear here. You may click on this area and drag the window with the mouse. -

    10: Bottom size grip

    - -You can drag this gray square to resize the window diff --git a/sdrgui/mainspectrum/readme.md b/sdrgui/mainspectrum/readme.md index 1ed0118dd..1f6bd3a8b 100644 --- a/sdrgui/mainspectrum/readme.md +++ b/sdrgui/mainspectrum/readme.md @@ -44,16 +44,6 @@ Click this button to reduce the window to its minimum size. Click this button to hide the main spectrum window -

    7: Top size grip

    - -You can drag this gray square to resize the window - -

    Bottom bar

    - -

    8. Window drag area

    +

    7. Window drag area

    You may click on this area and drag the window with the mouse. - -

    9: Bottom size grip

    - -You can drag this gray square to resize the window diff --git a/sdrgui/readme.md b/sdrgui/readme.md index e289a7d25..d304790bd 100644 --- a/sdrgui/readme.md +++ b/sdrgui/readme.md @@ -121,11 +121,24 @@ Arranges windows in the MDI area of the workspace as cascaded windows. Arranges windows in the MDI area of the workspace as tiled windows. -

    1.9: Dock/undock workspace

    +

    1.9: Stack windows

    + +Arranges windowsa in the MDO area of the workspace in a way similar to the arrangement in version 6: + + - Devices are stacked in numerical order top left. + - Fixed height features are stacked in order underneath devices and resized to match width of devices + - Channels are stacked in device then channel index order down the right and resized horizontally to match and then any available vertical space is shared being vertically expanding channels + - Remaining space in the middle is shared between spectrums and expanding features + +

    1.10: Auto stack windows

    + +Applies the stacked arrangement above automatically when elements are added. + +

    1.11: Dock/undock workspace

    use this button to dock or undock the workspace from the main window docking area. -

    1.10: Hide workspace

    +

    1.12: Hide workspace

    Use this button to hide the workspace. Use the `Workspaces` / `View all` common on the main window the show all (hidden) workspaces. From 4990144a6130e0d831956ac272c80ceb5002e2b2 Mon Sep 17 00:00:00 2001 From: f4exb Date: Thu, 28 Apr 2022 00:01:08 +0200 Subject: [PATCH 078/115] Massive UI revamping (v7): fixed add channel and spectrum display after device renumbering. Fixes #1217 --- plugins/feature/aprs/aprsgui.cpp | 2 +- sdrgui/mainwindow.cpp | 22 ++++++++++++++++------ sdrgui/mainwindow.h | 2 +- 3 files changed, 18 insertions(+), 8 deletions(-) diff --git a/plugins/feature/aprs/aprsgui.cpp b/plugins/feature/aprs/aprsgui.cpp index f251c9a44..906cf23b4 100644 --- a/plugins/feature/aprs/aprsgui.cpp +++ b/plugins/feature/aprs/aprsgui.cpp @@ -662,7 +662,7 @@ void APRSGUI::updateChannelList() ui->sourcePipes->blockSignals(false); } -void APRSGUI::resizeEvent(QResizeEvent* size) +void APRSGUI::resizeEvent(QResizeEvent*) { // Replot graphs to ensure Axis are visible plotWeather(); diff --git a/sdrgui/mainwindow.cpp b/sdrgui/mainwindow.cpp index d1a7faac9..9aa973555 100644 --- a/sdrgui/mainwindow.cpp +++ b/sdrgui/mainwindow.cpp @@ -433,7 +433,7 @@ void MainWindow::sampleSourceCreate( deviceGUI, &DeviceGUI::showSpectrum, this, - [=](int deviceSetIndex){ this->mainSpectrumShow(this->m_deviceUIs[deviceSetIndex]->m_mainSpectrumGUI); } + &MainWindow::mainSpectrumShow ); QObject::connect( deviceGUI, @@ -648,7 +648,7 @@ void MainWindow::sampleSinkCreate( deviceGUI, &DeviceGUI::showSpectrum, this, - [=](int deviceSetIndex){ this->mainSpectrumShow(this->m_deviceUIs[deviceSetIndex]->m_mainSpectrumGUI); } + &MainWindow::mainSpectrumShow ); QObject::connect( deviceGUI, @@ -837,7 +837,7 @@ void MainWindow::sampleMIMOCreate( deviceGUI, &DeviceGUI::showSpectrum, this, - [=](int deviceSetIndex){ this->mainSpectrumShow(this->m_deviceUIs[deviceSetIndex]->m_mainSpectrumGUI); } + &MainWindow::mainSpectrumShow ); QObject::connect( deviceGUI, @@ -973,6 +973,15 @@ void MainWindow::removeDeviceSet(int deviceSetIndex) { DeviceUISet *deviceUISet = m_deviceUIs[i]; deviceUISet->setIndex(i); + DeviceGUI *deviceGUI = m_deviceUIs[i]->m_deviceGUI; + Workspace *deviceWorkspace = m_workspaces[deviceGUI->getWorkspaceIndex()]; + + QObject::connect( + deviceGUI, + &DeviceGUI::addChannelEmitted, + this, + [=](int channelPluginIndex){ this->channelAddClicked(deviceWorkspace, i, channelPluginIndex); } + ); } emit m_mainCore->deviceSetRemoved(deviceSetIndex); @@ -2572,10 +2581,11 @@ void MainWindow::mainSpectrumMove(MainSpectrumGUI *gui, int wsIndexDestnation) m_workspaces[wsIndexDestnation]->addToMdiArea(gui); } -void MainWindow::mainSpectrumShow(MainSpectrumGUI *gui) +void MainWindow::mainSpectrumShow(int deviceSetIndex) { - gui->show(); - gui->raise(); + DeviceUISet *deviceUISet = m_deviceUIs[deviceSetIndex]; + deviceUISet->m_mainSpectrumGUI->show(); + deviceUISet->m_mainSpectrumGUI->raise(); } void MainWindow::showAllChannels(int deviceSetIndex) diff --git a/sdrgui/mainwindow.h b/sdrgui/mainwindow.h index e49fa6d9c..21488f49a 100644 --- a/sdrgui/mainwindow.h +++ b/sdrgui/mainwindow.h @@ -207,7 +207,7 @@ private slots: void openFeaturePresetsDialog(QPoint p, Workspace *workspace); void deviceMove(DeviceGUI *gui, int wsIndexDestnation); void mainSpectrumMove(MainSpectrumGUI *gui, int wsIndexDestnation); - void mainSpectrumShow(MainSpectrumGUI *gui); + void mainSpectrumShow(int deviceSetIndex); void showAllChannels(int deviceSetIndex); void openDeviceSetPresetsDialog(QPoint p, DeviceGUI *deviceGUI); void commandKeyPressed(Qt::Key key, Qt::KeyboardModifiers keyModifiers, bool release); From f8d3af287e8050f8ac0868c6160c82dbc41dfbd7 Mon Sep 17 00:00:00 2001 From: f4exb Date: Thu, 28 Apr 2022 00:19:54 +0200 Subject: [PATCH 079/115] Massive UI revamping (v7): updated version to alpha.2 --- CMakeLists.txt | 2 +- sdrgui/mainwindow.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d1b8b396d..c5a320578 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,7 +17,7 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON) set(sdrangel_VERSION_MAJOR "7") set(sdrangel_VERSION_MINOR "0") set(sdrangel_VERSION_PATCH "0") -set(sdrangel_VERSION_SUFFIX "alpha.1") +set(sdrangel_VERSION_SUFFIX "alpha.2") # SDRAngel cmake options option(DEBUG_OUTPUT "Print debug messages" OFF) diff --git a/sdrgui/mainwindow.cpp b/sdrgui/mainwindow.cpp index 9aa973555..f65eca5c5 100644 --- a/sdrgui/mainwindow.cpp +++ b/sdrgui/mainwindow.cpp @@ -113,7 +113,7 @@ MainWindow::MainWindow(qtwebapp::LoggerWithFile *logger, const MainParser& parse m_fftWisdomProcess(nullptr) { qDebug() << "MainWindow::MainWindow: start"; - setWindowTitle("SDRangel v7"); + setWindowTitle("SDRangel"); m_instance = this; m_mainCore->m_logger = logger; From eb0730e93a0a32827316ceb271110e5cd5bdabfa Mon Sep 17 00:00:00 2001 From: f4exb Date: Sat, 30 Apr 2022 11:46:20 +0200 Subject: [PATCH 080/115] Massive UI revamping (v7): Test Sink: fixed minimum window height --- plugins/samplesink/testsink/testsinkgui.ui | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/samplesink/testsink/testsinkgui.ui b/plugins/samplesink/testsink/testsinkgui.ui index 37ecfb351..7858fc1c5 100644 --- a/plugins/samplesink/testsink/testsinkgui.ui +++ b/plugins/samplesink/testsink/testsinkgui.ui @@ -7,7 +7,7 @@ 0 0 364 - 350 + 420 @@ -19,7 +19,7 @@ 364 - 0 + 420 From fcb2906fd4174aa2caa685bc49532cfcde878079 Mon Sep 17 00:00:00 2001 From: f4exb Date: Sat, 30 Apr 2022 11:47:05 +0200 Subject: [PATCH 081/115] Massive UI revamping (v7): fixed device change. Fixes #1220 --- sdrgui/device/devicegui.cpp | 1 + sdrgui/mainwindow.cpp | 14 +++++++++++--- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/sdrgui/device/devicegui.cpp b/sdrgui/device/devicegui.cpp index 3b72ca117..4e313c03b 100644 --- a/sdrgui/device/devicegui.cpp +++ b/sdrgui/device/devicegui.cpp @@ -275,6 +275,7 @@ void DeviceGUI::openChangeDeviceDialog() if (dialog.exec() == QDialog::Accepted) { m_currentDeviceIndex = dialog.getSelectedDeviceIndex(); + dialog.setParent(nullptr); emit deviceChange(m_currentDeviceIndex); } } diff --git a/sdrgui/mainwindow.cpp b/sdrgui/mainwindow.cpp index f65eca5c5..08dfa2faa 100644 --- a/sdrgui/mainwindow.cpp +++ b/sdrgui/mainwindow.cpp @@ -2080,6 +2080,7 @@ void MainWindow::sampleSourceChange(int deviceSetIndex, int newDeviceIndex, Work { qDebug("MainWindow::sampleSourceChange: deviceSet %d workspace: %d", deviceSetIndex, workspace->getIndex()); DeviceUISet *deviceUISet = m_deviceUIs[deviceSetIndex]; + QPoint p = deviceUISet->m_deviceGUI->pos(); workspace->removeFromMdiArea(deviceUISet->m_deviceGUI); // deviceUI->m_deviceAPI->saveSamplingDeviceSettings(m_mainCore->m_settings.getWorkingPreset()); // save old API settings deviceUISet->m_deviceAPI->stopDeviceEngine(); @@ -2087,7 +2088,7 @@ void MainWindow::sampleSourceChange(int deviceSetIndex, int newDeviceIndex, Work // deletes old UI and input object deviceUISet->m_deviceAPI->getSampleSource()->setMessageQueueToGUI(nullptr); // have source stop sending messages to the GUI - //deviceUISet->m_deviceGUI->destroy(); + deviceUISet->m_deviceGUI->destroy(); deviceUISet->m_deviceAPI->resetSamplingDeviceId(); deviceUISet->m_deviceAPI->getPluginInterface()->deleteSampleSourcePluginInstanceInput(deviceUISet->m_deviceAPI->getSampleSource()); deviceUISet->m_deviceAPI->clearBuddiesLists(); // clear old API buddies lists @@ -2095,6 +2096,7 @@ void MainWindow::sampleSourceChange(int deviceSetIndex, int newDeviceIndex, Work sampleSourceCreate(deviceSetIndex, newDeviceIndex, deviceUISet); deviceUISet->m_deviceGUI->setWorkspaceIndex(workspace->getIndex()); workspace->addToMdiArea(deviceUISet->m_deviceGUI); + deviceUISet->m_deviceGUI->move(p); QObject::connect( deviceUISet->m_deviceGUI, @@ -2111,12 +2113,14 @@ void MainWindow::sampleSinkChange(int deviceSetIndex, int newDeviceIndex, Worksp { qDebug("MainWindow::sampleSinkChange: deviceSet %d workspace: %d", deviceSetIndex, workspace->getIndex()); DeviceUISet *deviceUISet = m_deviceUIs[deviceSetIndex]; + QPoint p = deviceUISet->m_deviceGUI->pos(); + workspace->removeFromMdiArea(deviceUISet->m_deviceGUI); deviceUISet->m_deviceAPI->saveSamplingDeviceSettings(m_mainCore->m_settings.getWorkingPreset()); // save old API settings deviceUISet->m_deviceAPI->stopDeviceEngine(); // deletes old UI and output object deviceUISet->m_deviceAPI->getSampleSink()->setMessageQueueToGUI(nullptr); // have sink stop sending messages to the GUI - // m_deviceUIs[deviceSetIndex]->m_deviceGUI->destroy(); + m_deviceUIs[deviceSetIndex]->m_deviceGUI->destroy(); deviceUISet->m_deviceAPI->resetSamplingDeviceId(); deviceUISet->m_deviceAPI->getPluginInterface()->deleteSampleSinkPluginInstanceOutput(deviceUISet->m_deviceAPI->getSampleSink()); deviceUISet->m_deviceAPI->clearBuddiesLists(); // clear old API buddies lists @@ -2124,6 +2128,7 @@ void MainWindow::sampleSinkChange(int deviceSetIndex, int newDeviceIndex, Worksp sampleSinkCreate(deviceSetIndex, newDeviceIndex, deviceUISet); deviceUISet->m_deviceGUI->setWorkspaceIndex(workspace->getIndex()); workspace->addToMdiArea(deviceUISet->m_deviceGUI); + deviceUISet->m_deviceGUI->move(p); QObject::connect( deviceUISet->m_deviceGUI, @@ -2140,18 +2145,21 @@ void MainWindow::sampleMIMOChange(int deviceSetIndex, int newDeviceIndex, Worksp { qDebug("MainWindow::sampleSinkChange: deviceSet %d workspace: %d", deviceSetIndex, workspace->getIndex()); DeviceUISet *deviceUISet = m_deviceUIs[deviceSetIndex]; + QPoint p = deviceUISet->m_deviceGUI->pos(); + workspace->removeFromMdiArea(deviceUISet->m_deviceGUI); deviceUISet->m_deviceAPI->saveSamplingDeviceSettings(m_mainCore->m_settings.getWorkingPreset()); // save old API settings deviceUISet->m_deviceAPI->stopDeviceEngine(); // deletes old UI and output object deviceUISet->m_deviceAPI->getSampleMIMO()->setMessageQueueToGUI(nullptr); // have sink stop sending messages to the GUI - // m_deviceUIs[tabIndex]->m_deviceGUI->destroy(); + deviceUISet->m_deviceGUI->destroy(); deviceUISet->m_deviceAPI->resetSamplingDeviceId(); deviceUISet->m_deviceAPI->getPluginInterface()->deleteSampleMIMOPluginInstanceMIMO(deviceUISet->m_deviceAPI->getSampleMIMO()); sampleMIMOCreate(deviceSetIndex, newDeviceIndex, deviceUISet); deviceUISet->m_deviceGUI->setWorkspaceIndex(workspace->getIndex()); workspace->addToMdiArea(deviceUISet->m_deviceGUI); + deviceUISet->m_deviceGUI->move(p); QObject::connect( deviceUISet->m_deviceGUI, From 89196c40b28f7852fabb7a07b8db317b79c79add Mon Sep 17 00:00:00 2001 From: f4exb Date: Sat, 30 Apr 2022 12:52:25 +0200 Subject: [PATCH 082/115] Massive UI revamping (v7): Make soapy SDR support optional. Fixes #718 --- sdrbase/mainparser.cpp | 10 +++++++--- sdrbase/mainparser.h | 3 +++ sdrbase/plugin/pluginmanager.cpp | 9 ++++++++- sdrbase/plugin/pluginmanager.h | 2 ++ sdrgui/mainwindow.cpp | 1 + sdrsrv/mainserver.cpp | 1 + 6 files changed, 22 insertions(+), 4 deletions(-) diff --git a/sdrbase/mainparser.cpp b/sdrbase/mainparser.cpp index 4f1a4bab6..fa96f6033 100644 --- a/sdrbase/mainparser.cpp +++ b/sdrbase/mainparser.cpp @@ -35,12 +35,14 @@ MainParser::MainParser() : "FFTW Wisdom file.", "file", ""), - m_scratchOption("scratch", "Start from scratch (no current config).") + m_scratchOption("scratch", "Start from scratch (no current config)."), + m_soapyOption("soapy", "Activate Soapy SDR support.") { m_serverAddress = ""; // Bind to any address m_serverPort = 8091; m_scratch = false; + m_soapy = false; m_fftwfWindowFileName = ""; m_parser.setApplicationDescription("Software Defined Radio application"); @@ -51,6 +53,7 @@ MainParser::MainParser() : m_parser.addOption(m_serverPortOption); m_parser.addOption(m_fftwfWisdomOption); m_parser.addOption(m_scratchOption); + m_parser.addOption(m_soapyOption); } MainParser::~MainParser() @@ -94,10 +97,11 @@ void MainParser::parse(const QCoreApplication& app) } // FFTWF wisdom file - m_fftwfWindowFileName = m_parser.value(m_fftwfWisdomOption); // Scratch mode - m_scratch = m_parser.isSet(m_scratchOption); + + // Soapy SDR support + m_soapy = m_parser.isSet(m_soapyOption); } diff --git a/sdrbase/mainparser.h b/sdrbase/mainparser.h index a04541909..f472d2733 100644 --- a/sdrbase/mainparser.h +++ b/sdrbase/mainparser.h @@ -35,6 +35,7 @@ public: const QString& getServerAddress() const { return m_serverAddress; } uint16_t getServerPort() const { return m_serverPort; } bool getScratch() const { return m_scratch; } + bool getSoapy() const { return m_soapy; } const QString& getFFTWFWisdomFileName() const { return m_fftwfWindowFileName; } private: @@ -42,12 +43,14 @@ private: uint16_t m_serverPort; QString m_fftwfWindowFileName; bool m_scratch; + bool m_soapy; QCommandLineParser m_parser; QCommandLineOption m_serverAddressOption; QCommandLineOption m_serverPortOption; QCommandLineOption m_fftwfWisdomOption; QCommandLineOption m_scratchOption; + QCommandLineOption m_soapyOption; }; diff --git a/sdrbase/plugin/pluginmanager.cpp b/sdrbase/plugin/pluginmanager.cpp index 79073b952..64229029f 100644 --- a/sdrbase/plugin/pluginmanager.cpp +++ b/sdrbase/plugin/pluginmanager.cpp @@ -53,7 +53,8 @@ const QString PluginManager::m_testMIMODeviceTypeID = "sdrangel.samplemimo.testm PluginManager::PluginManager(QObject* parent) : QObject(parent), - m_pluginAPI(this) + m_pluginAPI(this), + m_enableSoapy(false) { } @@ -216,6 +217,12 @@ void PluginManager::loadPluginsDir(const QDir& dir) { if (QLibrary::isLibrary(fileName)) { + if (!m_enableSoapy && (fileName.contains("libinputsoapysdr") || (fileName.contains("liboutputsoapysdr")))) + { + qInfo("PluginManager::loadPluginsDir: Soapy SDR disabled skipping %s", qPrintable(fileName)); + continue; + } + qDebug("PluginManager::loadPluginsDir: fileName: %s", qPrintable(fileName)); QPluginLoader* pluginLoader = new QPluginLoader(pluginsDir.absoluteFilePath(fileName)); diff --git a/sdrbase/plugin/pluginmanager.h b/sdrbase/plugin/pluginmanager.h index 71713810c..763af09e9 100644 --- a/sdrbase/plugin/pluginmanager.h +++ b/sdrbase/plugin/pluginmanager.h @@ -57,6 +57,7 @@ public: ~PluginManager(); PluginAPI *getPluginAPI() { return &m_pluginAPI; } + void setEnableSoapy(bool enableSoapy) { m_enableSoapy = enableSoapy; } void loadPlugins(const QString& pluginsSubDir); void loadPluginsPart(const QString& pluginsSubDir); void loadPluginsFinal(); @@ -124,6 +125,7 @@ private: PluginAPI m_pluginAPI; Plugins m_plugins; + bool m_enableSoapy; PluginAPI::ChannelRegistrations m_rxChannelRegistrations; //!< Channel plugins register here PluginAPI::ChannelRegistrations m_txChannelRegistrations; //!< Channel plugins register here diff --git a/sdrgui/mainwindow.cpp b/sdrgui/mainwindow.cpp index 08dfa2faa..14e535310 100644 --- a/sdrgui/mainwindow.cpp +++ b/sdrgui/mainwindow.cpp @@ -185,6 +185,7 @@ MainWindow::MainWindow(qtwebapp::LoggerWithFile *logger, const MainParser& parse m_pluginManager = new PluginManager(this); m_mainCore->m_pluginManager = m_pluginManager; + m_pluginManager->setEnableSoapy(parser.getSoapy()); m_pluginManager->loadPlugins(QString("plugins")); m_pluginManager->loadPluginsNonDiscoverable(m_mainCore->m_settings.getDeviceUserArgs()); diff --git a/sdrsrv/mainserver.cpp b/sdrsrv/mainserver.cpp index 5380041f0..a02632844 100644 --- a/sdrsrv/mainserver.cpp +++ b/sdrsrv/mainserver.cpp @@ -59,6 +59,7 @@ MainServer::MainServer(qtwebapp::LoggerWithFile *logger, const MainParser& parse qDebug() << "MainServer::MainServer: load plugins..."; m_mainCore->m_pluginManager = new PluginManager(this); + m_mainCore->m_pluginManager->setEnableSoapy(parser.getSoapy()); m_mainCore->m_pluginManager->loadPlugins(QString("pluginssrv")); connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleMessages()), Qt::QueuedConnection); From fd61700882796d5f0a7f92ab29cbb81596b799df Mon Sep 17 00:00:00 2001 From: f4exb Date: Sun, 1 May 2022 09:59:24 +0200 Subject: [PATCH 083/115] v7: rename VORDemod classes to VORDemodMC classes. Part of #1223 --- plugins/channelrx/demodvor/vordemod.cpp | 90 +++++------ plugins/channelrx/demodvor/vordemod.h | 30 ++-- .../channelrx/demodvor/vordemodbaseband.cpp | 46 +++--- plugins/channelrx/demodvor/vordemodbaseband.h | 22 +-- plugins/channelrx/demodvor/vordemodgui.cpp | 144 +++++++++--------- plugins/channelrx/demodvor/vordemodgui.h | 30 ++-- plugins/channelrx/demodvor/vordemodgui.ui | 4 +- plugins/channelrx/demodvor/vordemodplugin.cpp | 24 +-- plugins/channelrx/demodvor/vordemodplugin.h | 4 +- plugins/channelrx/demodvor/vordemodreport.cpp | 6 +- plugins/channelrx/demodvor/vordemodreport.h | 6 +- .../channelrx/demodvor/vordemodsettings.cpp | 8 +- plugins/channelrx/demodvor/vordemodsettings.h | 4 +- plugins/channelrx/demodvor/vordemodsink.cpp | 32 ++-- plugins/channelrx/demodvor/vordemodsink.h | 10 +- .../demodvor/vordemodwebapiadapter.cpp | 4 +- .../demodvor/vordemodwebapiadapter.h | 2 +- 17 files changed, 233 insertions(+), 233 deletions(-) diff --git a/plugins/channelrx/demodvor/vordemod.cpp b/plugins/channelrx/demodvor/vordemod.cpp index f09204d93..fa4fd4b10 100644 --- a/plugins/channelrx/demodvor/vordemod.cpp +++ b/plugins/channelrx/demodvor/vordemod.cpp @@ -41,19 +41,19 @@ #include "util/db.h" #include "maincore.h" -MESSAGE_CLASS_DEFINITION(VORDemod::MsgConfigureVORDemod, Message) +MESSAGE_CLASS_DEFINITION(VORDemodMC::MsgConfigureVORDemod, Message) -const char * const VORDemod::m_channelIdURI = "sdrangel.channel.vordemod"; -const char * const VORDemod::m_channelId = "VORDemod"; +const char * const VORDemodMC::m_channelIdURI = "sdrangel.channel.vordemod"; +const char * const VORDemodMC::m_channelId = "VORDemodMC"; -VORDemod::VORDemod(DeviceAPI *deviceAPI) : +VORDemodMC::VORDemodMC(DeviceAPI *deviceAPI) : ChannelAPI(m_channelIdURI, ChannelAPI::StreamSingleSink), m_deviceAPI(deviceAPI), m_basebandSampleRate(0) { setObjectName(m_channelId); - m_basebandSink = new VORDemodBaseband(); + m_basebandSink = new VORDemodMCBaseband(); m_basebandSink->moveToThread(&m_thread); applySettings(m_settings, true); @@ -66,24 +66,24 @@ VORDemod::VORDemod(DeviceAPI *deviceAPI) : m_networkManager, &QNetworkAccessManager::finished, this, - &VORDemod::networkManagerFinished + &VORDemodMC::networkManagerFinished ); QObject::connect( this, &ChannelAPI::indexInDeviceSetChanged, this, - &VORDemod::handleIndexInDeviceSetChanged + &VORDemodMC::handleIndexInDeviceSetChanged ); } -VORDemod::~VORDemod() +VORDemodMC::~VORDemodMC() { - qDebug("VORDemod::~VORDemod"); + qDebug("VORDemodMC::~VORDemodMC"); QObject::disconnect( m_networkManager, &QNetworkAccessManager::finished, this, - &VORDemod::networkManagerFinished + &VORDemodMC::networkManagerFinished ); delete m_networkManager; m_deviceAPI->removeChannelSinkAPI(this); @@ -96,7 +96,7 @@ VORDemod::~VORDemod() delete m_basebandSink; } -void VORDemod::setDeviceAPI(DeviceAPI *deviceAPI) +void VORDemodMC::setDeviceAPI(DeviceAPI *deviceAPI) { if (deviceAPI != m_deviceAPI) { @@ -108,20 +108,20 @@ void VORDemod::setDeviceAPI(DeviceAPI *deviceAPI) } } -uint32_t VORDemod::getNumberOfDeviceStreams() const +uint32_t VORDemodMC::getNumberOfDeviceStreams() const { return m_deviceAPI->getNbSourceStreams(); } -void VORDemod::feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool firstOfBurst) +void VORDemodMC::feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool firstOfBurst) { (void) firstOfBurst; m_basebandSink->feed(begin, end); } -void VORDemod::start() +void VORDemodMC::start() { - qDebug("VORDemod::start"); + qDebug("VORDemodMC::start"); m_basebandSink->reset(); m_basebandSink->startWork(); @@ -130,24 +130,24 @@ void VORDemod::start() DSPSignalNotification *dspMsg = new DSPSignalNotification(m_basebandSampleRate, m_centerFrequency); m_basebandSink->getInputMessageQueue()->push(dspMsg); - VORDemodBaseband::MsgConfigureVORDemodBaseband *msg = VORDemodBaseband::MsgConfigureVORDemodBaseband::create(m_settings, true); + VORDemodMCBaseband::MsgConfigureVORDemodBaseband *msg = VORDemodMCBaseband::MsgConfigureVORDemodBaseband::create(m_settings, true); m_basebandSink->getInputMessageQueue()->push(msg); } -void VORDemod::stop() +void VORDemodMC::stop() { - qDebug("VORDemod::stop"); + qDebug("VORDemodMC::stop"); m_basebandSink->stopWork(); m_thread.quit(); m_thread.wait(); } -bool VORDemod::handleMessage(const Message& cmd) +bool VORDemodMC::handleMessage(const Message& cmd) { if (MsgConfigureVORDemod::match(cmd)) { MsgConfigureVORDemod& cfg = (MsgConfigureVORDemod&) cmd; - qDebug() << "VORDemod::handleMessage: MsgConfigureVORDemod"; + qDebug() << "VORDemodMC::handleMessage: MsgConfigureVORDemod"; applySettings(cfg.getSettings(), cfg.getForce()); return true; @@ -159,7 +159,7 @@ bool VORDemod::handleMessage(const Message& cmd) m_centerFrequency = notif.getCenterFrequency(); // Forward to the sink DSPSignalNotification* rep = new DSPSignalNotification(notif); // make a copy - qDebug() << "VORDemod::handleMessage: DSPSignalNotification"; + qDebug() << "VORDemodMC::handleMessage: DSPSignalNotification"; m_basebandSink->getInputMessageQueue()->push(rep); // Forward to GUI if any if (m_guiMessageQueue) { @@ -174,9 +174,9 @@ bool VORDemod::handleMessage(const Message& cmd) } } -void VORDemod::applySettings(const VORDemodSettings& settings, bool force) +void VORDemodMC::applySettings(const VORDemodMCSettings& settings, bool force) { - qDebug() << "VORDemod::applySettings:" + qDebug() << "VORDemodMC::applySettings:" << " m_volume: " << settings.m_volume << " m_squelch: " << settings.m_squelch << " m_audioMute: " << settings.m_audioMute @@ -227,7 +227,7 @@ void VORDemod::applySettings(const VORDemodSettings& settings, bool force) reverseAPIKeys.append("magDecAdjust"); } - VORDemodBaseband::MsgConfigureVORDemodBaseband *msg = VORDemodBaseband::MsgConfigureVORDemodBaseband::create(settings, force); + VORDemodMCBaseband::MsgConfigureVORDemodBaseband *msg = VORDemodMCBaseband::MsgConfigureVORDemodBaseband::create(settings, force); m_basebandSink->getInputMessageQueue()->push(msg); if (settings.m_useReverseAPI) @@ -250,12 +250,12 @@ void VORDemod::applySettings(const VORDemodSettings& settings, bool force) m_settings = settings; } -QByteArray VORDemod::serialize() const +QByteArray VORDemodMC::serialize() const { return m_settings.serialize(); } -bool VORDemod::deserialize(const QByteArray& data) +bool VORDemodMC::deserialize(const QByteArray& data) { if (m_settings.deserialize(data)) { @@ -272,7 +272,7 @@ bool VORDemod::deserialize(const QByteArray& data) } } -int VORDemod::webapiSettingsGet( +int VORDemodMC::webapiSettingsGet( SWGSDRangel::SWGChannelSettings& response, QString& errorMessage) { @@ -283,20 +283,20 @@ int VORDemod::webapiSettingsGet( return 200; } -int VORDemod::webapiSettingsPutPatch( +int VORDemodMC::webapiSettingsPutPatch( bool force, const QStringList& channelSettingsKeys, SWGSDRangel::SWGChannelSettings& response, QString& errorMessage) { (void) errorMessage; - VORDemodSettings settings = m_settings; + VORDemodMCSettings settings = m_settings; webapiUpdateChannelSettings(settings, channelSettingsKeys, response); MsgConfigureVORDemod *msg = MsgConfigureVORDemod::create(settings, force); m_inputMessageQueue.push(msg); - qDebug("VORDemod::webapiSettingsPutPatch: forward to GUI: %p", m_guiMessageQueue); + qDebug("VORDemodMC::webapiSettingsPutPatch: forward to GUI: %p", m_guiMessageQueue); if (m_guiMessageQueue) // forward to GUI if any { MsgConfigureVORDemod *msgToGUI = MsgConfigureVORDemod::create(settings, force); @@ -308,8 +308,8 @@ int VORDemod::webapiSettingsPutPatch( return 200; } -void VORDemod::webapiUpdateChannelSettings( - VORDemodSettings& settings, +void VORDemodMC::webapiUpdateChannelSettings( + VORDemodMCSettings& settings, const QStringList& channelSettingsKeys, SWGSDRangel::SWGChannelSettings& response) { @@ -364,7 +364,7 @@ void VORDemod::webapiUpdateChannelSettings( } } -int VORDemod::webapiReportGet( +int VORDemodMC::webapiReportGet( SWGSDRangel::SWGChannelReport& response, QString& errorMessage) { @@ -375,7 +375,7 @@ int VORDemod::webapiReportGet( return 200; } -void VORDemod::webapiFormatChannelSettings(SWGSDRangel::SWGChannelSettings& response, const VORDemodSettings& settings) +void VORDemodMC::webapiFormatChannelSettings(SWGSDRangel::SWGChannelSettings& response, const VORDemodMCSettings& settings) { response.getVorDemodSettings()->setAudioMute(settings.m_audioMute ? 1 : 0); response.getVorDemodSettings()->setRgbColor(settings.m_rgbColor); @@ -439,7 +439,7 @@ void VORDemod::webapiFormatChannelSettings(SWGSDRangel::SWGChannelSettings& resp } } -void VORDemod::webapiFormatChannelReport(SWGSDRangel::SWGChannelReport& response) +void VORDemodMC::webapiFormatChannelReport(SWGSDRangel::SWGChannelReport& response) { double magsqAvg, magsqPeak; int nbMagsqSamples; @@ -450,7 +450,7 @@ void VORDemod::webapiFormatChannelReport(SWGSDRangel::SWGChannelReport& response response.getVorDemodReport()->setAudioSampleRate(m_basebandSink->getAudioSampleRate()); } -void VORDemod::webapiReverseSendSettings(QList& channelSettingsKeys, const VORDemodSettings& settings, bool force) +void VORDemodMC::webapiReverseSendSettings(QList& channelSettingsKeys, const VORDemodMCSettings& settings, bool force) { SWGSDRangel::SWGChannelSettings *swgChannelSettings = new SWGSDRangel::SWGChannelSettings(); webapiFormatChannelSettings(channelSettingsKeys, swgChannelSettings, settings, force); @@ -475,10 +475,10 @@ void VORDemod::webapiReverseSendSettings(QList& channelSettingsKeys, co delete swgChannelSettings; } -void VORDemod::sendChannelSettings( +void VORDemodMC::sendChannelSettings( const QList& pipes, QList& channelSettingsKeys, - const VORDemodSettings& settings, + const VORDemodMCSettings& settings, bool force) { for (const auto& pipe : pipes) @@ -500,17 +500,17 @@ void VORDemod::sendChannelSettings( } } -void VORDemod::webapiFormatChannelSettings( +void VORDemodMC::webapiFormatChannelSettings( QList& channelSettingsKeys, SWGSDRangel::SWGChannelSettings *swgChannelSettings, - const VORDemodSettings& settings, + const VORDemodMCSettings& settings, bool force ) { swgChannelSettings->setDirection(0); // Single sink (Rx) swgChannelSettings->setOriginatorChannelIndex(getIndexInDeviceSet()); swgChannelSettings->setOriginatorDeviceSetIndex(getDeviceSetIndex()); - swgChannelSettings->setChannelType(new QString("VORDemod")); + swgChannelSettings->setChannelType(new QString("VORDemodMC")); swgChannelSettings->setVorDemodSettings(new SWGSDRangel::SWGVORDemodSettings()); SWGSDRangel::SWGVORDemodSettings *swgVORDemodSettings = swgChannelSettings->getVorDemodSettings(); @@ -559,13 +559,13 @@ void VORDemod::webapiFormatChannelSettings( } } -void VORDemod::networkManagerFinished(QNetworkReply *reply) +void VORDemodMC::networkManagerFinished(QNetworkReply *reply) { QNetworkReply::NetworkError replyError = reply->error(); if (replyError) { - qWarning() << "VORDemod::networkManagerFinished:" + qWarning() << "VORDemodMC::networkManagerFinished:" << " error(" << (int) replyError << "): " << replyError << ": " << reply->errorString(); @@ -574,13 +574,13 @@ void VORDemod::networkManagerFinished(QNetworkReply *reply) { QString answer = reply->readAll(); answer.chop(1); // remove last \n - qDebug("VORDemod::networkManagerFinished: reply:\n%s", answer.toStdString().c_str()); + qDebug("VORDemodMC::networkManagerFinished: reply:\n%s", answer.toStdString().c_str()); } reply->deleteLater(); } -void VORDemod::handleIndexInDeviceSetChanged(int index) +void VORDemodMC::handleIndexInDeviceSetChanged(int index) { if (index < 0) { return; diff --git a/plugins/channelrx/demodvor/vordemod.h b/plugins/channelrx/demodvor/vordemod.h index ef16083d6..f6325b5d8 100644 --- a/plugins/channelrx/demodvor/vordemod.h +++ b/plugins/channelrx/demodvor/vordemod.h @@ -37,33 +37,33 @@ class QThread; class DeviceAPI; class ObjectPipe; -class VORDemod : public BasebandSampleSink, public ChannelAPI { +class VORDemodMC : public BasebandSampleSink, public ChannelAPI { public: class MsgConfigureVORDemod : public Message { MESSAGE_CLASS_DECLARATION public: - const VORDemodSettings& getSettings() const { return m_settings; } + const VORDemodMCSettings& getSettings() const { return m_settings; } bool getForce() const { return m_force; } - static MsgConfigureVORDemod* create(const VORDemodSettings& settings, bool force) + static MsgConfigureVORDemod* create(const VORDemodMCSettings& settings, bool force) { return new MsgConfigureVORDemod(settings, force); } private: - VORDemodSettings m_settings; + VORDemodMCSettings m_settings; bool m_force; - MsgConfigureVORDemod(const VORDemodSettings& settings, bool force) : + MsgConfigureVORDemod(const VORDemodMCSettings& settings, bool force) : Message(), m_settings(settings), m_force(force) { } }; - VORDemod(DeviceAPI *deviceAPI); - virtual ~VORDemod(); + VORDemodMC(DeviceAPI *deviceAPI); + virtual ~VORDemodMC(); virtual void destroy() { delete this; } virtual void setDeviceAPI(DeviceAPI *deviceAPI); virtual DeviceAPI *getDeviceAPI() { return m_deviceAPI; } @@ -111,10 +111,10 @@ public: static void webapiFormatChannelSettings( SWGSDRangel::SWGChannelSettings& response, - const VORDemodSettings& settings); + const VORDemodMCSettings& settings); static void webapiUpdateChannelSettings( - VORDemodSettings& settings, + VORDemodMCSettings& settings, const QStringList& channelSettingsKeys, SWGSDRangel::SWGChannelSettings& response); @@ -138,8 +138,8 @@ public: private: DeviceAPI *m_deviceAPI; QThread m_thread; - VORDemodBaseband* m_basebandSink; - VORDemodSettings m_settings; + VORDemodMCBaseband* m_basebandSink; + VORDemodMCSettings m_settings; int m_basebandSampleRate; //!< stored from device message used when starting baseband sink qint64 m_centerFrequency; @@ -147,19 +147,19 @@ private: QNetworkRequest m_networkRequest; virtual bool handleMessage(const Message& cmd); - void applySettings(const VORDemodSettings& settings, bool force = false); + void applySettings(const VORDemodMCSettings& settings, bool force = false); void webapiFormatChannelReport(SWGSDRangel::SWGChannelReport& response); - void webapiReverseSendSettings(QList& channelSettingsKeys, const VORDemodSettings& settings, bool force); + void webapiReverseSendSettings(QList& channelSettingsKeys, const VORDemodMCSettings& settings, bool force); void sendChannelSettings( const QList& pipes, QList& channelSettingsKeys, - const VORDemodSettings& settings, + const VORDemodMCSettings& settings, bool force ); void webapiFormatChannelSettings( QList& channelSettingsKeys, SWGSDRangel::SWGChannelSettings *swgChannelSettings, - const VORDemodSettings& settings, + const VORDemodMCSettings& settings, bool force ); diff --git a/plugins/channelrx/demodvor/vordemodbaseband.cpp b/plugins/channelrx/demodvor/vordemodbaseband.cpp index 41911eb34..9cdebd04f 100644 --- a/plugins/channelrx/demodvor/vordemodbaseband.cpp +++ b/plugins/channelrx/demodvor/vordemodbaseband.cpp @@ -25,15 +25,15 @@ #include "vordemodbaseband.h" #include "vordemodreport.h" -MESSAGE_CLASS_DEFINITION(VORDemodBaseband::MsgConfigureVORDemodBaseband, Message) +MESSAGE_CLASS_DEFINITION(VORDemodMCBaseband::MsgConfigureVORDemodBaseband, Message) -VORDemodBaseband::VORDemodBaseband() : +VORDemodMCBaseband::VORDemodMCBaseband() : m_running(false), m_mutex(QMutex::Recursive), m_messageQueueToGUI(nullptr), m_basebandSampleRate(0) { - qDebug("VORDemodBaseband::VORDemodBaseband"); + qDebug("VORDemodMCBaseband::VORDemodMCBaseband"); m_sampleFifo.setSize(SampleSinkFifo::getSizePolicy(48000)); @@ -41,7 +41,7 @@ VORDemodBaseband::VORDemodBaseband() : DSPEngine::instance()->getAudioDeviceManager()->addAudioSink(&m_audioFifoBug, getInputMessageQueue()); } -VORDemodBaseband::~VORDemodBaseband() +VORDemodMCBaseband::~VORDemodMCBaseband() { m_inputMessageQueue.clear(); @@ -60,28 +60,28 @@ VORDemodBaseband::~VORDemodBaseband() m_channelizers.clear(); } -void VORDemodBaseband::reset() +void VORDemodMCBaseband::reset() { QMutexLocker mutexLocker(&m_mutex); m_inputMessageQueue.clear(); m_sampleFifo.reset(); } -void VORDemodBaseband::startWork() +void VORDemodMCBaseband::startWork() { QMutexLocker mutexLocker(&m_mutex); QObject::connect( &m_sampleFifo, &SampleSinkFifo::dataReady, this, - &VORDemodBaseband::handleData, + &VORDemodMCBaseband::handleData, Qt::QueuedConnection ); connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages())); m_running = true; } -void VORDemodBaseband::stopWork() +void VORDemodMCBaseband::stopWork() { QMutexLocker mutexLocker(&m_mutex); disconnect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages())); @@ -89,17 +89,17 @@ void VORDemodBaseband::stopWork() &m_sampleFifo, &SampleSinkFifo::dataReady, this, - &VORDemodBaseband::handleData + &VORDemodMCBaseband::handleData ); m_running = false; } -void VORDemodBaseband::feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end) +void VORDemodMCBaseband::feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end) { m_sampleFifo.write(begin, end); } -void VORDemodBaseband::handleData() +void VORDemodMCBaseband::handleData() { QMutexLocker mutexLocker(&m_mutex); @@ -128,7 +128,7 @@ void VORDemodBaseband::handleData() } } -void VORDemodBaseband::handleInputMessages() +void VORDemodMCBaseband::handleInputMessages() { Message* message; @@ -140,13 +140,13 @@ void VORDemodBaseband::handleInputMessages() } } -bool VORDemodBaseband::handleMessage(const Message& cmd) +bool VORDemodMCBaseband::handleMessage(const Message& cmd) { if (MsgConfigureVORDemodBaseband::match(cmd)) { QMutexLocker mutexLocker(&m_mutex); MsgConfigureVORDemodBaseband& cfg = (MsgConfigureVORDemodBaseband&) cmd; - qDebug() << "VORDemodBaseband::handleMessage: MsgConfigureVORDemodBaseband"; + qDebug() << "VORDemodMCBaseband::handleMessage: MsgConfigureVORDemodBaseband"; applySettings(cfg.getSettings(), cfg.getForce()); @@ -156,7 +156,7 @@ bool VORDemodBaseband::handleMessage(const Message& cmd) { QMutexLocker mutexLocker(&m_mutex); DSPSignalNotification& notif = (DSPSignalNotification&) cmd; - qDebug() << "VORDemodBaseband::handleMessage: DSPSignalNotification: basebandSampleRate: " << notif.getSampleRate() << " centerFrequency: " << notif.getCenterFrequency(); + qDebug() << "VORDemodMCBaseband::handleMessage: DSPSignalNotification: basebandSampleRate: " << notif.getSampleRate() << " centerFrequency: " << notif.getCenterFrequency(); m_centerFrequency = notif.getCenterFrequency(); setBasebandSampleRate(notif.getSampleRate()); m_sampleFifo.setSize(SampleSinkFifo::getSizePolicy(m_basebandSampleRate)); @@ -170,14 +170,14 @@ bool VORDemodBaseband::handleMessage(const Message& cmd) } // Calculate offset of VOR center frequency from sample source center frequency -void VORDemodBaseband::calculateOffset(VORDemodSink *sink) +void VORDemodMCBaseband::calculateOffset(VORDemodMCSink *sink) { int frequencyOffset = sink->m_vorFrequencyHz - m_centerFrequency; bool outOfBand = std::abs(frequencyOffset)+VORDEMOD_CHANNEL_BANDWIDTH > (m_basebandSampleRate/2); if (m_messageQueueToGUI != nullptr) { - VORDemodReport::MsgReportFreqOffset *msg = VORDemodReport::MsgReportFreqOffset::create(sink->m_subChannelId, frequencyOffset, outOfBand); + VORDemodMCReport::MsgReportFreqOffset *msg = VORDemodMCReport::MsgReportFreqOffset::create(sink->m_subChannelId, frequencyOffset, outOfBand); m_messageQueueToGUI->push(msg); } @@ -185,15 +185,15 @@ void VORDemodBaseband::calculateOffset(VORDemodSink *sink) sink->m_outOfBand = outOfBand; } -void VORDemodBaseband::applySettings(const VORDemodSettings& settings, bool force) +void VORDemodMCBaseband::applySettings(const VORDemodMCSettings& settings, bool force) { // Remove sub-channels no longer needed for (int i = 0; i < m_sinks.size(); i++) { if (!settings.m_subChannelSettings.contains(m_sinks[i]->m_subChannelId)) { - qDebug() << "VORDemodBaseband::applySettings: Removing sink " << m_sinks[i]->m_subChannelId; - VORDemodSink *sink = m_sinks[i]; + qDebug() << "VORDemodMCBaseband::applySettings: Removing sink " << m_sinks[i]->m_subChannelId; + VORDemodMCSink *sink = m_sinks[i]; DSPEngine::instance()->getAudioDeviceManager()->removeAudioSink(m_sinks[i]->getAudioFifo()); m_sinks.removeAt(i); delete sink; @@ -217,8 +217,8 @@ void VORDemodBaseband::applySettings(const VORDemodSettings& settings, bool forc if (j == m_sinks.size()) { // Add a sub-channel sink - qDebug() << "VORDemodBaseband::applySettings: Adding sink " << subChannelSettings->m_id; - VORDemodSink *sink = new VORDemodSink(settings, subChannelSettings->m_id, m_messageQueueToGUI); + qDebug() << "VORDemodMCBaseband::applySettings: Adding sink " << subChannelSettings->m_id; + VORDemodMCSink *sink = new VORDemodMCSink(settings, subChannelSettings->m_id, m_messageQueueToGUI); DownChannelizer *channelizer = new DownChannelizer(sink); channelizer->setBasebandSampleRate(m_basebandSampleRate); DSPEngine::instance()->getAudioDeviceManager()->addAudioSink(sink->getAudioFifo(), getInputMessageQueue()); @@ -269,7 +269,7 @@ void VORDemodBaseband::applySettings(const VORDemodSettings& settings, bool forc m_settings = settings; } -void VORDemodBaseband::setBasebandSampleRate(int sampleRate) +void VORDemodMCBaseband::setBasebandSampleRate(int sampleRate) { m_basebandSampleRate = sampleRate; for (int i = 0; i < m_sinks.size(); i++) diff --git a/plugins/channelrx/demodvor/vordemodbaseband.h b/plugins/channelrx/demodvor/vordemodbaseband.h index 5dad65a95..13f1633b4 100644 --- a/plugins/channelrx/demodvor/vordemodbaseband.h +++ b/plugins/channelrx/demodvor/vordemodbaseband.h @@ -30,7 +30,7 @@ class DownChannelizer; -class VORDemodBaseband : public QObject +class VORDemodMCBaseband : public QObject { Q_OBJECT public: @@ -38,27 +38,27 @@ public: MESSAGE_CLASS_DECLARATION public: - const VORDemodSettings& getSettings() const { return m_settings; } + const VORDemodMCSettings& getSettings() const { return m_settings; } bool getForce() const { return m_force; } - static MsgConfigureVORDemodBaseband* create(const VORDemodSettings& settings, bool force) + static MsgConfigureVORDemodBaseband* create(const VORDemodMCSettings& settings, bool force) { return new MsgConfigureVORDemodBaseband(settings, force); } private: - VORDemodSettings m_settings; + VORDemodMCSettings m_settings; bool m_force; - MsgConfigureVORDemodBaseband(const VORDemodSettings& settings, bool force) : + MsgConfigureVORDemodBaseband(const VORDemodMCSettings& settings, bool force) : Message(), m_settings(settings), m_force(force) { } }; - VORDemodBaseband(); - ~VORDemodBaseband(); + VORDemodMCBaseband(); + ~VORDemodMCBaseband(); void reset(); void startWork(); void stopWork(); @@ -115,10 +115,10 @@ public: private: SampleSinkFifo m_sampleFifo; QList m_channelizers; - QList m_sinks; + QList m_sinks; AudioFifo m_audioFifoBug; // FIXME: Removing this results in audio stopping when demod is closed MessageQueue m_inputMessageQueue; //!< Queue for asynchronous inbound communication - VORDemodSettings m_settings; + VORDemodMCSettings m_settings; bool m_running; QMutex m_mutex; MessageQueue *m_messageQueueToGUI; @@ -126,8 +126,8 @@ private: int m_centerFrequency; bool handleMessage(const Message& cmd); - void calculateOffset(VORDemodSink *sink); - void applySettings(const VORDemodSettings& settings, bool force = false); + void calculateOffset(VORDemodMCSink *sink); + void applySettings(const VORDemodMCSettings& settings, bool force = false); private slots: void handleInputMessages(); diff --git a/plugins/channelrx/demodvor/vordemodgui.cpp b/plugins/channelrx/demodvor/vordemodgui.cpp index 1a4820be2..30ffacafd 100644 --- a/plugins/channelrx/demodvor/vordemodgui.cpp +++ b/plugins/channelrx/demodvor/vordemodgui.cpp @@ -390,7 +390,7 @@ static bool calcIntersectionPoint(float lat1, float lon1, float bearing1, float return true; } -VORGUI::VORGUI(NavAid *navAid, VORDemodGUI *gui) : +VORGUI::VORGUI(NavAid *navAid, VORDemodMCGUI *gui) : m_navAid(navAid), m_gui(gui) { @@ -577,7 +577,7 @@ bool VORModel::findIntersection(float &lat, float &lon) return false; } -void VORDemodGUI::resizeTable() +void VORDemodMCGUI::resizeTable() { // Fill table with a row of dummy data that will size the columns nicely // Trailing spaces are for sort arrow @@ -600,7 +600,7 @@ void VORDemodGUI::resizeTable() } // Columns in table reordered -void VORDemodGUI::vorData_sectionMoved(int logicalIndex, int oldVisualIndex, int newVisualIndex) +void VORDemodMCGUI::vorData_sectionMoved(int logicalIndex, int oldVisualIndex, int newVisualIndex) { (void) oldVisualIndex; @@ -608,7 +608,7 @@ void VORDemodGUI::vorData_sectionMoved(int logicalIndex, int oldVisualIndex, int } // Column in table resized (when hidden size is 0) -void VORDemodGUI::vorData_sectionResized(int logicalIndex, int oldSize, int newSize) +void VORDemodMCGUI::vorData_sectionResized(int logicalIndex, int oldSize, int newSize) { (void) oldSize; @@ -616,13 +616,13 @@ void VORDemodGUI::vorData_sectionResized(int logicalIndex, int oldSize, int newS } // Right click in table header - show column select menu -void VORDemodGUI::columnSelectMenu(QPoint pos) +void VORDemodMCGUI::columnSelectMenu(QPoint pos) { menu->popup(ui->vorData->horizontalHeader()->viewport()->mapToGlobal(pos)); } // Hide/show column when menu selected -void VORDemodGUI::columnSelectMenuChecked(bool checked) +void VORDemodMCGUI::columnSelectMenuChecked(bool checked) { (void) checked; @@ -635,7 +635,7 @@ void VORDemodGUI::columnSelectMenuChecked(bool checked) } // Create column select menu item -QAction *VORDemodGUI::createCheckableItem(QString &text, int idx, bool checked) +QAction *VORDemodMCGUI::createCheckableItem(QString &text, int idx, bool checked) { QAction *action = new QAction(text, this); action->setCheckable(true); @@ -646,7 +646,7 @@ QAction *VORDemodGUI::createCheckableItem(QString &text, int idx, bool checked) } // Called when a VOR is selected on the map -void VORDemodGUI::selectVOR(VORGUI *vorGUI, bool selected) +void VORDemodMCGUI::selectVOR(VORGUI *vorGUI, bool selected) { if (selected) { @@ -691,7 +691,7 @@ void VORDemodGUI::selectVOR(VORGUI *vorGUI, bool selected) } } -void VORDemodGUI::updateVORs() +void VORDemodMCGUI::updateVORs() { m_vorModel.removeAllVORs(); QHash::iterator i = m_vors->begin(); @@ -714,30 +714,30 @@ void VORDemodGUI::updateVORs() } } -VORDemodGUI* VORDemodGUI::create(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel) +VORDemodMCGUI* VORDemodMCGUI::create(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel) { - VORDemodGUI* gui = new VORDemodGUI(pluginAPI, deviceUISet, rxChannel); + VORDemodMCGUI* gui = new VORDemodMCGUI(pluginAPI, deviceUISet, rxChannel); return gui; } -void VORDemodGUI::destroy() +void VORDemodMCGUI::destroy() { delete this; } -void VORDemodGUI::resetToDefaults() +void VORDemodMCGUI::resetToDefaults() { m_settings.resetToDefaults(); displaySettings(); applySettings(true); } -QByteArray VORDemodGUI::serialize() const +QByteArray VORDemodMCGUI::serialize() const { return m_settings.serialize(); } -bool VORDemodGUI::deserialize(const QByteArray& data) +bool VORDemodMCGUI::deserialize(const QByteArray& data) { if(m_settings.deserialize(data)) { displaySettings(); @@ -749,12 +749,12 @@ bool VORDemodGUI::deserialize(const QByteArray& data) } } -bool VORDemodGUI::handleMessage(const Message& message) +bool VORDemodMCGUI::handleMessage(const Message& message) { - if (VORDemod::MsgConfigureVORDemod::match(message)) + if (VORDemodMC::MsgConfigureVORDemod::match(message)) { - qDebug("VORDemodGUI::handleMessage: VORDemod::MsgConfigureVORDemod"); - const VORDemod::MsgConfigureVORDemod& cfg = (VORDemod::MsgConfigureVORDemod&) message; + qDebug("VORDemodMCGUI::handleMessage: VORDemodMC::MsgConfigureVORDemod"); + const VORDemodMC::MsgConfigureVORDemod& cfg = (VORDemodMC::MsgConfigureVORDemod&) message; m_settings = cfg.getSettings(); blockApplySettings(true); displaySettings(); @@ -769,9 +769,9 @@ bool VORDemodGUI::handleMessage(const Message& message) updateAbsoluteCenterFrequency(); return true; } - else if (VORDemodReport::MsgReportRadial::match(message)) + else if (VORDemodMCReport::MsgReportRadial::match(message)) { - VORDemodReport::MsgReportRadial& report = (VORDemodReport::MsgReportRadial&) message; + VORDemodMCReport::MsgReportRadial& report = (VORDemodMCReport::MsgReportRadial&) message; int subChannelId = report.getSubChannelId(); VORGUI *vorGUI = m_selectedVORs.value(subChannelId); @@ -806,9 +806,9 @@ bool VORDemodGUI::handleMessage(const Message& message) return true; } - else if (VORDemodReport::MsgReportFreqOffset::match(message)) + else if (VORDemodMCReport::MsgReportFreqOffset::match(message)) { - VORDemodReport::MsgReportFreqOffset& report = (VORDemodReport::MsgReportFreqOffset&) message; + VORDemodMCReport::MsgReportFreqOffset& report = (VORDemodMCReport::MsgReportFreqOffset&) message; int subChannelId = report.getSubChannelId(); VORGUI *vorGUI = m_selectedVORs.value(subChannelId); @@ -826,9 +826,9 @@ bool VORDemodGUI::handleMessage(const Message& message) else vorGUI->m_offsetItem->setForeground(QBrush(Qt::white)); } - else if (VORDemodReport::MsgReportIdent::match(message)) + else if (VORDemodMCReport::MsgReportIdent::match(message)) { - VORDemodReport::MsgReportIdent& report = (VORDemodReport::MsgReportIdent&) message; + VORDemodMCReport::MsgReportIdent& report = (VORDemodMCReport::MsgReportIdent&) message; int subChannelId = report.getSubChannelId(); VORGUI *vorGUI = m_selectedVORs.value(subChannelId); @@ -870,7 +870,7 @@ bool VORDemodGUI::handleMessage(const Message& message) return false; } -void VORDemodGUI::handleInputMessages() +void VORDemodMCGUI::handleInputMessages() { Message* message; @@ -883,43 +883,43 @@ void VORDemodGUI::handleInputMessages() } } -void VORDemodGUI::channelMarkerChangedByCursor() +void VORDemodMCGUI::channelMarkerChangedByCursor() { } -void VORDemodGUI::channelMarkerHighlightedByCursor() +void VORDemodMCGUI::channelMarkerHighlightedByCursor() { setHighlighted(m_channelMarker.getHighlighted()); } -void VORDemodGUI::on_thresh_valueChanged(int value) +void VORDemodMCGUI::on_thresh_valueChanged(int value) { ui->threshText->setText(QString("%1").arg(value / 10.0, 0, 'f', 1)); m_settings.m_identThreshold = value / 10.0; applySettings(); } -void VORDemodGUI::on_volume_valueChanged(int value) +void VORDemodMCGUI::on_volume_valueChanged(int value) { ui->volumeText->setText(QString("%1").arg(value / 10.0, 0, 'f', 1)); m_settings.m_volume = value / 10.0; applySettings(); } -void VORDemodGUI::on_squelch_valueChanged(int value) +void VORDemodMCGUI::on_squelch_valueChanged(int value) { ui->squelchText->setText(QString("%1 dB").arg(value)); m_settings.m_squelch = value; applySettings(); } -void VORDemodGUI::on_audioMute_toggled(bool checked) +void VORDemodMCGUI::on_audioMute_toggled(bool checked) { m_settings.m_audioMute = checked; applySettings(); } -qint64 VORDemodGUI::fileAgeInDays(QString filename) +qint64 VORDemodMCGUI::fileAgeInDays(QString filename) { QFile file(filename); if (file.exists()) @@ -933,7 +933,7 @@ qint64 VORDemodGUI::fileAgeInDays(QString filename) return -1; } -bool VORDemodGUI::confirmDownload(QString filename) +bool VORDemodMCGUI::confirmDownload(QString filename) { qint64 age = fileAgeInDays(filename); if ((age == -1) || (age > 100)) @@ -951,7 +951,7 @@ bool VORDemodGUI::confirmDownload(QString filename) } } -QString VORDemodGUI::getDataDir() +QString VORDemodMCGUI::getDataDir() { // Get directory to store app data in QStringList locations = QStandardPaths::standardLocations(QStandardPaths::AppDataLocation); @@ -959,7 +959,7 @@ QString VORDemodGUI::getDataDir() return locations[0]; } -QString VORDemodGUI::getOpenAIPVORDBFilename(int i) +QString VORDemodMCGUI::getOpenAIPVORDBFilename(int i) { if (countryCodes[i] != nullptr) return getDataDir() + "/" + countryCodes[i] + "_nav.aip"; @@ -967,7 +967,7 @@ QString VORDemodGUI::getOpenAIPVORDBFilename(int i) return ""; } -QString VORDemodGUI::getOpenAIPVORDBURL(int i) +QString VORDemodMCGUI::getOpenAIPVORDBURL(int i) { if (countryCodes[i] != nullptr) return QString(OPENAIP_NAVAIDS_URL).arg(countryCodes[i]); @@ -975,12 +975,12 @@ QString VORDemodGUI::getOpenAIPVORDBURL(int i) return ""; } -QString VORDemodGUI::getVORDBFilename() +QString VORDemodMCGUI::getVORDBFilename() { return getDataDir() + "/vorDatabase.csv"; } -void VORDemodGUI::updateDownloadProgress(qint64 bytesRead, qint64 totalBytes) +void VORDemodMCGUI::updateDownloadProgress(qint64 bytesRead, qint64 totalBytes) { if (m_progressDialog) { @@ -989,7 +989,7 @@ void VORDemodGUI::updateDownloadProgress(qint64 bytesRead, qint64 totalBytes) } } -void VORDemodGUI::downloadFinished(const QString& filename, bool success) +void VORDemodMCGUI::downloadFinished(const QString& filename, bool success) { bool closeDialog = true; if (success) @@ -1022,12 +1022,12 @@ void VORDemodGUI::downloadFinished(const QString& filename, bool success) } else { - qDebug() << "VORDemodGUI::downloadFinished: Unexpected filename: " << filename; + qDebug() << "VORDemodMCGUI::downloadFinished: Unexpected filename: " << filename; } } else { - qDebug() << "VORDemodGUI::downloadFinished: Failed: " << filename; + qDebug() << "VORDemodMCGUI::downloadFinished: Failed: " << filename; QMessageBox::warning(this, "Download failed", QString("Failed to download %1").arg(filename)); } if (closeDialog && m_progressDialog) @@ -1038,7 +1038,7 @@ void VORDemodGUI::downloadFinished(const QString& filename, bool success) } } -void VORDemodGUI::on_getOurAirportsVORDB_clicked(bool checked) +void VORDemodMCGUI::on_getOurAirportsVORDB_clicked(bool checked) { (void) checked; @@ -1060,7 +1060,7 @@ void VORDemodGUI::on_getOurAirportsVORDB_clicked(bool checked) } } -void VORDemodGUI::on_getOpenAIPVORDB_clicked(bool checked) +void VORDemodMCGUI::on_getOpenAIPVORDB_clicked(bool checked) { (void) checked; @@ -1085,7 +1085,7 @@ void VORDemodGUI::on_getOpenAIPVORDB_clicked(bool checked) } } -void VORDemodGUI::readNavAids() +void VORDemodMCGUI::readNavAids() { m_vors = new QHash(); for (int countryIndex = 0; countryCodes[countryIndex] != nullptr; countryIndex++) @@ -1095,14 +1095,14 @@ void VORDemodGUI::readNavAids() } } -void VORDemodGUI::on_magDecAdjust_clicked(bool checked) +void VORDemodMCGUI::on_magDecAdjust_clicked(bool checked) { m_settings.m_magDecAdjust = checked; m_vorModel.allVORUpdated(); applySettings(); } -void VORDemodGUI::onWidgetRolled(QWidget* widget, bool rollDown) +void VORDemodMCGUI::onWidgetRolled(QWidget* widget, bool rollDown) { (void) widget; (void) rollDown; @@ -1122,7 +1122,7 @@ void VORDemodGUI::onWidgetRolled(QWidget* widget, bool rollDown) applySettings(); } -void VORDemodGUI::onMenuDialogCalled(const QPoint &p) +void VORDemodMCGUI::onMenuDialogCalled(const QPoint &p) { if (m_contextMenuType == ContextMenuChannelSettings) { @@ -1169,9 +1169,9 @@ void VORDemodGUI::onMenuDialogCalled(const QPoint &p) resetContextMenuType(); } -VORDemodGUI::VORDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel, QWidget* parent) : +VORDemodMCGUI::VORDemodMCGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel, QWidget* parent) : ChannelGUI(parent), - ui(new Ui::VORDemodGUI), + ui(new Ui::VORDemodMCGUI), m_pluginAPI(pluginAPI), m_deviceUISet(deviceUISet), m_channelMarker(this), @@ -1198,9 +1198,9 @@ VORDemodGUI::VORDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Baseban m_muteIcon.addPixmap(QPixmap("://sound_on.png"), QIcon::Normal, QIcon::Off); connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &))); - connect(&m_dlm, &HttpDownloadManager::downloadComplete, this, &VORDemodGUI::downloadFinished); + connect(&m_dlm, &HttpDownloadManager::downloadComplete, this, &VORDemodMCGUI::downloadFinished); - m_vorDemod = reinterpret_cast(rxChannel); + m_vorDemod = reinterpret_cast(rxChannel); m_vorDemod->setMessageQueueToGUI(getInputMessageQueue()); connect(&MainCore::instance()->getMasterTimer(), SIGNAL(timeout()), this, SLOT(tick())); // 50 ms @@ -1295,26 +1295,26 @@ VORDemodGUI::VORDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Baseban applySettings(true); } -VORDemodGUI::~VORDemodGUI() +VORDemodMCGUI::~VORDemodMCGUI() { delete ui; } -void VORDemodGUI::blockApplySettings(bool block) +void VORDemodMCGUI::blockApplySettings(bool block) { m_doApplySettings = !block; } -void VORDemodGUI::applySettings(bool force) +void VORDemodMCGUI::applySettings(bool force) { if (m_doApplySettings) { - VORDemod::MsgConfigureVORDemod* message = VORDemod::MsgConfigureVORDemod::create( m_settings, force); + VORDemodMC::MsgConfigureVORDemod* message = VORDemodMC::MsgConfigureVORDemod::create( m_settings, force); m_vorDemod->getInputMessageQueue()->push(message); } } -void VORDemodGUI::displaySettings() +void VORDemodMCGUI::displaySettings() { m_channelMarker.blockSignals(true); m_channelMarker.setCenterFrequency(0); @@ -1358,21 +1358,21 @@ void VORDemodGUI::displaySettings() blockApplySettings(false); } -void VORDemodGUI::leaveEvent(QEvent* event) +void VORDemodMCGUI::leaveEvent(QEvent* event) { m_channelMarker.setHighlighted(false); ChannelGUI::leaveEvent(event); } -void VORDemodGUI::enterEvent(QEvent* event) +void VORDemodMCGUI::enterEvent(QEvent* event) { m_channelMarker.setHighlighted(true); ChannelGUI::enterEvent(event); } -void VORDemodGUI::audioSelect() +void VORDemodMCGUI::audioSelect() { - qDebug("VORDemodGUI::audioSelect"); + qDebug("VORDemodMCGUI::audioSelect"); AudioSelectDialog audioSelect(DSPEngine::instance()->getAudioDeviceManager(), m_settings.m_audioDeviceName); audioSelect.exec(); @@ -1383,7 +1383,7 @@ void VORDemodGUI::audioSelect() } } -void VORDemodGUI::tick() +void VORDemodMCGUI::tick() { double magsqAvg, magsqPeak; int nbMagsqSamples; @@ -1440,19 +1440,19 @@ void VORDemodGUI::tick() m_tickCount++; } -void VORDemodGUI::makeUIConnections() +void VORDemodMCGUI::makeUIConnections() { - QObject::connect(ui->audioMute, &QToolButton::toggled, this, &VORDemodGUI::on_audioMute_toggled); - QObject::connect(ui->thresh, &QDial::valueChanged, this, &VORDemodGUI::on_thresh_valueChanged); - QObject::connect(ui->volume, &QDial::valueChanged, this, &VORDemodGUI::on_volume_valueChanged); - QObject::connect(ui->squelch, &QDial::valueChanged, this, &VORDemodGUI::on_squelch_valueChanged); - QObject::connect(ui->audioMute, &QToolButton::toggled, this, &VORDemodGUI::on_audioMute_toggled); - QObject::connect(ui->getOurAirportsVORDB, &QPushButton::clicked, this, &VORDemodGUI::on_getOurAirportsVORDB_clicked); - QObject::connect(ui->getOpenAIPVORDB, &QPushButton::clicked, this, &VORDemodGUI::on_getOpenAIPVORDB_clicked); - QObject::connect(ui->magDecAdjust, &QPushButton::clicked, this, &VORDemodGUI::on_magDecAdjust_clicked); + QObject::connect(ui->audioMute, &QToolButton::toggled, this, &VORDemodMCGUI::on_audioMute_toggled); + QObject::connect(ui->thresh, &QDial::valueChanged, this, &VORDemodMCGUI::on_thresh_valueChanged); + QObject::connect(ui->volume, &QDial::valueChanged, this, &VORDemodMCGUI::on_volume_valueChanged); + QObject::connect(ui->squelch, &QDial::valueChanged, this, &VORDemodMCGUI::on_squelch_valueChanged); + QObject::connect(ui->audioMute, &QToolButton::toggled, this, &VORDemodMCGUI::on_audioMute_toggled); + QObject::connect(ui->getOurAirportsVORDB, &QPushButton::clicked, this, &VORDemodMCGUI::on_getOurAirportsVORDB_clicked); + QObject::connect(ui->getOpenAIPVORDB, &QPushButton::clicked, this, &VORDemodMCGUI::on_getOpenAIPVORDB_clicked); + QObject::connect(ui->magDecAdjust, &QPushButton::clicked, this, &VORDemodMCGUI::on_magDecAdjust_clicked); } -void VORDemodGUI::updateAbsoluteCenterFrequency() +void VORDemodMCGUI::updateAbsoluteCenterFrequency() { setStatusFrequency(m_deviceCenterFrequency); } diff --git a/plugins/channelrx/demodvor/vordemodgui.h b/plugins/channelrx/demodvor/vordemodgui.h index e1ee119da..9be477bf1 100644 --- a/plugins/channelrx/demodvor/vordemodgui.h +++ b/plugins/channelrx/demodvor/vordemodgui.h @@ -44,13 +44,13 @@ class PluginAPI; class DeviceUISet; class BasebandSampleSink; -class VORDemod; -class VORDemodGUI; +class VORDemodMC; +class VORDemodMCGUI; namespace Ui { - class VORDemodGUI; + class VORDemodMCGUI; } -class VORDemodGUI; +class VORDemodMCGUI; // Table items for each VOR class VORGUI : public QObject { @@ -58,7 +58,7 @@ class VORGUI : public QObject { public: NavAid *m_navAid; QVariantList m_coordinates; - VORDemodGUI *m_gui; + VORDemodMCGUI *m_gui; QTableWidgetItem *m_nameItem; QTableWidgetItem *m_frequencyItem; @@ -73,7 +73,7 @@ public: QWidget *m_muteItem; QToolButton *m_muteButton; - VORGUI(NavAid *navAid, VORDemodGUI *gui); + VORGUI(NavAid *navAid, VORDemodMCGUI *gui); private slots: void on_audioMute_toggled(bool checked); }; @@ -93,7 +93,7 @@ public: selectedRole = Qt::UserRole + 6 }; - VORModel(VORDemodGUI *gui) : + VORModel(VORDemodMCGUI *gui) : m_gui(gui), m_radialsVisible(true) { @@ -192,7 +192,7 @@ public: bool findIntersection(float &lat, float &lon); private: - VORDemodGUI *m_gui; + VORDemodMCGUI *m_gui; bool m_radialsVisible; QList m_vors; QList m_selected; @@ -200,11 +200,11 @@ private: QList m_vorGUIs; }; -class VORDemodGUI : public ChannelGUI { +class VORDemodMCGUI : public ChannelGUI { Q_OBJECT public: - static VORDemodGUI* create(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel); + static VORDemodMCGUI* create(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel); virtual void destroy(); void resetToDefaults(); @@ -233,16 +233,16 @@ private: friend class VORGUI; friend class VORModel; - Ui::VORDemodGUI* ui; + Ui::VORDemodMCGUI* ui; PluginAPI* m_pluginAPI; DeviceUISet* m_deviceUISet; ChannelMarker m_channelMarker; RollupState m_rollupState; - VORDemodSettings m_settings; + VORDemodMCSettings m_settings; qint64 m_deviceCenterFrequency; bool m_doApplySettings; - VORDemod* m_vorDemod; + VORDemodMC* m_vorDemod; bool m_squelchOpen; int m_basebandSampleRate; uint32_t m_tickCount; @@ -258,8 +258,8 @@ private: AzEl m_azEl; // Position of station QIcon m_muteIcon; - explicit VORDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel, QWidget* parent = 0); - virtual ~VORDemodGUI(); + explicit VORDemodMCGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel, QWidget* parent = 0); + virtual ~VORDemodMCGUI(); void blockApplySettings(bool block); void applySettings(bool force = false); diff --git a/plugins/channelrx/demodvor/vordemodgui.ui b/plugins/channelrx/demodvor/vordemodgui.ui index ba95565bd..264377ed6 100644 --- a/plugins/channelrx/demodvor/vordemodgui.ui +++ b/plugins/channelrx/demodvor/vordemodgui.ui @@ -1,6 +1,6 @@ - VORDemodGUI + VORDemodMCGUI @@ -32,7 +32,7 @@ Qt::StrongFocus - VOR Demodulator + VOR MC Demodulator diff --git a/plugins/channelrx/demodvor/vordemodplugin.cpp b/plugins/channelrx/demodvor/vordemodplugin.cpp index cca086822..c41a6464d 100644 --- a/plugins/channelrx/demodvor/vordemodplugin.cpp +++ b/plugins/channelrx/demodvor/vordemodplugin.cpp @@ -26,8 +26,8 @@ #include "vordemodwebapiadapter.h" #include "vordemodplugin.h" -const PluginDescriptor VORDemodPlugin::m_pluginDescriptor = { - VORDemod::m_channelId, +const PluginDescriptor VORDemodMCPlugin::m_pluginDescriptor = { + VORDemodMC::m_channelId, QStringLiteral("VOR Demodulator"), QStringLiteral("6.20.2"), QStringLiteral("(c) Jon Beniston, M7RCE"), @@ -36,29 +36,29 @@ const PluginDescriptor VORDemodPlugin::m_pluginDescriptor = { QStringLiteral("https://github.com/f4exb/sdrangel") }; -VORDemodPlugin::VORDemodPlugin(QObject* parent) : +VORDemodMCPlugin::VORDemodMCPlugin(QObject* parent) : QObject(parent), m_pluginAPI(0) { } -const PluginDescriptor& VORDemodPlugin::getPluginDescriptor() const +const PluginDescriptor& VORDemodMCPlugin::getPluginDescriptor() const { return m_pluginDescriptor; } -void VORDemodPlugin::initPlugin(PluginAPI* pluginAPI) +void VORDemodMCPlugin::initPlugin(PluginAPI* pluginAPI) { m_pluginAPI = pluginAPI; - m_pluginAPI->registerRxChannel(VORDemod::m_channelIdURI, VORDemod::m_channelId, this); + m_pluginAPI->registerRxChannel(VORDemodMC::m_channelIdURI, VORDemodMC::m_channelId, this); } -void VORDemodPlugin::createRxChannel(DeviceAPI *deviceAPI, BasebandSampleSink **bs, ChannelAPI **cs) const +void VORDemodMCPlugin::createRxChannel(DeviceAPI *deviceAPI, BasebandSampleSink **bs, ChannelAPI **cs) const { if (bs || cs) { - VORDemod *instance = new VORDemod(deviceAPI); + VORDemodMC *instance = new VORDemodMC(deviceAPI); if (bs) { *bs = instance; @@ -71,7 +71,7 @@ void VORDemodPlugin::createRxChannel(DeviceAPI *deviceAPI, BasebandSampleSink ** } #ifdef SERVER_MODE -ChannelGUI* VORDemodPlugin::createRxChannelGUI( +ChannelGUI* VORDemodMCPlugin::createRxChannelGUI( DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel) const { @@ -80,13 +80,13 @@ ChannelGUI* VORDemodPlugin::createRxChannelGUI( return 0; } #else -ChannelGUI* VORDemodPlugin::createRxChannelGUI(DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel) const +ChannelGUI* VORDemodMCPlugin::createRxChannelGUI(DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel) const { - return VORDemodGUI::create(m_pluginAPI, deviceUISet, rxChannel); + return VORDemodMCGUI::create(m_pluginAPI, deviceUISet, rxChannel); } #endif -ChannelWebAPIAdapter* VORDemodPlugin::createChannelWebAPIAdapter() const +ChannelWebAPIAdapter* VORDemodMCPlugin::createChannelWebAPIAdapter() const { return new VORDemodWebAPIAdapter(); } diff --git a/plugins/channelrx/demodvor/vordemodplugin.h b/plugins/channelrx/demodvor/vordemodplugin.h index a43c13c99..b3757ca5f 100644 --- a/plugins/channelrx/demodvor/vordemodplugin.h +++ b/plugins/channelrx/demodvor/vordemodplugin.h @@ -25,13 +25,13 @@ class DeviceUISet; class BasebandSampleSink; -class VORDemodPlugin : public QObject, PluginInterface { +class VORDemodMCPlugin : public QObject, PluginInterface { Q_OBJECT Q_INTERFACES(PluginInterface) Q_PLUGIN_METADATA(IID "sdrangel.channel.vordemod") public: - explicit VORDemodPlugin(QObject* parent = NULL); + explicit VORDemodMCPlugin(QObject* parent = NULL); const PluginDescriptor& getPluginDescriptor() const; void initPlugin(PluginAPI* pluginAPI); diff --git a/plugins/channelrx/demodvor/vordemodreport.cpp b/plugins/channelrx/demodvor/vordemodreport.cpp index 6ac03f99a..61c4ddba6 100644 --- a/plugins/channelrx/demodvor/vordemodreport.cpp +++ b/plugins/channelrx/demodvor/vordemodreport.cpp @@ -17,6 +17,6 @@ #include "vordemodreport.h" -MESSAGE_CLASS_DEFINITION(VORDemodReport::MsgReportFreqOffset, Message) -MESSAGE_CLASS_DEFINITION(VORDemodReport::MsgReportRadial, Message) -MESSAGE_CLASS_DEFINITION(VORDemodReport::MsgReportIdent, Message) +MESSAGE_CLASS_DEFINITION(VORDemodMCReport::MsgReportFreqOffset, Message) +MESSAGE_CLASS_DEFINITION(VORDemodMCReport::MsgReportRadial, Message) +MESSAGE_CLASS_DEFINITION(VORDemodMCReport::MsgReportIdent, Message) diff --git a/plugins/channelrx/demodvor/vordemodreport.h b/plugins/channelrx/demodvor/vordemodreport.h index 88a067d4e..58849c63c 100644 --- a/plugins/channelrx/demodvor/vordemodreport.h +++ b/plugins/channelrx/demodvor/vordemodreport.h @@ -23,7 +23,7 @@ #include "util/message.h" -class VORDemodReport : public QObject +class VORDemodMCReport : public QObject { Q_OBJECT public: @@ -109,8 +109,8 @@ public: }; public: - VORDemodReport() {} - ~VORDemodReport() {} + VORDemodMCReport() {} + ~VORDemodMCReport() {} }; #endif // INCLUDE_VORDEMODREPORT_H diff --git a/plugins/channelrx/demodvor/vordemodsettings.cpp b/plugins/channelrx/demodvor/vordemodsettings.cpp index 97f28663e..5bd4f2d79 100644 --- a/plugins/channelrx/demodvor/vordemodsettings.cpp +++ b/plugins/channelrx/demodvor/vordemodsettings.cpp @@ -23,14 +23,14 @@ #include "settings/serializable.h" #include "vordemodsettings.h" -VORDemodSettings::VORDemodSettings() : +VORDemodMCSettings::VORDemodMCSettings() : m_channelMarker(nullptr), m_rollupState(nullptr) { resetToDefaults(); } -void VORDemodSettings::resetToDefaults() +void VORDemodMCSettings::resetToDefaults() { m_squelch = -60.0; m_volume = 2.0; @@ -59,7 +59,7 @@ void VORDemodSettings::resetToDefaults() } } -QByteArray VORDemodSettings::serialize() const +QByteArray VORDemodMCSettings::serialize() const { SimpleSerializer s(1); s.writeS32(3, m_streamIndex); @@ -102,7 +102,7 @@ QByteArray VORDemodSettings::serialize() const return s.final(); } -bool VORDemodSettings::deserialize(const QByteArray& data) +bool VORDemodMCSettings::deserialize(const QByteArray& data) { SimpleDeserializer d(data); diff --git a/plugins/channelrx/demodvor/vordemodsettings.h b/plugins/channelrx/demodvor/vordemodsettings.h index af755e360..907a1fa7d 100644 --- a/plugins/channelrx/demodvor/vordemodsettings.h +++ b/plugins/channelrx/demodvor/vordemodsettings.h @@ -33,7 +33,7 @@ struct VORDemodSubChannelSettings { bool m_audioMute; //!< Mute the audio from this VOR }; -struct VORDemodSettings +struct VORDemodMCSettings { Real m_squelch; Real m_volume; @@ -63,7 +63,7 @@ struct VORDemodSettings QHash m_subChannelSettings; - VORDemodSettings(); + VORDemodMCSettings(); void resetToDefaults(); void setChannelMarker(Serializable *channelMarker) { m_channelMarker = channelMarker; } void setRollupState(Serializable *rollupState) { m_rollupState = rollupState; } diff --git a/plugins/channelrx/demodvor/vordemodsink.cpp b/plugins/channelrx/demodvor/vordemodsink.cpp index b7f046dad..8dfa47a03 100644 --- a/plugins/channelrx/demodvor/vordemodsink.cpp +++ b/plugins/channelrx/demodvor/vordemodsink.cpp @@ -30,7 +30,7 @@ #include "vordemodsink.h" #include "vordemodreport.h" -VORDemodSink::VORDemodSink(const VORDemodSettings& settings, int subChannel, +VORDemodMCSink::VORDemodMCSink(const VORDemodMCSettings& settings, int subChannel, MessageQueue *messageQueueToGUI) : m_channelFrequencyOffset(0), m_outOfBand(true), @@ -67,11 +67,11 @@ VORDemodSink::VORDemodSink(const VORDemodSettings& settings, int subChannel, } } -VORDemodSink::~VORDemodSink() +VORDemodMCSink::~VORDemodMCSink() { } -void VORDemodSink::feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end) +void VORDemodMCSink::feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end) { Complex ci; @@ -102,7 +102,7 @@ void VORDemodSink::feed(const SampleVector::const_iterator& begin, const SampleV } } -void VORDemodSink::processOneAudioSample(Complex &ci) +void VORDemodMCSink::processOneAudioSample(Complex &ci) { Real re = ci.real() / SDR_RX_SCALEF; Real im = ci.imag() / SDR_RX_SCALEF; @@ -167,7 +167,7 @@ void VORDemodSink::processOneAudioSample(Complex &ci) if (res != m_audioBufferFill) { - qDebug("VORDemodSink::processOneAudioSample: %u/%u audio samples written", res, m_audioBufferFill); + qDebug("VORDemodMCSink::processOneAudioSample: %u/%u audio samples written", res, m_audioBufferFill); m_audioFifo.clear(); } @@ -176,7 +176,7 @@ void VORDemodSink::processOneAudioSample(Complex &ci) } -void VORDemodSink::processOneSample(Complex &ci) +void VORDemodMCSink::processOneSample(Complex &ci) { Complex ca; @@ -251,7 +251,7 @@ void VORDemodSink::processOneSample(Complex &ci) if (getMessageQueueToGUI()) { - VORDemodReport::MsgReportRadial *msg = VORDemodReport::MsgReportRadial::create(m_subChannelId, phaseDifference, refMag, varMag); + VORDemodMCReport::MsgReportRadial *msg = VORDemodMCReport::MsgReportRadial::create(m_subChannelId, phaseDifference, refMag, varMag); getMessageQueueToGUI()->push(msg); } @@ -305,7 +305,7 @@ void VORDemodSink::processOneSample(Complex &ci) qDebug() << m_ident << " " << Morse::toString(m_ident); if (getMessageQueueToGUI()) { - VORDemodReport::MsgReportIdent *msg = VORDemodReport::MsgReportIdent::create(m_subChannelId, m_ident); + VORDemodMCReport::MsgReportIdent *msg = VORDemodMCReport::MsgReportIdent::create(m_subChannelId, m_ident); getMessageQueueToGUI()->push(msg); } m_ident = ""; @@ -344,7 +344,7 @@ void VORDemodSink::processOneSample(Complex &ci) qDebug() << m_ident << " " << Morse::toString(m_ident); if (getMessageQueueToGUI()) { - VORDemodReport::MsgReportIdent *msg = VORDemodReport::MsgReportIdent::create(m_subChannelId, m_ident); + VORDemodMCReport::MsgReportIdent *msg = VORDemodMCReport::MsgReportIdent::create(m_subChannelId, m_ident); getMessageQueueToGUI()->push(msg); } m_ident = ""; @@ -355,9 +355,9 @@ void VORDemodSink::processOneSample(Complex &ci) m_prevBit = bit; } -void VORDemodSink::applyChannelSettings(int channelSampleRate, int channelFrequencyOffset, bool force) +void VORDemodMCSink::applyChannelSettings(int channelSampleRate, int channelFrequencyOffset, bool force) { - qDebug() << "VORDemodSink::applyChannelSettings:" + qDebug() << "VORDemodMCSink::applyChannelSettings:" << " channelSampleRate: " << channelSampleRate << " channelFrequencyOffset: " << channelFrequencyOffset; @@ -395,9 +395,9 @@ void VORDemodSink::applyChannelSettings(int channelSampleRate, int channelFreque m_channelFrequencyOffset = channelFrequencyOffset; } -void VORDemodSink::applySettings(const VORDemodSettings& settings, bool force) +void VORDemodMCSink::applySettings(const VORDemodMCSettings& settings, bool force) { - qDebug() << "VORDemodSink::applySettings:" + qDebug() << "VORDemodMCSink::applySettings:" << " m_volume: " << settings.m_volume << " m_squelch: " << settings.m_squelch << " m_audioMute: " << settings.m_audioMute @@ -411,15 +411,15 @@ void VORDemodSink::applySettings(const VORDemodSettings& settings, bool force) m_settings = settings; } -void VORDemodSink::applyAudioSampleRate(int sampleRate) +void VORDemodMCSink::applyAudioSampleRate(int sampleRate) { if (sampleRate < 0) { - qWarning("VORDemodSink::applyAudioSampleRate: invalid sample rate: %d", sampleRate); + qWarning("VORDemodMCSink::applyAudioSampleRate: invalid sample rate: %d", sampleRate); return; } - qDebug("VORDemodSink::applyAudioSampleRate: sampleRate: %d m_channelSampleRate: %d", sampleRate, m_channelSampleRate); + qDebug("VORDemodMCSink::applyAudioSampleRate: sampleRate: %d m_channelSampleRate: %d", sampleRate, m_channelSampleRate); // (ICAO Annex 10 3.3.6.3) - Optional voice audio is 300Hz to 3kHz m_audioInterpolator.create(16, VORDEMOD_CHANNEL_SAMPLE_RATE, 3000.0f); diff --git a/plugins/channelrx/demodvor/vordemodsink.h b/plugins/channelrx/demodvor/vordemodsink.h index d6243b4e7..d4bf9fda6 100644 --- a/plugins/channelrx/demodvor/vordemodsink.h +++ b/plugins/channelrx/demodvor/vordemodsink.h @@ -42,16 +42,16 @@ // May as well make it a common audio rate, to possibly avoid decimation #define VORDEMOD_CHANNEL_SAMPLE_RATE 48000 -class VORDemodSink : public ChannelSampleSink { +class VORDemodMCSink : public ChannelSampleSink { public: - VORDemodSink(const VORDemodSettings& settings, int subChannel, + VORDemodMCSink(const VORDemodMCSettings& settings, int subChannel, MessageQueue *messageQueueToGUI); - ~VORDemodSink(); + ~VORDemodMCSink(); virtual void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end); void applyChannelSettings(int channelSampleRate, int channelFrequencyOffset, bool force = false); - void applySettings(const VORDemodSettings& settings, bool force = false); + void applySettings(const VORDemodMCSettings& settings, bool force = false); void setMessageQueueToGUI(MessageQueue *messageQueue) { m_messageQueueToGUI = messageQueue; } void applyAudioSampleRate(int sampleRate); @@ -95,7 +95,7 @@ private: double m_magsqPeak; }; - VORDemodSettings m_settings; + VORDemodMCSettings m_settings; int m_channelSampleRate; int m_audioSampleRate; diff --git a/plugins/channelrx/demodvor/vordemodwebapiadapter.cpp b/plugins/channelrx/demodvor/vordemodwebapiadapter.cpp index 79be603db..abe64e3de 100644 --- a/plugins/channelrx/demodvor/vordemodwebapiadapter.cpp +++ b/plugins/channelrx/demodvor/vordemodwebapiadapter.cpp @@ -33,7 +33,7 @@ int VORDemodWebAPIAdapter::webapiSettingsGet( (void) errorMessage; response.setVorDemodSettings(new SWGSDRangel::SWGVORDemodSettings()); response.getVorDemodSettings()->init(); - VORDemod::webapiFormatChannelSettings(response, m_settings); + VORDemodMC::webapiFormatChannelSettings(response, m_settings); return 200; } @@ -46,7 +46,7 @@ int VORDemodWebAPIAdapter::webapiSettingsPutPatch( { (void) force; (void) errorMessage; - VORDemod::webapiUpdateChannelSettings(m_settings, channelSettingsKeys, response); + VORDemodMC::webapiUpdateChannelSettings(m_settings, channelSettingsKeys, response); return 200; } diff --git a/plugins/channelrx/demodvor/vordemodwebapiadapter.h b/plugins/channelrx/demodvor/vordemodwebapiadapter.h index 94a1513d6..08b76113a 100644 --- a/plugins/channelrx/demodvor/vordemodwebapiadapter.h +++ b/plugins/channelrx/demodvor/vordemodwebapiadapter.h @@ -44,7 +44,7 @@ public: QString& errorMessage); private: - VORDemodSettings m_settings; + VORDemodMCSettings m_settings; }; #endif // INCLUDE_VORDEMOD_WEBAPIADAPTER_H From 046b79cab3c1eda3b7474bb4d60e8a633301cf3a Mon Sep 17 00:00:00 2001 From: f4exb Date: Sun, 1 May 2022 10:32:08 +0200 Subject: [PATCH 084/115] v7: rename vordemod files to vordemodmc files. Part of #1223 --- plugins/channelrx/CMakeLists.txt | 2 +- .../{demodvor => demodvormc}/CMakeLists.txt | 54 +++++++++--------- .../{demodvor => demodvormc}/icons.qrc | 0 .../icons/compass.png | Bin .../{demodvor => demodvormc}/icons/vor.png | Bin .../{demodvor => demodvormc}/map.qrc | 0 .../map/MapStation.qml | 0 .../{demodvor => demodvormc}/map/VOR-DME.png | Bin .../{demodvor => demodvormc}/map/VOR.png | Bin .../{demodvor => demodvormc}/map/VORTAC.png | Bin .../{demodvor => demodvormc}/map/antenna.png | Bin .../{demodvor => demodvormc}/map/map.qml | 0 .../{demodvor => demodvormc}/navaid.h | 0 .../{demodvor => demodvormc}/readme.md | 0 .../vordemodmc.cpp} | 2 +- .../vordemod.h => demodvormc/vordemodmc.h} | 4 +- .../vordemodmcbaseband.cpp} | 4 +- .../vordemodmcbaseband.h} | 2 +- .../vordemodmcgui.cpp} | 10 ++-- .../vordemodmcgui.h} | 2 +- .../vordemodmcgui.ui} | 0 .../vordemodmcplugin.cpp} | 10 ++-- .../vordemodmcplugin.h} | 0 .../vordemodmcreport.cpp} | 2 +- .../vordemodmcreport.h} | 0 .../vordemodmcsettings.cpp} | 2 +- .../vordemodmcsettings.h} | 0 .../vordemodmcsink.cpp} | 4 +- .../vordemodmcsink.h} | 2 +- .../vordemodmcwebapiadapter.cpp} | 12 ++-- .../vordemodmcwebapiadapter.h} | 8 +-- 31 files changed, 60 insertions(+), 60 deletions(-) rename plugins/channelrx/{demodvor => demodvormc}/CMakeLists.txt (56%) rename plugins/channelrx/{demodvor => demodvormc}/icons.qrc (100%) rename plugins/channelrx/{demodvor => demodvormc}/icons/compass.png (100%) rename plugins/channelrx/{demodvor => demodvormc}/icons/vor.png (100%) rename plugins/channelrx/{demodvor => demodvormc}/map.qrc (100%) rename plugins/channelrx/{demodvor => demodvormc}/map/MapStation.qml (100%) rename plugins/channelrx/{demodvor => demodvormc}/map/VOR-DME.png (100%) rename plugins/channelrx/{demodvor => demodvormc}/map/VOR.png (100%) rename plugins/channelrx/{demodvor => demodvormc}/map/VORTAC.png (100%) rename plugins/channelrx/{demodvor => demodvormc}/map/antenna.png (100%) rename plugins/channelrx/{demodvor => demodvormc}/map/map.qml (100%) rename plugins/channelrx/{demodvor => demodvormc}/navaid.h (100%) rename plugins/channelrx/{demodvor => demodvormc}/readme.md (100%) rename plugins/channelrx/{demodvor/vordemod.cpp => demodvormc/vordemodmc.cpp} (99%) rename plugins/channelrx/{demodvor/vordemod.h => demodvormc/vordemodmc.h} (99%) rename plugins/channelrx/{demodvor/vordemodbaseband.cpp => demodvormc/vordemodmcbaseband.cpp} (99%) rename plugins/channelrx/{demodvor/vordemodbaseband.h => demodvormc/vordemodmcbaseband.h} (99%) rename plugins/channelrx/{demodvor/vordemodgui.cpp => demodvormc/vordemodmcgui.cpp} (99%) rename plugins/channelrx/{demodvor/vordemodgui.h => demodvormc/vordemodmcgui.h} (99%) rename plugins/channelrx/{demodvor/vordemodgui.ui => demodvormc/vordemodmcgui.ui} (100%) rename plugins/channelrx/{demodvor/vordemodplugin.cpp => demodvormc/vordemodmcplugin.cpp} (95%) rename plugins/channelrx/{demodvor/vordemodplugin.h => demodvormc/vordemodmcplugin.h} (100%) rename plugins/channelrx/{demodvor/vordemodreport.cpp => demodvormc/vordemodmcreport.cpp} (98%) rename plugins/channelrx/{demodvor/vordemodreport.h => demodvormc/vordemodmcreport.h} (100%) rename plugins/channelrx/{demodvor/vordemodsettings.cpp => demodvormc/vordemodmcsettings.cpp} (99%) rename plugins/channelrx/{demodvor/vordemodsettings.h => demodvormc/vordemodmcsettings.h} (100%) rename plugins/channelrx/{demodvor/vordemodsink.cpp => demodvormc/vordemodmcsink.cpp} (99%) rename plugins/channelrx/{demodvor/vordemodsink.h => demodvormc/vordemodmcsink.h} (99%) rename plugins/channelrx/{demodvor/vordemodwebapiadapter.cpp => demodvormc/vordemodmcwebapiadapter.cpp} (88%) rename plugins/channelrx/{demodvor/vordemodwebapiadapter.h => demodvormc/vordemodmcwebapiadapter.h} (93%) diff --git a/plugins/channelrx/CMakeLists.txt b/plugins/channelrx/CMakeLists.txt index 7c8c51318..1153de171 100644 --- a/plugins/channelrx/CMakeLists.txt +++ b/plugins/channelrx/CMakeLists.txt @@ -2,7 +2,7 @@ project(demod) if (Qt5Quick_FOUND AND Qt5QuickWidgets_FOUND AND Qt5Positioning_FOUND) add_subdirectory(demodadsb) - add_subdirectory(demodvor) + add_subdirectory(demodvormc) endif() add_subdirectory(demodam) diff --git a/plugins/channelrx/demodvor/CMakeLists.txt b/plugins/channelrx/demodvormc/CMakeLists.txt similarity index 56% rename from plugins/channelrx/demodvor/CMakeLists.txt rename to plugins/channelrx/demodvormc/CMakeLists.txt index fdd035c46..46490ec17 100644 --- a/plugins/channelrx/demodvor/CMakeLists.txt +++ b/plugins/channelrx/demodvormc/CMakeLists.txt @@ -1,23 +1,23 @@ -project(vor) +project(vormc) -set(vor_SOURCES - vordemod.cpp - vordemodsettings.cpp - vordemodbaseband.cpp - vordemodsink.cpp - vordemodplugin.cpp - vordemodwebapiadapter.cpp - vordemodreport.cpp +set(vormc_SOURCES + vordemodmc.cpp + vordemodmcsettings.cpp + vordemodmcbaseband.cpp + vordemodmcsink.cpp + vordemodmcplugin.cpp + vordemodmcwebapiadapter.cpp + vordemodmcreport.cpp ) -set(vor_HEADERS - vordemod.h - vordemodsettings.h - vordemodbaseband.h - vordemodsink.h - vordemodplugin.h - vordemodwebapiadapter.h - vordemodreport.h +set(vormc_HEADERS + vordemodmc.h + vordemodmcsettings.h + vordemodmcbaseband.h + vordemodmcsink.h + vordemodmcplugin.h + vordemodmcwebapiadapter.h + vordemodmcreport.h ) @@ -26,33 +26,33 @@ include_directories( ) if(NOT SERVER_MODE) - set(vor_SOURCES - ${vor_SOURCES} - vordemodgui.cpp - vordemodgui.ui + set(vormc_SOURCES + ${vormc_SOURCES} + vordemodmcgui.cpp + vordemodmcgui.ui map.qrc icons.qrc ) - set(vor_HEADERS - ${vor_HEADERS} - vordemodgui.h + set(vormc_HEADERS + ${vormc_HEADERS} + vordemodmcgui.h navaid.h ../demodadsb/csv.h ) - set(TARGET_NAME demodvor) + set(TARGET_NAME demodvormc) set(TARGET_LIB "Qt5::Widgets" Qt5::Quick Qt5::QuickWidgets Qt5::Positioning) set(TARGET_LIB_GUI "sdrgui") set(INSTALL_FOLDER ${INSTALL_PLUGINS_DIR}) else() - set(TARGET_NAME demodvorsrv) + set(TARGET_NAME demodvormcsrv) set(TARGET_LIB "") set(TARGET_LIB_GUI "") set(INSTALL_FOLDER ${INSTALL_PLUGINSSRV_DIR}) endif() add_library(${TARGET_NAME} SHARED - ${vor_SOURCES} + ${vormc_SOURCES} ) target_link_libraries(${TARGET_NAME} diff --git a/plugins/channelrx/demodvor/icons.qrc b/plugins/channelrx/demodvormc/icons.qrc similarity index 100% rename from plugins/channelrx/demodvor/icons.qrc rename to plugins/channelrx/demodvormc/icons.qrc diff --git a/plugins/channelrx/demodvor/icons/compass.png b/plugins/channelrx/demodvormc/icons/compass.png similarity index 100% rename from plugins/channelrx/demodvor/icons/compass.png rename to plugins/channelrx/demodvormc/icons/compass.png diff --git a/plugins/channelrx/demodvor/icons/vor.png b/plugins/channelrx/demodvormc/icons/vor.png similarity index 100% rename from plugins/channelrx/demodvor/icons/vor.png rename to plugins/channelrx/demodvormc/icons/vor.png diff --git a/plugins/channelrx/demodvor/map.qrc b/plugins/channelrx/demodvormc/map.qrc similarity index 100% rename from plugins/channelrx/demodvor/map.qrc rename to plugins/channelrx/demodvormc/map.qrc diff --git a/plugins/channelrx/demodvor/map/MapStation.qml b/plugins/channelrx/demodvormc/map/MapStation.qml similarity index 100% rename from plugins/channelrx/demodvor/map/MapStation.qml rename to plugins/channelrx/demodvormc/map/MapStation.qml diff --git a/plugins/channelrx/demodvor/map/VOR-DME.png b/plugins/channelrx/demodvormc/map/VOR-DME.png similarity index 100% rename from plugins/channelrx/demodvor/map/VOR-DME.png rename to plugins/channelrx/demodvormc/map/VOR-DME.png diff --git a/plugins/channelrx/demodvor/map/VOR.png b/plugins/channelrx/demodvormc/map/VOR.png similarity index 100% rename from plugins/channelrx/demodvor/map/VOR.png rename to plugins/channelrx/demodvormc/map/VOR.png diff --git a/plugins/channelrx/demodvor/map/VORTAC.png b/plugins/channelrx/demodvormc/map/VORTAC.png similarity index 100% rename from plugins/channelrx/demodvor/map/VORTAC.png rename to plugins/channelrx/demodvormc/map/VORTAC.png diff --git a/plugins/channelrx/demodvor/map/antenna.png b/plugins/channelrx/demodvormc/map/antenna.png similarity index 100% rename from plugins/channelrx/demodvor/map/antenna.png rename to plugins/channelrx/demodvormc/map/antenna.png diff --git a/plugins/channelrx/demodvor/map/map.qml b/plugins/channelrx/demodvormc/map/map.qml similarity index 100% rename from plugins/channelrx/demodvor/map/map.qml rename to plugins/channelrx/demodvormc/map/map.qml diff --git a/plugins/channelrx/demodvor/navaid.h b/plugins/channelrx/demodvormc/navaid.h similarity index 100% rename from plugins/channelrx/demodvor/navaid.h rename to plugins/channelrx/demodvormc/navaid.h diff --git a/plugins/channelrx/demodvor/readme.md b/plugins/channelrx/demodvormc/readme.md similarity index 100% rename from plugins/channelrx/demodvor/readme.md rename to plugins/channelrx/demodvormc/readme.md diff --git a/plugins/channelrx/demodvor/vordemod.cpp b/plugins/channelrx/demodvormc/vordemodmc.cpp similarity index 99% rename from plugins/channelrx/demodvor/vordemod.cpp rename to plugins/channelrx/demodvormc/vordemodmc.cpp index fa4fd4b10..4623f56b9 100644 --- a/plugins/channelrx/demodvor/vordemod.cpp +++ b/plugins/channelrx/demodvormc/vordemodmc.cpp @@ -16,7 +16,7 @@ // along with this program. If not, see . // /////////////////////////////////////////////////////////////////////////////////// -#include "vordemod.h" +#include "vordemodmc.h" #include #include diff --git a/plugins/channelrx/demodvor/vordemod.h b/plugins/channelrx/demodvormc/vordemodmc.h similarity index 99% rename from plugins/channelrx/demodvor/vordemod.h rename to plugins/channelrx/demodvormc/vordemodmc.h index f6325b5d8..20799a1d4 100644 --- a/plugins/channelrx/demodvor/vordemod.h +++ b/plugins/channelrx/demodvormc/vordemodmc.h @@ -28,8 +28,8 @@ #include "channel/channelapi.h" #include "util/message.h" -#include "vordemodbaseband.h" -#include "vordemodsettings.h" +#include "vordemodmcbaseband.h" +#include "vordemodmcsettings.h" class QNetworkAccessManager; class QNetworkReply; diff --git a/plugins/channelrx/demodvor/vordemodbaseband.cpp b/plugins/channelrx/demodvormc/vordemodmcbaseband.cpp similarity index 99% rename from plugins/channelrx/demodvor/vordemodbaseband.cpp rename to plugins/channelrx/demodvormc/vordemodmcbaseband.cpp index 9cdebd04f..063ffa34f 100644 --- a/plugins/channelrx/demodvor/vordemodbaseband.cpp +++ b/plugins/channelrx/demodvormc/vordemodmcbaseband.cpp @@ -22,8 +22,8 @@ #include "dsp/dspcommands.h" #include "dsp/downchannelizer.h" -#include "vordemodbaseband.h" -#include "vordemodreport.h" +#include "vordemodmcbaseband.h" +#include "vordemodmcreport.h" MESSAGE_CLASS_DEFINITION(VORDemodMCBaseband::MsgConfigureVORDemodBaseband, Message) diff --git a/plugins/channelrx/demodvor/vordemodbaseband.h b/plugins/channelrx/demodvormc/vordemodmcbaseband.h similarity index 99% rename from plugins/channelrx/demodvor/vordemodbaseband.h rename to plugins/channelrx/demodvormc/vordemodmcbaseband.h index 13f1633b4..dac1f9ae3 100644 --- a/plugins/channelrx/demodvor/vordemodbaseband.h +++ b/plugins/channelrx/demodvormc/vordemodmcbaseband.h @@ -26,7 +26,7 @@ #include "util/message.h" #include "util/messagequeue.h" -#include "vordemodsink.h" +#include "vordemodmcsink.h" class DownChannelizer; diff --git a/plugins/channelrx/demodvor/vordemodgui.cpp b/plugins/channelrx/demodvormc/vordemodmcgui.cpp similarity index 99% rename from plugins/channelrx/demodvor/vordemodgui.cpp rename to plugins/channelrx/demodvormc/vordemodmcgui.cpp index 30ffacafd..c86912709 100644 --- a/plugins/channelrx/demodvor/vordemodgui.cpp +++ b/plugins/channelrx/demodvormc/vordemodmcgui.cpp @@ -27,12 +27,12 @@ #include #include -#include "vordemodgui.h" +#include "vordemodmcgui.h" #include "device/deviceuiset.h" #include "dsp/dspengine.h" #include "dsp/dspcommands.h" -#include "ui_vordemodgui.h" +#include "ui_vordemodmcgui.h" #include "plugin/pluginapi.h" #include "util/simpleserializer.h" #include "util/db.h" @@ -46,9 +46,9 @@ #include "channel/channelwebapiutils.h" #include "maincore.h" -#include "vordemod.h" -#include "vordemodreport.h" -#include "vordemodsink.h" +#include "vordemodmc.h" +#include "vordemodmcreport.h" +#include "vordemodmcsink.h" #define VOR_COL_NAME 0 #define VOR_COL_FREQUENCY 1 diff --git a/plugins/channelrx/demodvor/vordemodgui.h b/plugins/channelrx/demodvormc/vordemodmcgui.h similarity index 99% rename from plugins/channelrx/demodvor/vordemodgui.h rename to plugins/channelrx/demodvormc/vordemodmcgui.h index 9be477bf1..4a08a14a1 100644 --- a/plugins/channelrx/demodvor/vordemodgui.h +++ b/plugins/channelrx/demodvormc/vordemodmcgui.h @@ -38,7 +38,7 @@ #include "util/httpdownloadmanager.h" #include "util/azel.h" #include "settings/rollupstate.h" -#include "vordemodsettings.h" +#include "vordemodmcsettings.h" #include "navaid.h" class PluginAPI; diff --git a/plugins/channelrx/demodvor/vordemodgui.ui b/plugins/channelrx/demodvormc/vordemodmcgui.ui similarity index 100% rename from plugins/channelrx/demodvor/vordemodgui.ui rename to plugins/channelrx/demodvormc/vordemodmcgui.ui diff --git a/plugins/channelrx/demodvor/vordemodplugin.cpp b/plugins/channelrx/demodvormc/vordemodmcplugin.cpp similarity index 95% rename from plugins/channelrx/demodvor/vordemodplugin.cpp rename to plugins/channelrx/demodvormc/vordemodmcplugin.cpp index c41a6464d..7ba829772 100644 --- a/plugins/channelrx/demodvor/vordemodplugin.cpp +++ b/plugins/channelrx/demodvormc/vordemodmcplugin.cpp @@ -20,11 +20,11 @@ #include "plugin/pluginapi.h" #ifndef SERVER_MODE -#include "vordemodgui.h" +#include "vordemodmcgui.h" #endif -#include "vordemod.h" -#include "vordemodwebapiadapter.h" -#include "vordemodplugin.h" +#include "vordemodmc.h" +#include "vordemodmcwebapiadapter.h" +#include "vordemodmcplugin.h" const PluginDescriptor VORDemodMCPlugin::m_pluginDescriptor = { VORDemodMC::m_channelId, @@ -88,5 +88,5 @@ ChannelGUI* VORDemodMCPlugin::createRxChannelGUI(DeviceUISet *deviceUISet, Baseb ChannelWebAPIAdapter* VORDemodMCPlugin::createChannelWebAPIAdapter() const { - return new VORDemodWebAPIAdapter(); + return new VORDemodMCWebAPIAdapter(); } diff --git a/plugins/channelrx/demodvor/vordemodplugin.h b/plugins/channelrx/demodvormc/vordemodmcplugin.h similarity index 100% rename from plugins/channelrx/demodvor/vordemodplugin.h rename to plugins/channelrx/demodvormc/vordemodmcplugin.h diff --git a/plugins/channelrx/demodvor/vordemodreport.cpp b/plugins/channelrx/demodvormc/vordemodmcreport.cpp similarity index 98% rename from plugins/channelrx/demodvor/vordemodreport.cpp rename to plugins/channelrx/demodvormc/vordemodmcreport.cpp index 61c4ddba6..524713e44 100644 --- a/plugins/channelrx/demodvor/vordemodreport.cpp +++ b/plugins/channelrx/demodvormc/vordemodmcreport.cpp @@ -15,7 +15,7 @@ // along with this program. If not, see . // /////////////////////////////////////////////////////////////////////////////////// -#include "vordemodreport.h" +#include "vordemodmcreport.h" MESSAGE_CLASS_DEFINITION(VORDemodMCReport::MsgReportFreqOffset, Message) MESSAGE_CLASS_DEFINITION(VORDemodMCReport::MsgReportRadial, Message) diff --git a/plugins/channelrx/demodvor/vordemodreport.h b/plugins/channelrx/demodvormc/vordemodmcreport.h similarity index 100% rename from plugins/channelrx/demodvor/vordemodreport.h rename to plugins/channelrx/demodvormc/vordemodmcreport.h diff --git a/plugins/channelrx/demodvor/vordemodsettings.cpp b/plugins/channelrx/demodvormc/vordemodmcsettings.cpp similarity index 99% rename from plugins/channelrx/demodvor/vordemodsettings.cpp rename to plugins/channelrx/demodvormc/vordemodmcsettings.cpp index 5bd4f2d79..fbcfcf6ee 100644 --- a/plugins/channelrx/demodvor/vordemodsettings.cpp +++ b/plugins/channelrx/demodvormc/vordemodmcsettings.cpp @@ -21,7 +21,7 @@ #include "dsp/dspengine.h" #include "util/simpleserializer.h" #include "settings/serializable.h" -#include "vordemodsettings.h" +#include "vordemodmcsettings.h" VORDemodMCSettings::VORDemodMCSettings() : m_channelMarker(nullptr), diff --git a/plugins/channelrx/demodvor/vordemodsettings.h b/plugins/channelrx/demodvormc/vordemodmcsettings.h similarity index 100% rename from plugins/channelrx/demodvor/vordemodsettings.h rename to plugins/channelrx/demodvormc/vordemodmcsettings.h diff --git a/plugins/channelrx/demodvor/vordemodsink.cpp b/plugins/channelrx/demodvormc/vordemodmcsink.cpp similarity index 99% rename from plugins/channelrx/demodvor/vordemodsink.cpp rename to plugins/channelrx/demodvormc/vordemodmcsink.cpp index 8dfa47a03..ab84286c1 100644 --- a/plugins/channelrx/demodvor/vordemodsink.cpp +++ b/plugins/channelrx/demodvormc/vordemodmcsink.cpp @@ -27,8 +27,8 @@ #include "util/morse.h" #include "util/units.h" -#include "vordemodsink.h" -#include "vordemodreport.h" +#include "vordemodmcsink.h" +#include "vordemodmcreport.h" VORDemodMCSink::VORDemodMCSink(const VORDemodMCSettings& settings, int subChannel, MessageQueue *messageQueueToGUI) : diff --git a/plugins/channelrx/demodvor/vordemodsink.h b/plugins/channelrx/demodvormc/vordemodmcsink.h similarity index 99% rename from plugins/channelrx/demodvor/vordemodsink.h rename to plugins/channelrx/demodvormc/vordemodmcsink.h index d4bf9fda6..fb6228b87 100644 --- a/plugins/channelrx/demodvor/vordemodsink.h +++ b/plugins/channelrx/demodvormc/vordemodmcsink.h @@ -30,7 +30,7 @@ #include "util/doublebufferfifo.h" #include "util/messagequeue.h" -#include "vordemodsettings.h" +#include "vordemodmcsettings.h" #include diff --git a/plugins/channelrx/demodvor/vordemodwebapiadapter.cpp b/plugins/channelrx/demodvormc/vordemodmcwebapiadapter.cpp similarity index 88% rename from plugins/channelrx/demodvor/vordemodwebapiadapter.cpp rename to plugins/channelrx/demodvormc/vordemodmcwebapiadapter.cpp index abe64e3de..7352e9d16 100644 --- a/plugins/channelrx/demodvor/vordemodwebapiadapter.cpp +++ b/plugins/channelrx/demodvormc/vordemodmcwebapiadapter.cpp @@ -17,16 +17,16 @@ /////////////////////////////////////////////////////////////////////////////////// #include "SWGChannelSettings.h" -#include "vordemod.h" -#include "vordemodwebapiadapter.h" +#include "vordemodmc.h" +#include "vordemodmcwebapiadapter.h" -VORDemodWebAPIAdapter::VORDemodWebAPIAdapter() +VORDemodMCWebAPIAdapter::VORDemodMCWebAPIAdapter() {} -VORDemodWebAPIAdapter::~VORDemodWebAPIAdapter() +VORDemodMCWebAPIAdapter::~VORDemodMCWebAPIAdapter() {} -int VORDemodWebAPIAdapter::webapiSettingsGet( +int VORDemodMCWebAPIAdapter::webapiSettingsGet( SWGSDRangel::SWGChannelSettings& response, QString& errorMessage) { @@ -38,7 +38,7 @@ int VORDemodWebAPIAdapter::webapiSettingsGet( return 200; } -int VORDemodWebAPIAdapter::webapiSettingsPutPatch( +int VORDemodMCWebAPIAdapter::webapiSettingsPutPatch( bool force, const QStringList& channelSettingsKeys, SWGSDRangel::SWGChannelSettings& response, diff --git a/plugins/channelrx/demodvor/vordemodwebapiadapter.h b/plugins/channelrx/demodvormc/vordemodmcwebapiadapter.h similarity index 93% rename from plugins/channelrx/demodvor/vordemodwebapiadapter.h rename to plugins/channelrx/demodvormc/vordemodmcwebapiadapter.h index 08b76113a..f2cbb378c 100644 --- a/plugins/channelrx/demodvor/vordemodwebapiadapter.h +++ b/plugins/channelrx/demodvormc/vordemodmcwebapiadapter.h @@ -20,15 +20,15 @@ #define INCLUDE_VORDEMOD_WEBAPIADAPTER_H #include "channel/channelwebapiadapter.h" -#include "vordemodsettings.h" +#include "vordemodmcsettings.h" /** * Standalone API adapter only for the settings */ -class VORDemodWebAPIAdapter : public ChannelWebAPIAdapter { +class VORDemodMCWebAPIAdapter : public ChannelWebAPIAdapter { public: - VORDemodWebAPIAdapter(); - virtual ~VORDemodWebAPIAdapter(); + VORDemodMCWebAPIAdapter(); + virtual ~VORDemodMCWebAPIAdapter(); virtual QByteArray serialize() const { return m_settings.serialize(); } virtual bool deserialize(const QByteArray& data) { return m_settings.deserialize(data); } From c2bcd990b952db31974636f5d37e4201f8f84b8c Mon Sep 17 00:00:00 2001 From: f4exb Date: Sun, 1 May 2022 11:25:15 +0200 Subject: [PATCH 085/115] v7: VORDemod to VORDemodMC conversion in swagger. Part of #1223 --- plugins/channelrx/demodvormc/vordemodmc.cpp | 104 ++-- .../channelrx/demodvormc/vordemodmcplugin.h | 2 +- .../demodvormc/vordemodmcwebapiadapter.cpp | 4 +- .../demodvorsc/vordemodscwebapiadapter.cpp | 4 +- sdrbase/resources/webapi/doc/html2/index.html | 134 ++--- .../doc/swagger/include/ChannelReport.yaml | 4 +- .../doc/swagger/include/ChannelSettings.yaml | 4 +- .../webapi/doc/swagger/include/VORDemod.yaml | 8 +- .../doc/swagger/include/VORDemodMC.yaml | 55 +++ sdrbase/webapi/webapirequestmapper.cpp | 15 +- sdrbase/webapi/webapiutils.cpp | 2 + .../api/swagger/include/ChannelReport.yaml | 4 +- .../api/swagger/include/ChannelSettings.yaml | 4 +- .../{VORDemod.yaml => VORDemodMC.yaml} | 8 +- swagger/sdrangel/code/html2/index.html | 134 ++--- .../code/qt5/client/SWGChannelReport.cpp | 32 +- .../code/qt5/client/SWGChannelReport.h | 10 +- .../code/qt5/client/SWGChannelSettings.cpp | 32 +- .../code/qt5/client/SWGChannelSettings.h | 10 +- .../code/qt5/client/SWGModelFactory.h | 18 +- .../code/qt5/client/SWGVORDemodMCReport.cpp | 154 ++++++ .../code/qt5/client/SWGVORDemodMCReport.h | 70 +++ .../code/qt5/client/SWGVORDemodMCSettings.cpp | 463 ++++++++++++++++++ .../code/qt5/client/SWGVORDemodMCSettings.h | 151 ++++++ 24 files changed, 1164 insertions(+), 262 deletions(-) create mode 100644 sdrbase/resources/webapi/doc/swagger/include/VORDemodMC.yaml rename swagger/sdrangel/api/swagger/include/{VORDemod.yaml => VORDemodMC.yaml} (93%) create mode 100644 swagger/sdrangel/code/qt5/client/SWGVORDemodMCReport.cpp create mode 100644 swagger/sdrangel/code/qt5/client/SWGVORDemodMCReport.h create mode 100644 swagger/sdrangel/code/qt5/client/SWGVORDemodMCSettings.cpp create mode 100644 swagger/sdrangel/code/qt5/client/SWGVORDemodMCSettings.h diff --git a/plugins/channelrx/demodvormc/vordemodmc.cpp b/plugins/channelrx/demodvormc/vordemodmc.cpp index 4623f56b9..ff6f1a972 100644 --- a/plugins/channelrx/demodvormc/vordemodmc.cpp +++ b/plugins/channelrx/demodvormc/vordemodmc.cpp @@ -43,7 +43,7 @@ MESSAGE_CLASS_DEFINITION(VORDemodMC::MsgConfigureVORDemod, Message) -const char * const VORDemodMC::m_channelIdURI = "sdrangel.channel.vordemod"; +const char * const VORDemodMC::m_channelIdURI = "sdrangel.channel.vordemodmc"; const char * const VORDemodMC::m_channelId = "VORDemodMC"; VORDemodMC::VORDemodMC(DeviceAPI *deviceAPI) : @@ -277,8 +277,8 @@ int VORDemodMC::webapiSettingsGet( QString& errorMessage) { (void) errorMessage; - response.setVorDemodSettings(new SWGSDRangel::SWGVORDemodSettings()); - response.getVorDemodSettings()->init(); + response.setVorDemodMcSettings(new SWGSDRangel::SWGVORDemodMCSettings()); + response.getVorDemodMcSettings()->init(); webapiFormatChannelSettings(response, m_settings); return 200; } @@ -314,53 +314,53 @@ void VORDemodMC::webapiUpdateChannelSettings( SWGSDRangel::SWGChannelSettings& response) { if (channelSettingsKeys.contains("audioMute")) { - settings.m_audioMute = response.getVorDemodSettings()->getAudioMute() != 0; + settings.m_audioMute = response.getVorDemodMcSettings()->getAudioMute() != 0; } if (channelSettingsKeys.contains("rgbColor")) { - settings.m_rgbColor = response.getVorDemodSettings()->getRgbColor(); + settings.m_rgbColor = response.getVorDemodMcSettings()->getRgbColor(); } if (channelSettingsKeys.contains("squelch")) { - settings.m_squelch = response.getVorDemodSettings()->getSquelch(); + settings.m_squelch = response.getVorDemodMcSettings()->getSquelch(); } if (channelSettingsKeys.contains("title")) { - settings.m_title = *response.getVorDemodSettings()->getTitle(); + settings.m_title = *response.getVorDemodMcSettings()->getTitle(); } if (channelSettingsKeys.contains("volume")) { - settings.m_volume = response.getVorDemodSettings()->getVolume(); + settings.m_volume = response.getVorDemodMcSettings()->getVolume(); } if (channelSettingsKeys.contains("audioDeviceName")) { - settings.m_audioDeviceName = *response.getVorDemodSettings()->getAudioDeviceName(); + settings.m_audioDeviceName = *response.getVorDemodMcSettings()->getAudioDeviceName(); } if (channelSettingsKeys.contains("streamIndex")) { - settings.m_streamIndex = response.getVorDemodSettings()->getStreamIndex(); + settings.m_streamIndex = response.getVorDemodMcSettings()->getStreamIndex(); } if (channelSettingsKeys.contains("useReverseAPI")) { - settings.m_useReverseAPI = response.getVorDemodSettings()->getUseReverseApi() != 0; + settings.m_useReverseAPI = response.getVorDemodMcSettings()->getUseReverseApi() != 0; } if (channelSettingsKeys.contains("reverseAPIAddress")) { - settings.m_reverseAPIAddress = *response.getVorDemodSettings()->getReverseApiAddress(); + settings.m_reverseAPIAddress = *response.getVorDemodMcSettings()->getReverseApiAddress(); } if (channelSettingsKeys.contains("reverseAPIPort")) { - settings.m_reverseAPIPort = response.getVorDemodSettings()->getReverseApiPort(); + settings.m_reverseAPIPort = response.getVorDemodMcSettings()->getReverseApiPort(); } if (channelSettingsKeys.contains("reverseAPIDeviceIndex")) { - settings.m_reverseAPIDeviceIndex = response.getVorDemodSettings()->getReverseApiDeviceIndex(); + settings.m_reverseAPIDeviceIndex = response.getVorDemodMcSettings()->getReverseApiDeviceIndex(); } if (channelSettingsKeys.contains("reverseAPIChannelIndex")) { - settings.m_reverseAPIChannelIndex = response.getVorDemodSettings()->getReverseApiChannelIndex(); + settings.m_reverseAPIChannelIndex = response.getVorDemodMcSettings()->getReverseApiChannelIndex(); } if (channelSettingsKeys.contains("identThreshold")) { - settings.m_identThreshold = response.getVorDemodSettings()->getIdentThreshold(); + settings.m_identThreshold = response.getVorDemodMcSettings()->getIdentThreshold(); } if (channelSettingsKeys.contains("magDecAdjust")) { - settings.m_magDecAdjust = response.getVorDemodSettings()->getMagDecAdjust() != 0; + settings.m_magDecAdjust = response.getVorDemodMcSettings()->getMagDecAdjust() != 0; } if (settings.m_channelMarker && channelSettingsKeys.contains("channelMarker")) { - settings.m_channelMarker->updateFrom(channelSettingsKeys, response.getVorDemodSettings()->getChannelMarker()); + settings.m_channelMarker->updateFrom(channelSettingsKeys, response.getVorDemodMcSettings()->getChannelMarker()); } if (settings.m_rollupState && channelSettingsKeys.contains("rollupState")) { - settings.m_rollupState->updateFrom(channelSettingsKeys, response.getVorDemodSettings()->getRollupState()); + settings.m_rollupState->updateFrom(channelSettingsKeys, response.getVorDemodMcSettings()->getRollupState()); } } @@ -369,72 +369,72 @@ int VORDemodMC::webapiReportGet( QString& errorMessage) { (void) errorMessage; - response.setVorDemodReport(new SWGSDRangel::SWGVORDemodReport()); - response.getVorDemodReport()->init(); + response.setVorDemodMcReport(new SWGSDRangel::SWGVORDemodMCReport()); + response.getVorDemodMcReport()->init(); webapiFormatChannelReport(response); return 200; } void VORDemodMC::webapiFormatChannelSettings(SWGSDRangel::SWGChannelSettings& response, const VORDemodMCSettings& settings) { - response.getVorDemodSettings()->setAudioMute(settings.m_audioMute ? 1 : 0); - response.getVorDemodSettings()->setRgbColor(settings.m_rgbColor); - response.getVorDemodSettings()->setSquelch(settings.m_squelch); - response.getVorDemodSettings()->setVolume(settings.m_volume); + response.getVorDemodMcSettings()->setAudioMute(settings.m_audioMute ? 1 : 0); + response.getVorDemodMcSettings()->setRgbColor(settings.m_rgbColor); + response.getVorDemodMcSettings()->setSquelch(settings.m_squelch); + response.getVorDemodMcSettings()->setVolume(settings.m_volume); - if (response.getVorDemodSettings()->getTitle()) { - *response.getVorDemodSettings()->getTitle() = settings.m_title; + if (response.getVorDemodMcSettings()->getTitle()) { + *response.getVorDemodMcSettings()->getTitle() = settings.m_title; } else { - response.getVorDemodSettings()->setTitle(new QString(settings.m_title)); + response.getVorDemodMcSettings()->setTitle(new QString(settings.m_title)); } - if (response.getVorDemodSettings()->getAudioDeviceName()) { - *response.getVorDemodSettings()->getAudioDeviceName() = settings.m_audioDeviceName; + if (response.getVorDemodMcSettings()->getAudioDeviceName()) { + *response.getVorDemodMcSettings()->getAudioDeviceName() = settings.m_audioDeviceName; } else { - response.getVorDemodSettings()->setAudioDeviceName(new QString(settings.m_audioDeviceName)); + response.getVorDemodMcSettings()->setAudioDeviceName(new QString(settings.m_audioDeviceName)); } - response.getVorDemodSettings()->setStreamIndex(settings.m_streamIndex); - response.getVorDemodSettings()->setUseReverseApi(settings.m_useReverseAPI ? 1 : 0); + response.getVorDemodMcSettings()->setStreamIndex(settings.m_streamIndex); + response.getVorDemodMcSettings()->setUseReverseApi(settings.m_useReverseAPI ? 1 : 0); - if (response.getVorDemodSettings()->getReverseApiAddress()) { - *response.getVorDemodSettings()->getReverseApiAddress() = settings.m_reverseAPIAddress; + if (response.getVorDemodMcSettings()->getReverseApiAddress()) { + *response.getVorDemodMcSettings()->getReverseApiAddress() = settings.m_reverseAPIAddress; } else { - response.getVorDemodSettings()->setReverseApiAddress(new QString(settings.m_reverseAPIAddress)); + response.getVorDemodMcSettings()->setReverseApiAddress(new QString(settings.m_reverseAPIAddress)); } - response.getVorDemodSettings()->setReverseApiPort(settings.m_reverseAPIPort); - response.getVorDemodSettings()->setReverseApiDeviceIndex(settings.m_reverseAPIDeviceIndex); - response.getVorDemodSettings()->setReverseApiChannelIndex(settings.m_reverseAPIChannelIndex); + response.getVorDemodMcSettings()->setReverseApiPort(settings.m_reverseAPIPort); + response.getVorDemodMcSettings()->setReverseApiDeviceIndex(settings.m_reverseAPIDeviceIndex); + response.getVorDemodMcSettings()->setReverseApiChannelIndex(settings.m_reverseAPIChannelIndex); - response.getVorDemodSettings()->setIdentThreshold(settings.m_identThreshold); - response.getVorDemodSettings()->setMagDecAdjust(settings.m_magDecAdjust ? 1 : 0); + response.getVorDemodMcSettings()->setIdentThreshold(settings.m_identThreshold); + response.getVorDemodMcSettings()->setMagDecAdjust(settings.m_magDecAdjust ? 1 : 0); if (settings.m_channelMarker) { - if (response.getVorDemodSettings()->getChannelMarker()) + if (response.getVorDemodMcSettings()->getChannelMarker()) { - settings.m_channelMarker->formatTo(response.getVorDemodSettings()->getChannelMarker()); + settings.m_channelMarker->formatTo(response.getVorDemodMcSettings()->getChannelMarker()); } else { SWGSDRangel::SWGChannelMarker *swgChannelMarker = new SWGSDRangel::SWGChannelMarker(); settings.m_channelMarker->formatTo(swgChannelMarker); - response.getVorDemodSettings()->setChannelMarker(swgChannelMarker); + response.getVorDemodMcSettings()->setChannelMarker(swgChannelMarker); } } if (settings.m_rollupState) { - if (response.getVorDemodSettings()->getRollupState()) + if (response.getVorDemodMcSettings()->getRollupState()) { - settings.m_rollupState->formatTo(response.getVorDemodSettings()->getRollupState()); + settings.m_rollupState->formatTo(response.getVorDemodMcSettings()->getRollupState()); } else { SWGSDRangel::SWGRollupState *swgRollupState = new SWGSDRangel::SWGRollupState(); settings.m_rollupState->formatTo(swgRollupState); - response.getVorDemodSettings()->setRollupState(swgRollupState); + response.getVorDemodMcSettings()->setRollupState(swgRollupState); } } } @@ -445,9 +445,9 @@ void VORDemodMC::webapiFormatChannelReport(SWGSDRangel::SWGChannelReport& respon int nbMagsqSamples; getMagSqLevels(magsqAvg, magsqPeak, nbMagsqSamples); - response.getVorDemodReport()->setChannelPowerDb(CalcDb::dbPower(magsqAvg)); - response.getVorDemodReport()->setSquelch(m_basebandSink->getSquelchOpen() ? 1 : 0); - response.getVorDemodReport()->setAudioSampleRate(m_basebandSink->getAudioSampleRate()); + response.getVorDemodMcReport()->setChannelPowerDb(CalcDb::dbPower(magsqAvg)); + response.getVorDemodMcReport()->setSquelch(m_basebandSink->getSquelchOpen() ? 1 : 0); + response.getVorDemodMcReport()->setAudioSampleRate(m_basebandSink->getAudioSampleRate()); } void VORDemodMC::webapiReverseSendSettings(QList& channelSettingsKeys, const VORDemodMCSettings& settings, bool force) @@ -511,8 +511,8 @@ void VORDemodMC::webapiFormatChannelSettings( swgChannelSettings->setOriginatorChannelIndex(getIndexInDeviceSet()); swgChannelSettings->setOriginatorDeviceSetIndex(getDeviceSetIndex()); swgChannelSettings->setChannelType(new QString("VORDemodMC")); - swgChannelSettings->setVorDemodSettings(new SWGSDRangel::SWGVORDemodSettings()); - SWGSDRangel::SWGVORDemodSettings *swgVORDemodSettings = swgChannelSettings->getVorDemodSettings(); + swgChannelSettings->setVorDemodMcSettings(new SWGSDRangel::SWGVORDemodMCSettings()); + SWGSDRangel::SWGVORDemodMCSettings *swgVORDemodSettings = swgChannelSettings->getVorDemodMcSettings(); // transfer data that has been modified. When force is on transfer all data except reverse API data diff --git a/plugins/channelrx/demodvormc/vordemodmcplugin.h b/plugins/channelrx/demodvormc/vordemodmcplugin.h index b3757ca5f..49f3bf0b8 100644 --- a/plugins/channelrx/demodvormc/vordemodmcplugin.h +++ b/plugins/channelrx/demodvormc/vordemodmcplugin.h @@ -28,7 +28,7 @@ class BasebandSampleSink; class VORDemodMCPlugin : public QObject, PluginInterface { Q_OBJECT Q_INTERFACES(PluginInterface) - Q_PLUGIN_METADATA(IID "sdrangel.channel.vordemod") + Q_PLUGIN_METADATA(IID "sdrangel.channel.vordemodmc") public: explicit VORDemodMCPlugin(QObject* parent = NULL); diff --git a/plugins/channelrx/demodvormc/vordemodmcwebapiadapter.cpp b/plugins/channelrx/demodvormc/vordemodmcwebapiadapter.cpp index 7352e9d16..467ae651a 100644 --- a/plugins/channelrx/demodvormc/vordemodmcwebapiadapter.cpp +++ b/plugins/channelrx/demodvormc/vordemodmcwebapiadapter.cpp @@ -31,8 +31,8 @@ int VORDemodMCWebAPIAdapter::webapiSettingsGet( QString& errorMessage) { (void) errorMessage; - response.setVorDemodSettings(new SWGSDRangel::SWGVORDemodSettings()); - response.getVorDemodSettings()->init(); + response.setVorDemodMcSettings(new SWGSDRangel::SWGVORDemodMCSettings()); + response.getVorDemodMcSettings()->init(); VORDemodMC::webapiFormatChannelSettings(response, m_settings); return 200; diff --git a/plugins/channelrx/demodvorsc/vordemodscwebapiadapter.cpp b/plugins/channelrx/demodvorsc/vordemodscwebapiadapter.cpp index 67ab13f0d..e94aa6aa0 100644 --- a/plugins/channelrx/demodvorsc/vordemodscwebapiadapter.cpp +++ b/plugins/channelrx/demodvorsc/vordemodscwebapiadapter.cpp @@ -31,8 +31,8 @@ int VORDemodSCWebAPIAdapter::webapiSettingsGet( QString& errorMessage) { (void) errorMessage; - response.setVorDemodSettings(new SWGSDRangel::SWGVORDemodSettings()); - response.getVorDemodSettings()->init(); + response.setVorDemodScSettings(new SWGSDRangel::SWGVORDemodSCSettings()); + response.getVorDemodScSettings()->init(); VORDemodSC::webapiFormatChannelSettings(response, m_settings); return 200; diff --git a/sdrbase/resources/webapi/doc/html2/index.html b/sdrbase/resources/webapi/doc/html2/index.html index b7c351ce8..72adae4bc 100644 --- a/sdrbase/resources/webapi/doc/html2/index.html +++ b/sdrbase/resources/webapi/doc/html2/index.html @@ -3377,8 +3377,8 @@ margin-bottom: 20px; "UDPSinkReport" : { "$ref" : "#/definitions/UDPSinkReport" }, - "VORDemodReport" : { - "$ref" : "#/definitions/VORDemodReport" + "VORDemodMCReport" : { + "$ref" : "#/definitions/VORDemodMCReport" }, "VORDemodSCReport" : { "$ref" : "#/definitions/VORDemodSCReport" @@ -3538,8 +3538,8 @@ margin-bottom: 20px; "UDPSinkSettings" : { "$ref" : "#/definitions/UDPSinkSettings" }, - "VORDemodSettings" : { - "$ref" : "#/definitions/VORDemodSettings" + "VORDemodMCSettings" : { + "$ref" : "#/definitions/VORDemodMCSettings" }, "VORDemodSCSettings" : { "$ref" : "#/definitions/VORDemodSCSettings" @@ -13616,7 +13616,7 @@ margin-bottom: 20px; }, "description" : "USRP" }; - defs.VORDemodReport = { + defs.VORDemodMCReport = { "properties" : { "channelPowerDB" : { "type" : "number", @@ -13631,7 +13631,67 @@ margin-bottom: 20px; "type" : "integer" } }, - "description" : "VORDemod" + "description" : "VORDemodMC" +}; + defs.VORDemodMCSettings = { + "properties" : { + "squelch" : { + "type" : "number", + "format" : "float", + "description" : "power squelch threshold in decibels" + }, + "volume" : { + "type" : "number", + "format" : "float" + }, + "audioMute" : { + "type" : "integer" + }, + "rgbColor" : { + "type" : "integer" + }, + "title" : { + "type" : "string" + }, + "audioDeviceName" : { + "type" : "string" + }, + "streamIndex" : { + "type" : "integer", + "description" : "MIMO channel. Not relevant when connected to SI (single Rx)." + }, + "useReverseAPI" : { + "type" : "integer", + "description" : "Synchronize with reverse API (1 for yes, 0 for no)" + }, + "reverseAPIAddress" : { + "type" : "string" + }, + "reverseAPIPort" : { + "type" : "integer" + }, + "reverseAPIDeviceIndex" : { + "type" : "integer" + }, + "reverseAPIChannelIndex" : { + "type" : "integer" + }, + "identThreshold" : { + "type" : "integer", + "description" : "Morse code ident threshold (linear SNR)" + }, + "magDecAdjust" : { + "type" : "integer", + "description" : "Adjust radial lines on map for magnetic declination of VOR" + }, + "channelMarker" : { + "$ref" : "#/definitions/ChannelMarker" + }, + "rollupState" : { + "$ref" : "#/definitions/RollupState" + } + }, + "description" : "VORDemodMC" }; defs.VORDemodSCReport = { "properties" : { @@ -13752,66 +13812,6 @@ margin-bottom: 20px; } }, "description" : "VORDemodSC" -}; - defs.VORDemodSettings = { - "properties" : { - "squelch" : { - "type" : "number", - "format" : "float", - "description" : "power squelch threshold in decibels" - }, - "volume" : { - "type" : "number", - "format" : "float" - }, - "audioMute" : { - "type" : "integer" - }, - "rgbColor" : { - "type" : "integer" - }, - "title" : { - "type" : "string" - }, - "audioDeviceName" : { - "type" : "string" - }, - "streamIndex" : { - "type" : "integer", - "description" : "MIMO channel. Not relevant when connected to SI (single Rx)." - }, - "useReverseAPI" : { - "type" : "integer", - "description" : "Synchronize with reverse API (1 for yes, 0 for no)" - }, - "reverseAPIAddress" : { - "type" : "string" - }, - "reverseAPIPort" : { - "type" : "integer" - }, - "reverseAPIDeviceIndex" : { - "type" : "integer" - }, - "reverseAPIChannelIndex" : { - "type" : "integer" - }, - "identThreshold" : { - "type" : "integer", - "description" : "Morse code ident threshold (linear SNR)" - }, - "magDecAdjust" : { - "type" : "integer", - "description" : "Adjust radial lines on map for magnetic declination of VOR" - }, - "channelMarker" : { - "$ref" : "#/definitions/ChannelMarker" - }, - "rollupState" : { - "$ref" : "#/definitions/RollupState" - } - }, - "description" : "VORDemod" }; defs.VORLocalizerActions = { "properties" : { @@ -52552,7 +52552,7 @@ except ApiException as e:
    - Generated 2022-03-05T04:59:37.901+01:00 + Generated 2022-05-01T10:49:23.866+02:00
    diff --git a/sdrbase/resources/webapi/doc/swagger/include/ChannelReport.yaml b/sdrbase/resources/webapi/doc/swagger/include/ChannelReport.yaml index d2da6d48a..37d2961e2 100644 --- a/sdrbase/resources/webapi/doc/swagger/include/ChannelReport.yaml +++ b/sdrbase/resources/webapi/doc/swagger/include/ChannelReport.yaml @@ -77,8 +77,8 @@ ChannelReport: $ref: "/doc/swagger/include/UDPSource.yaml#/UDPSourceReport" UDPSinkReport: $ref: "/doc/swagger/include/UDPSink.yaml#/UDPSinkReport" - VORDemodReport: - $ref: "/doc/swagger/include/VORDemod.yaml#/VORDemodReport" + VORDemodMCReport: + $ref: "/doc/swagger/include/VORDemodMC.yaml#/VORDemodMCReport" VORDemodSCReport: $ref: "/doc/swagger/include/VORDemodSC.yaml#/VORDemodSCReport" WFMDemodReport: diff --git a/sdrbase/resources/webapi/doc/swagger/include/ChannelSettings.yaml b/sdrbase/resources/webapi/doc/swagger/include/ChannelSettings.yaml index 0b55f606b..ddd206777 100644 --- a/sdrbase/resources/webapi/doc/swagger/include/ChannelSettings.yaml +++ b/sdrbase/resources/webapi/doc/swagger/include/ChannelSettings.yaml @@ -101,8 +101,8 @@ ChannelSettings: $ref: "/doc/swagger/include/UDPSource.yaml#/UDPSourceSettings" UDPSinkSettings: $ref: "/doc/swagger/include/UDPSink.yaml#/UDPSinkSettings" - VORDemodSettings: - $ref: "/doc/swagger/include/VORDemod.yaml#/VORDemodSettings" + VORDemodMCSettings: + $ref: "/doc/swagger/include/VORDemodMC.yaml#/VORDemodMCSettings" VORDemodSCSettings: $ref: "/doc/swagger/include/VORDemodSC.yaml#/VORDemodSCSettings" WFMDemodSettings: diff --git a/sdrbase/resources/webapi/doc/swagger/include/VORDemod.yaml b/sdrbase/resources/webapi/doc/swagger/include/VORDemod.yaml index f5f7e386d..6dbc27998 100644 --- a/sdrbase/resources/webapi/doc/swagger/include/VORDemod.yaml +++ b/sdrbase/resources/webapi/doc/swagger/include/VORDemod.yaml @@ -1,5 +1,5 @@ -VORDemodSettings: - description: VORDemod +VORDemodMCSettings: + description: VORDemodMC properties: squelch: description: power squelch threshold in decibels @@ -41,8 +41,8 @@ VORDemodSettings: rollupState: $ref: "/doc/swagger/include/RollupState.yaml#/RollupState" -VORDemodReport: - description: VORDemod +VORDemodMCReport: + description: VORDemodMC properties: channelPowerDB: description: power received in channel (dB) diff --git a/sdrbase/resources/webapi/doc/swagger/include/VORDemodMC.yaml b/sdrbase/resources/webapi/doc/swagger/include/VORDemodMC.yaml new file mode 100644 index 000000000..6dbc27998 --- /dev/null +++ b/sdrbase/resources/webapi/doc/swagger/include/VORDemodMC.yaml @@ -0,0 +1,55 @@ +VORDemodMCSettings: + description: VORDemodMC + properties: + squelch: + description: power squelch threshold in decibels + type: number + format: float + volume: + type: number + format: float + audioMute: + type: integer + rgbColor: + type: integer + title: + type: string + audioDeviceName: + type: string + streamIndex: + description: MIMO channel. Not relevant when connected to SI (single Rx). + type: integer + useReverseAPI: + description: Synchronize with reverse API (1 for yes, 0 for no) + type: integer + reverseAPIAddress: + type: string + reverseAPIPort: + type: integer + reverseAPIDeviceIndex: + type: integer + reverseAPIChannelIndex: + type: integer + identThreshold: + description: Morse code ident threshold (linear SNR) + type: integer + magDecAdjust: + description: Adjust radial lines on map for magnetic declination of VOR + type: integer + channelMarker: + $ref: "/doc/swagger/include/ChannelMarker.yaml#/ChannelMarker" + rollupState: + $ref: "/doc/swagger/include/RollupState.yaml#/RollupState" + +VORDemodMCReport: + description: VORDemodMC + properties: + channelPowerDB: + description: power received in channel (dB) + type: number + format: float + squelch: + description: squelch status (1 if open else 0) + type: integer + audioSampleRate: + type: integer diff --git a/sdrbase/webapi/webapirequestmapper.cpp b/sdrbase/webapi/webapirequestmapper.cpp index 516b455c2..206f09281 100644 --- a/sdrbase/webapi/webapirequestmapper.cpp +++ b/sdrbase/webapi/webapirequestmapper.cpp @@ -4357,10 +4357,15 @@ bool WebAPIRequestMapper::getChannelSettings( channelSettings->setUdpSinkSettings(new SWGSDRangel::SWGUDPSinkSettings()); channelSettings->getUdpSinkSettings()->fromJsonObject(settingsJsonObject); } + else if (channelSettingsKey == "VORDemodMCSettings") + { + channelSettings->setVorDemodMcSettings(new SWGSDRangel::SWGVORDemodMCSettings()); + channelSettings->getVorDemodMcSettings()->fromJsonObject(settingsJsonObject); + } else if (channelSettingsKey == "VORDemodSettings") { - channelSettings->setVorDemodSettings(new SWGSDRangel::SWGVORDemodSettings()); - channelSettings->getVorDemodSettings()->fromJsonObject(settingsJsonObject); + channelSettings->setVorDemodScSettings(new SWGSDRangel::SWGVORDemodSCSettings()); + channelSettings->getVorDemodScSettings()->fromJsonObject(settingsJsonObject); } else if (channelSettingsKey == "WFMDemodSettings") { @@ -5077,7 +5082,8 @@ void WebAPIRequestMapper::resetChannelSettings(SWGSDRangel::SWGChannelSettings& channelSettings.setSsbModSettings(nullptr); channelSettings.setUdpSourceSettings(nullptr); channelSettings.setUdpSinkSettings(nullptr); - channelSettings.setVorDemodSettings(nullptr); + channelSettings.setVorDemodMcSettings(nullptr); + channelSettings.setVorDemodScSettings(nullptr); channelSettings.setWfmDemodSettings(nullptr); channelSettings.setWfmModSettings(nullptr); } @@ -5108,7 +5114,8 @@ void WebAPIRequestMapper::resetChannelReport(SWGSDRangel::SWGChannelReport& chan channelReport.setSsbModReport(nullptr); channelReport.setUdpSourceReport(nullptr); channelReport.setUdpSinkReport(nullptr); - channelReport.setVorDemodReport(nullptr); + channelReport.setVorDemodMcReport(nullptr); + channelReport.setVorDemodScReport(nullptr); channelReport.setWfmDemodReport(nullptr); channelReport.setWfmModReport(nullptr); } diff --git a/sdrbase/webapi/webapiutils.cpp b/sdrbase/webapi/webapiutils.cpp index 8a11a53d0..21398951c 100644 --- a/sdrbase/webapi/webapiutils.cpp +++ b/sdrbase/webapi/webapiutils.cpp @@ -69,6 +69,7 @@ const QMap WebAPIUtils::m_channelURIToSettingsKey = { {"sdrangel.channeltx.udpsink", "UDPSinkSettings"}, // remap {"sdrangel.channel.udpsink", "UDPSinkSettings"}, {"sdrangel.channel.udpsrc", "UDPSourceSettings"}, // remap + {"sdrangel.channel.vordemodmc", "VORDemodMCSettings"}, {"sdrangel.channel.vordemod", "VORDemodSettings"}, {"sdrangel.channel.wfmdemod", "WFMDemodSettings"}, {"de.maintech.sdrangelove.channel.wfm", "WFMDemodSettings"}, // remap @@ -167,6 +168,7 @@ const QMap WebAPIUtils::m_channelTypeToSettingsKey = { {"SSBDemod", "SSBDemodSettings"}, {"UDPSink", "UDPSinkSettings"}, {"UDPSource", "UDPSourceSettings"}, + {"VORDemodMC", "VORDemodMCSettings"}, {"VORDemod", "VORDemodSettings"}, {"WFMDemod", "WFMDemodSettings"}, {"WFMMod", "WFMModSettings"}, diff --git a/swagger/sdrangel/api/swagger/include/ChannelReport.yaml b/swagger/sdrangel/api/swagger/include/ChannelReport.yaml index d3650cede..a8585f123 100644 --- a/swagger/sdrangel/api/swagger/include/ChannelReport.yaml +++ b/swagger/sdrangel/api/swagger/include/ChannelReport.yaml @@ -77,8 +77,8 @@ ChannelReport: $ref: "http://swgserver:8081/api/swagger/include/UDPSource.yaml#/UDPSourceReport" UDPSinkReport: $ref: "http://swgserver:8081/api/swagger/include/UDPSink.yaml#/UDPSinkReport" - VORDemodReport: - $ref: "http://swgserver:8081/api/swagger/include/VORDemod.yaml#/VORDemodReport" + VORDemodMCReport: + $ref: "http://swgserver:8081/api/swagger/include/VORDemodMC.yaml#/VORDemodMCReport" VORDemodSCReport: $ref: "http://swgserver:8081/api/swagger/include/VORDemodSC.yaml#/VORDemodSCReport" WFMDemodReport: diff --git a/swagger/sdrangel/api/swagger/include/ChannelSettings.yaml b/swagger/sdrangel/api/swagger/include/ChannelSettings.yaml index 0ec7f3e1a..59fd529a4 100644 --- a/swagger/sdrangel/api/swagger/include/ChannelSettings.yaml +++ b/swagger/sdrangel/api/swagger/include/ChannelSettings.yaml @@ -101,8 +101,8 @@ ChannelSettings: $ref: "http://swgserver:8081/api/swagger/include/UDPSource.yaml#/UDPSourceSettings" UDPSinkSettings: $ref: "http://swgserver:8081/api/swagger/include/UDPSink.yaml#/UDPSinkSettings" - VORDemodSettings: - $ref: "http://swgserver:8081/api/swagger/include/VORDemod.yaml#/VORDemodSettings" + VORDemodMCSettings: + $ref: "http://swgserver:8081/api/swagger/include/VORDemodMC.yaml#/VORDemodMCSettings" VORDemodSCSettings: $ref: "http://swgserver:8081/api/swagger/include/VORDemodSC.yaml#/VORDemodSCSettings" WFMDemodSettings: diff --git a/swagger/sdrangel/api/swagger/include/VORDemod.yaml b/swagger/sdrangel/api/swagger/include/VORDemodMC.yaml similarity index 93% rename from swagger/sdrangel/api/swagger/include/VORDemod.yaml rename to swagger/sdrangel/api/swagger/include/VORDemodMC.yaml index 83b01cbbf..02da32e46 100644 --- a/swagger/sdrangel/api/swagger/include/VORDemod.yaml +++ b/swagger/sdrangel/api/swagger/include/VORDemodMC.yaml @@ -1,5 +1,5 @@ -VORDemodSettings: - description: VORDemod +VORDemodMCSettings: + description: VORDemodMC properties: squelch: description: power squelch threshold in decibels @@ -41,8 +41,8 @@ VORDemodSettings: rollupState: $ref: "http://swgserver:8081/api/swagger/include/RollupState.yaml#/RollupState" -VORDemodReport: - description: VORDemod +VORDemodMCReport: + description: VORDemodMC properties: channelPowerDB: description: power received in channel (dB) diff --git a/swagger/sdrangel/code/html2/index.html b/swagger/sdrangel/code/html2/index.html index b7c351ce8..72adae4bc 100644 --- a/swagger/sdrangel/code/html2/index.html +++ b/swagger/sdrangel/code/html2/index.html @@ -3377,8 +3377,8 @@ margin-bottom: 20px; "UDPSinkReport" : { "$ref" : "#/definitions/UDPSinkReport" }, - "VORDemodReport" : { - "$ref" : "#/definitions/VORDemodReport" + "VORDemodMCReport" : { + "$ref" : "#/definitions/VORDemodMCReport" }, "VORDemodSCReport" : { "$ref" : "#/definitions/VORDemodSCReport" @@ -3538,8 +3538,8 @@ margin-bottom: 20px; "UDPSinkSettings" : { "$ref" : "#/definitions/UDPSinkSettings" }, - "VORDemodSettings" : { - "$ref" : "#/definitions/VORDemodSettings" + "VORDemodMCSettings" : { + "$ref" : "#/definitions/VORDemodMCSettings" }, "VORDemodSCSettings" : { "$ref" : "#/definitions/VORDemodSCSettings" @@ -13616,7 +13616,7 @@ margin-bottom: 20px; }, "description" : "USRP" }; - defs.VORDemodReport = { + defs.VORDemodMCReport = { "properties" : { "channelPowerDB" : { "type" : "number", @@ -13631,7 +13631,67 @@ margin-bottom: 20px; "type" : "integer" } }, - "description" : "VORDemod" + "description" : "VORDemodMC" +}; + defs.VORDemodMCSettings = { + "properties" : { + "squelch" : { + "type" : "number", + "format" : "float", + "description" : "power squelch threshold in decibels" + }, + "volume" : { + "type" : "number", + "format" : "float" + }, + "audioMute" : { + "type" : "integer" + }, + "rgbColor" : { + "type" : "integer" + }, + "title" : { + "type" : "string" + }, + "audioDeviceName" : { + "type" : "string" + }, + "streamIndex" : { + "type" : "integer", + "description" : "MIMO channel. Not relevant when connected to SI (single Rx)." + }, + "useReverseAPI" : { + "type" : "integer", + "description" : "Synchronize with reverse API (1 for yes, 0 for no)" + }, + "reverseAPIAddress" : { + "type" : "string" + }, + "reverseAPIPort" : { + "type" : "integer" + }, + "reverseAPIDeviceIndex" : { + "type" : "integer" + }, + "reverseAPIChannelIndex" : { + "type" : "integer" + }, + "identThreshold" : { + "type" : "integer", + "description" : "Morse code ident threshold (linear SNR)" + }, + "magDecAdjust" : { + "type" : "integer", + "description" : "Adjust radial lines on map for magnetic declination of VOR" + }, + "channelMarker" : { + "$ref" : "#/definitions/ChannelMarker" + }, + "rollupState" : { + "$ref" : "#/definitions/RollupState" + } + }, + "description" : "VORDemodMC" }; defs.VORDemodSCReport = { "properties" : { @@ -13752,66 +13812,6 @@ margin-bottom: 20px; } }, "description" : "VORDemodSC" -}; - defs.VORDemodSettings = { - "properties" : { - "squelch" : { - "type" : "number", - "format" : "float", - "description" : "power squelch threshold in decibels" - }, - "volume" : { - "type" : "number", - "format" : "float" - }, - "audioMute" : { - "type" : "integer" - }, - "rgbColor" : { - "type" : "integer" - }, - "title" : { - "type" : "string" - }, - "audioDeviceName" : { - "type" : "string" - }, - "streamIndex" : { - "type" : "integer", - "description" : "MIMO channel. Not relevant when connected to SI (single Rx)." - }, - "useReverseAPI" : { - "type" : "integer", - "description" : "Synchronize with reverse API (1 for yes, 0 for no)" - }, - "reverseAPIAddress" : { - "type" : "string" - }, - "reverseAPIPort" : { - "type" : "integer" - }, - "reverseAPIDeviceIndex" : { - "type" : "integer" - }, - "reverseAPIChannelIndex" : { - "type" : "integer" - }, - "identThreshold" : { - "type" : "integer", - "description" : "Morse code ident threshold (linear SNR)" - }, - "magDecAdjust" : { - "type" : "integer", - "description" : "Adjust radial lines on map for magnetic declination of VOR" - }, - "channelMarker" : { - "$ref" : "#/definitions/ChannelMarker" - }, - "rollupState" : { - "$ref" : "#/definitions/RollupState" - } - }, - "description" : "VORDemod" }; defs.VORLocalizerActions = { "properties" : { @@ -52552,7 +52552,7 @@ except ApiException as e:
    - Generated 2022-03-05T04:59:37.901+01:00 + Generated 2022-05-01T10:49:23.866+02:00
    diff --git a/swagger/sdrangel/code/qt5/client/SWGChannelReport.cpp b/swagger/sdrangel/code/qt5/client/SWGChannelReport.cpp index 265179eff..aa0eaf3bc 100644 --- a/swagger/sdrangel/code/qt5/client/SWGChannelReport.cpp +++ b/swagger/sdrangel/code/qt5/client/SWGChannelReport.cpp @@ -98,8 +98,8 @@ SWGChannelReport::SWGChannelReport() { m_udp_source_report_isSet = false; udp_sink_report = nullptr; m_udp_sink_report_isSet = false; - vor_demod_report = nullptr; - m_vor_demod_report_isSet = false; + vor_demod_mc_report = nullptr; + m_vor_demod_mc_report_isSet = false; vor_demod_sc_report = nullptr; m_vor_demod_sc_report_isSet = false; wfm_demod_report = nullptr; @@ -184,8 +184,8 @@ SWGChannelReport::init() { m_udp_source_report_isSet = false; udp_sink_report = new SWGUDPSinkReport(); m_udp_sink_report_isSet = false; - vor_demod_report = new SWGVORDemodReport(); - m_vor_demod_report_isSet = false; + vor_demod_mc_report = new SWGVORDemodMCReport(); + m_vor_demod_mc_report_isSet = false; vor_demod_sc_report = new SWGVORDemodSCReport(); m_vor_demod_sc_report_isSet = false; wfm_demod_report = new SWGWFMDemodReport(); @@ -299,8 +299,8 @@ SWGChannelReport::cleanup() { if(udp_sink_report != nullptr) { delete udp_sink_report; } - if(vor_demod_report != nullptr) { - delete vor_demod_report; + if(vor_demod_mc_report != nullptr) { + delete vor_demod_mc_report; } if(vor_demod_sc_report != nullptr) { delete vor_demod_sc_report; @@ -394,7 +394,7 @@ SWGChannelReport::fromJsonObject(QJsonObject &pJson) { ::SWGSDRangel::setValue(&udp_sink_report, pJson["UDPSinkReport"], "SWGUDPSinkReport", "SWGUDPSinkReport"); - ::SWGSDRangel::setValue(&vor_demod_report, pJson["VORDemodReport"], "SWGVORDemodReport", "SWGVORDemodReport"); + ::SWGSDRangel::setValue(&vor_demod_mc_report, pJson["VORDemodMCReport"], "SWGVORDemodMCReport", "SWGVORDemodMCReport"); ::SWGSDRangel::setValue(&vor_demod_sc_report, pJson["VORDemodSCReport"], "SWGVORDemodSCReport", "SWGVORDemodSCReport"); @@ -523,8 +523,8 @@ SWGChannelReport::asJsonObject() { if((udp_sink_report != nullptr) && (udp_sink_report->isSet())){ toJsonValue(QString("UDPSinkReport"), udp_sink_report, obj, QString("SWGUDPSinkReport")); } - if((vor_demod_report != nullptr) && (vor_demod_report->isSet())){ - toJsonValue(QString("VORDemodReport"), vor_demod_report, obj, QString("SWGVORDemodReport")); + if((vor_demod_mc_report != nullptr) && (vor_demod_mc_report->isSet())){ + toJsonValue(QString("VORDemodMCReport"), vor_demod_mc_report, obj, QString("SWGVORDemodMCReport")); } if((vor_demod_sc_report != nullptr) && (vor_demod_sc_report->isSet())){ toJsonValue(QString("VORDemodSCReport"), vor_demod_sc_report, obj, QString("SWGVORDemodSCReport")); @@ -889,14 +889,14 @@ SWGChannelReport::setUdpSinkReport(SWGUDPSinkReport* udp_sink_report) { this->m_udp_sink_report_isSet = true; } -SWGVORDemodReport* -SWGChannelReport::getVorDemodReport() { - return vor_demod_report; +SWGVORDemodMCReport* +SWGChannelReport::getVorDemodMcReport() { + return vor_demod_mc_report; } void -SWGChannelReport::setVorDemodReport(SWGVORDemodReport* vor_demod_report) { - this->vor_demod_report = vor_demod_report; - this->m_vor_demod_report_isSet = true; +SWGChannelReport::setVorDemodMcReport(SWGVORDemodMCReport* vor_demod_mc_report) { + this->vor_demod_mc_report = vor_demod_mc_report; + this->m_vor_demod_mc_report_isSet = true; } SWGVORDemodSCReport* @@ -1039,7 +1039,7 @@ SWGChannelReport::isSet(){ if(udp_sink_report && udp_sink_report->isSet()){ isObjectUpdated = true; break; } - if(vor_demod_report && vor_demod_report->isSet()){ + if(vor_demod_mc_report && vor_demod_mc_report->isSet()){ isObjectUpdated = true; break; } if(vor_demod_sc_report && vor_demod_sc_report->isSet()){ diff --git a/swagger/sdrangel/code/qt5/client/SWGChannelReport.h b/swagger/sdrangel/code/qt5/client/SWGChannelReport.h index 9f91fc4c1..87de98329 100644 --- a/swagger/sdrangel/code/qt5/client/SWGChannelReport.h +++ b/swagger/sdrangel/code/qt5/client/SWGChannelReport.h @@ -55,7 +55,7 @@ #include "SWGSigMFFileSinkReport.h" #include "SWGUDPSinkReport.h" #include "SWGUDPSourceReport.h" -#include "SWGVORDemodReport.h" +#include "SWGVORDemodMCReport.h" #include "SWGVORDemodSCReport.h" #include "SWGWFMDemodReport.h" #include "SWGWFMModReport.h" @@ -184,8 +184,8 @@ public: SWGUDPSinkReport* getUdpSinkReport(); void setUdpSinkReport(SWGUDPSinkReport* udp_sink_report); - SWGVORDemodReport* getVorDemodReport(); - void setVorDemodReport(SWGVORDemodReport* vor_demod_report); + SWGVORDemodMCReport* getVorDemodMcReport(); + void setVorDemodMcReport(SWGVORDemodMCReport* vor_demod_mc_report); SWGVORDemodSCReport* getVorDemodScReport(); void setVorDemodScReport(SWGVORDemodSCReport* vor_demod_sc_report); @@ -305,8 +305,8 @@ private: SWGUDPSinkReport* udp_sink_report; bool m_udp_sink_report_isSet; - SWGVORDemodReport* vor_demod_report; - bool m_vor_demod_report_isSet; + SWGVORDemodMCReport* vor_demod_mc_report; + bool m_vor_demod_mc_report_isSet; SWGVORDemodSCReport* vor_demod_sc_report; bool m_vor_demod_sc_report_isSet; diff --git a/swagger/sdrangel/code/qt5/client/SWGChannelSettings.cpp b/swagger/sdrangel/code/qt5/client/SWGChannelSettings.cpp index 56fa8a4d4..e396533bf 100644 --- a/swagger/sdrangel/code/qt5/client/SWGChannelSettings.cpp +++ b/swagger/sdrangel/code/qt5/client/SWGChannelSettings.cpp @@ -120,8 +120,8 @@ SWGChannelSettings::SWGChannelSettings() { m_udp_source_settings_isSet = false; udp_sink_settings = nullptr; m_udp_sink_settings_isSet = false; - vor_demod_settings = nullptr; - m_vor_demod_settings_isSet = false; + vor_demod_mc_settings = nullptr; + m_vor_demod_mc_settings_isSet = false; vor_demod_sc_settings = nullptr; m_vor_demod_sc_settings_isSet = false; wfm_demod_settings = nullptr; @@ -228,8 +228,8 @@ SWGChannelSettings::init() { m_udp_source_settings_isSet = false; udp_sink_settings = new SWGUDPSinkSettings(); m_udp_sink_settings_isSet = false; - vor_demod_settings = new SWGVORDemodSettings(); - m_vor_demod_settings_isSet = false; + vor_demod_mc_settings = new SWGVORDemodMCSettings(); + m_vor_demod_mc_settings_isSet = false; vor_demod_sc_settings = new SWGVORDemodSCSettings(); m_vor_demod_sc_settings_isSet = false; wfm_demod_settings = new SWGWFMDemodSettings(); @@ -372,8 +372,8 @@ SWGChannelSettings::cleanup() { if(udp_sink_settings != nullptr) { delete udp_sink_settings; } - if(vor_demod_settings != nullptr) { - delete vor_demod_settings; + if(vor_demod_mc_settings != nullptr) { + delete vor_demod_mc_settings; } if(vor_demod_sc_settings != nullptr) { delete vor_demod_sc_settings; @@ -489,7 +489,7 @@ SWGChannelSettings::fromJsonObject(QJsonObject &pJson) { ::SWGSDRangel::setValue(&udp_sink_settings, pJson["UDPSinkSettings"], "SWGUDPSinkSettings", "SWGUDPSinkSettings"); - ::SWGSDRangel::setValue(&vor_demod_settings, pJson["VORDemodSettings"], "SWGVORDemodSettings", "SWGVORDemodSettings"); + ::SWGSDRangel::setValue(&vor_demod_mc_settings, pJson["VORDemodMCSettings"], "SWGVORDemodMCSettings", "SWGVORDemodMCSettings"); ::SWGSDRangel::setValue(&vor_demod_sc_settings, pJson["VORDemodSCSettings"], "SWGVORDemodSCSettings", "SWGVORDemodSCSettings"); @@ -651,8 +651,8 @@ SWGChannelSettings::asJsonObject() { if((udp_sink_settings != nullptr) && (udp_sink_settings->isSet())){ toJsonValue(QString("UDPSinkSettings"), udp_sink_settings, obj, QString("SWGUDPSinkSettings")); } - if((vor_demod_settings != nullptr) && (vor_demod_settings->isSet())){ - toJsonValue(QString("VORDemodSettings"), vor_demod_settings, obj, QString("SWGVORDemodSettings")); + if((vor_demod_mc_settings != nullptr) && (vor_demod_mc_settings->isSet())){ + toJsonValue(QString("VORDemodMCSettings"), vor_demod_mc_settings, obj, QString("SWGVORDemodMCSettings")); } if((vor_demod_sc_settings != nullptr) && (vor_demod_sc_settings->isSet())){ toJsonValue(QString("VORDemodSCSettings"), vor_demod_sc_settings, obj, QString("SWGVORDemodSCSettings")); @@ -1127,14 +1127,14 @@ SWGChannelSettings::setUdpSinkSettings(SWGUDPSinkSettings* udp_sink_settings) { this->m_udp_sink_settings_isSet = true; } -SWGVORDemodSettings* -SWGChannelSettings::getVorDemodSettings() { - return vor_demod_settings; +SWGVORDemodMCSettings* +SWGChannelSettings::getVorDemodMcSettings() { + return vor_demod_mc_settings; } void -SWGChannelSettings::setVorDemodSettings(SWGVORDemodSettings* vor_demod_settings) { - this->vor_demod_settings = vor_demod_settings; - this->m_vor_demod_settings_isSet = true; +SWGChannelSettings::setVorDemodMcSettings(SWGVORDemodMCSettings* vor_demod_mc_settings) { + this->vor_demod_mc_settings = vor_demod_mc_settings; + this->m_vor_demod_mc_settings_isSet = true; } SWGVORDemodSCSettings* @@ -1310,7 +1310,7 @@ SWGChannelSettings::isSet(){ if(udp_sink_settings && udp_sink_settings->isSet()){ isObjectUpdated = true; break; } - if(vor_demod_settings && vor_demod_settings->isSet()){ + if(vor_demod_mc_settings && vor_demod_mc_settings->isSet()){ isObjectUpdated = true; break; } if(vor_demod_sc_settings && vor_demod_sc_settings->isSet()){ diff --git a/swagger/sdrangel/code/qt5/client/SWGChannelSettings.h b/swagger/sdrangel/code/qt5/client/SWGChannelSettings.h index ea6549f45..8f761dd7e 100644 --- a/swagger/sdrangel/code/qt5/client/SWGChannelSettings.h +++ b/swagger/sdrangel/code/qt5/client/SWGChannelSettings.h @@ -64,8 +64,8 @@ #include "SWGSigMFFileSinkSettings.h" #include "SWGUDPSinkSettings.h" #include "SWGUDPSourceSettings.h" +#include "SWGVORDemodMCSettings.h" #include "SWGVORDemodSCSettings.h" -#include "SWGVORDemodSettings.h" #include "SWGWFMDemodSettings.h" #include "SWGWFMModSettings.h" #include @@ -226,8 +226,8 @@ public: SWGUDPSinkSettings* getUdpSinkSettings(); void setUdpSinkSettings(SWGUDPSinkSettings* udp_sink_settings); - SWGVORDemodSettings* getVorDemodSettings(); - void setVorDemodSettings(SWGVORDemodSettings* vor_demod_settings); + SWGVORDemodMCSettings* getVorDemodMcSettings(); + void setVorDemodMcSettings(SWGVORDemodMCSettings* vor_demod_mc_settings); SWGVORDemodSCSettings* getVorDemodScSettings(); void setVorDemodScSettings(SWGVORDemodSCSettings* vor_demod_sc_settings); @@ -380,8 +380,8 @@ private: SWGUDPSinkSettings* udp_sink_settings; bool m_udp_sink_settings_isSet; - SWGVORDemodSettings* vor_demod_settings; - bool m_vor_demod_settings_isSet; + SWGVORDemodMCSettings* vor_demod_mc_settings; + bool m_vor_demod_mc_settings_isSet; SWGVORDemodSCSettings* vor_demod_sc_settings; bool m_vor_demod_sc_settings_isSet; diff --git a/swagger/sdrangel/code/qt5/client/SWGModelFactory.h b/swagger/sdrangel/code/qt5/client/SWGModelFactory.h index bf01101d3..b237ccd2c 100644 --- a/swagger/sdrangel/code/qt5/client/SWGModelFactory.h +++ b/swagger/sdrangel/code/qt5/client/SWGModelFactory.h @@ -302,10 +302,10 @@ #include "SWGUSRPInputSettings.h" #include "SWGUSRPOutputReport.h" #include "SWGUSRPOutputSettings.h" -#include "SWGVORDemodReport.h" +#include "SWGVORDemodMCReport.h" +#include "SWGVORDemodMCSettings.h" #include "SWGVORDemodSCReport.h" #include "SWGVORDemodSCSettings.h" -#include "SWGVORDemodSettings.h" #include "SWGVORLocalizerActions.h" #include "SWGVORLocalizerReport.h" #include "SWGVORLocalizerSettings.h" @@ -1763,8 +1763,13 @@ namespace SWGSDRangel { obj->init(); return obj; } - if(QString("SWGVORDemodReport").compare(type) == 0) { - SWGVORDemodReport *obj = new SWGVORDemodReport(); + if(QString("SWGVORDemodMCReport").compare(type) == 0) { + SWGVORDemodMCReport *obj = new SWGVORDemodMCReport(); + obj->init(); + return obj; + } + if(QString("SWGVORDemodMCSettings").compare(type) == 0) { + SWGVORDemodMCSettings *obj = new SWGVORDemodMCSettings(); obj->init(); return obj; } @@ -1778,11 +1783,6 @@ namespace SWGSDRangel { obj->init(); return obj; } - if(QString("SWGVORDemodSettings").compare(type) == 0) { - SWGVORDemodSettings *obj = new SWGVORDemodSettings(); - obj->init(); - return obj; - } if(QString("SWGVORLocalizerActions").compare(type) == 0) { SWGVORLocalizerActions *obj = new SWGVORLocalizerActions(); obj->init(); diff --git a/swagger/sdrangel/code/qt5/client/SWGVORDemodMCReport.cpp b/swagger/sdrangel/code/qt5/client/SWGVORDemodMCReport.cpp new file mode 100644 index 000000000..b986139f7 --- /dev/null +++ b/swagger/sdrangel/code/qt5/client/SWGVORDemodMCReport.cpp @@ -0,0 +1,154 @@ +/** + * SDRangel + * This is the web REST/JSON API of SDRangel SDR software. SDRangel is an Open Source Qt5/OpenGL 3.0+ (4.3+ in Windows) GUI and server Software Defined Radio and signal analyzer in software. It supports Airspy, BladeRF, HackRF, LimeSDR, PlutoSDR, RTL-SDR, SDRplay RSP1 and FunCube --- Limitations and specifcities: * In SDRangel GUI the first Rx device set cannot be deleted. Conversely the server starts with no device sets and its number of device sets can be reduced to zero by as many calls as necessary to /sdrangel/deviceset with DELETE method. * Preset import and export from/to file is a server only feature. * Device set focus is a GUI only feature. * The following channels are not implemented (status 501 is returned): ATV and DATV demodulators, Channel Analyzer NG, LoRa demodulator * The device settings and report structures contains only the sub-structure corresponding to the device type. The DeviceSettings and DeviceReport structures documented here shows all of them but only one will be or should be present at a time * The channel settings and report structures contains only the sub-structure corresponding to the channel type. The ChannelSettings and ChannelReport structures documented here shows all of them but only one will be or should be present at a time --- + * + * OpenAPI spec version: 6.0.0 + * Contact: f4exb06@gmail.com + * + * NOTE: This class is auto generated by the swagger code generator program. + * https://github.com/swagger-api/swagger-codegen.git + * Do not edit the class manually. + */ + + +#include "SWGVORDemodMCReport.h" + +#include "SWGHelpers.h" + +#include +#include +#include +#include + +namespace SWGSDRangel { + +SWGVORDemodMCReport::SWGVORDemodMCReport(QString* json) { + init(); + this->fromJson(*json); +} + +SWGVORDemodMCReport::SWGVORDemodMCReport() { + channel_power_db = 0.0f; + m_channel_power_db_isSet = false; + squelch = 0; + m_squelch_isSet = false; + audio_sample_rate = 0; + m_audio_sample_rate_isSet = false; +} + +SWGVORDemodMCReport::~SWGVORDemodMCReport() { + this->cleanup(); +} + +void +SWGVORDemodMCReport::init() { + channel_power_db = 0.0f; + m_channel_power_db_isSet = false; + squelch = 0; + m_squelch_isSet = false; + audio_sample_rate = 0; + m_audio_sample_rate_isSet = false; +} + +void +SWGVORDemodMCReport::cleanup() { + + + +} + +SWGVORDemodMCReport* +SWGVORDemodMCReport::fromJson(QString &json) { + QByteArray array (json.toStdString().c_str()); + QJsonDocument doc = QJsonDocument::fromJson(array); + QJsonObject jsonObject = doc.object(); + this->fromJsonObject(jsonObject); + return this; +} + +void +SWGVORDemodMCReport::fromJsonObject(QJsonObject &pJson) { + ::SWGSDRangel::setValue(&channel_power_db, pJson["channelPowerDB"], "float", ""); + + ::SWGSDRangel::setValue(&squelch, pJson["squelch"], "qint32", ""); + + ::SWGSDRangel::setValue(&audio_sample_rate, pJson["audioSampleRate"], "qint32", ""); + +} + +QString +SWGVORDemodMCReport::asJson () +{ + QJsonObject* obj = this->asJsonObject(); + + QJsonDocument doc(*obj); + QByteArray bytes = doc.toJson(); + delete obj; + return QString(bytes); +} + +QJsonObject* +SWGVORDemodMCReport::asJsonObject() { + QJsonObject* obj = new QJsonObject(); + if(m_channel_power_db_isSet){ + obj->insert("channelPowerDB", QJsonValue(channel_power_db)); + } + if(m_squelch_isSet){ + obj->insert("squelch", QJsonValue(squelch)); + } + if(m_audio_sample_rate_isSet){ + obj->insert("audioSampleRate", QJsonValue(audio_sample_rate)); + } + + return obj; +} + +float +SWGVORDemodMCReport::getChannelPowerDb() { + return channel_power_db; +} +void +SWGVORDemodMCReport::setChannelPowerDb(float channel_power_db) { + this->channel_power_db = channel_power_db; + this->m_channel_power_db_isSet = true; +} + +qint32 +SWGVORDemodMCReport::getSquelch() { + return squelch; +} +void +SWGVORDemodMCReport::setSquelch(qint32 squelch) { + this->squelch = squelch; + this->m_squelch_isSet = true; +} + +qint32 +SWGVORDemodMCReport::getAudioSampleRate() { + return audio_sample_rate; +} +void +SWGVORDemodMCReport::setAudioSampleRate(qint32 audio_sample_rate) { + this->audio_sample_rate = audio_sample_rate; + this->m_audio_sample_rate_isSet = true; +} + + +bool +SWGVORDemodMCReport::isSet(){ + bool isObjectUpdated = false; + do{ + if(m_channel_power_db_isSet){ + isObjectUpdated = true; break; + } + if(m_squelch_isSet){ + isObjectUpdated = true; break; + } + if(m_audio_sample_rate_isSet){ + isObjectUpdated = true; break; + } + }while(false); + return isObjectUpdated; +} +} + diff --git a/swagger/sdrangel/code/qt5/client/SWGVORDemodMCReport.h b/swagger/sdrangel/code/qt5/client/SWGVORDemodMCReport.h new file mode 100644 index 000000000..37927bd75 --- /dev/null +++ b/swagger/sdrangel/code/qt5/client/SWGVORDemodMCReport.h @@ -0,0 +1,70 @@ +/** + * SDRangel + * This is the web REST/JSON API of SDRangel SDR software. SDRangel is an Open Source Qt5/OpenGL 3.0+ (4.3+ in Windows) GUI and server Software Defined Radio and signal analyzer in software. It supports Airspy, BladeRF, HackRF, LimeSDR, PlutoSDR, RTL-SDR, SDRplay RSP1 and FunCube --- Limitations and specifcities: * In SDRangel GUI the first Rx device set cannot be deleted. Conversely the server starts with no device sets and its number of device sets can be reduced to zero by as many calls as necessary to /sdrangel/deviceset with DELETE method. * Preset import and export from/to file is a server only feature. * Device set focus is a GUI only feature. * The following channels are not implemented (status 501 is returned): ATV and DATV demodulators, Channel Analyzer NG, LoRa demodulator * The device settings and report structures contains only the sub-structure corresponding to the device type. The DeviceSettings and DeviceReport structures documented here shows all of them but only one will be or should be present at a time * The channel settings and report structures contains only the sub-structure corresponding to the channel type. The ChannelSettings and ChannelReport structures documented here shows all of them but only one will be or should be present at a time --- + * + * OpenAPI spec version: 6.0.0 + * Contact: f4exb06@gmail.com + * + * NOTE: This class is auto generated by the swagger code generator program. + * https://github.com/swagger-api/swagger-codegen.git + * Do not edit the class manually. + */ + +/* + * SWGVORDemodMCReport.h + * + * VORDemodMC + */ + +#ifndef SWGVORDemodMCReport_H_ +#define SWGVORDemodMCReport_H_ + +#include + + + +#include "SWGObject.h" +#include "export.h" + +namespace SWGSDRangel { + +class SWG_API SWGVORDemodMCReport: public SWGObject { +public: + SWGVORDemodMCReport(); + SWGVORDemodMCReport(QString* json); + virtual ~SWGVORDemodMCReport(); + void init(); + void cleanup(); + + virtual QString asJson () override; + virtual QJsonObject* asJsonObject() override; + virtual void fromJsonObject(QJsonObject &json) override; + virtual SWGVORDemodMCReport* fromJson(QString &jsonString) override; + + float getChannelPowerDb(); + void setChannelPowerDb(float channel_power_db); + + qint32 getSquelch(); + void setSquelch(qint32 squelch); + + qint32 getAudioSampleRate(); + void setAudioSampleRate(qint32 audio_sample_rate); + + + virtual bool isSet() override; + +private: + float channel_power_db; + bool m_channel_power_db_isSet; + + qint32 squelch; + bool m_squelch_isSet; + + qint32 audio_sample_rate; + bool m_audio_sample_rate_isSet; + +}; + +} + +#endif /* SWGVORDemodMCReport_H_ */ diff --git a/swagger/sdrangel/code/qt5/client/SWGVORDemodMCSettings.cpp b/swagger/sdrangel/code/qt5/client/SWGVORDemodMCSettings.cpp new file mode 100644 index 000000000..1e7cc0201 --- /dev/null +++ b/swagger/sdrangel/code/qt5/client/SWGVORDemodMCSettings.cpp @@ -0,0 +1,463 @@ +/** + * SDRangel + * This is the web REST/JSON API of SDRangel SDR software. SDRangel is an Open Source Qt5/OpenGL 3.0+ (4.3+ in Windows) GUI and server Software Defined Radio and signal analyzer in software. It supports Airspy, BladeRF, HackRF, LimeSDR, PlutoSDR, RTL-SDR, SDRplay RSP1 and FunCube --- Limitations and specifcities: * In SDRangel GUI the first Rx device set cannot be deleted. Conversely the server starts with no device sets and its number of device sets can be reduced to zero by as many calls as necessary to /sdrangel/deviceset with DELETE method. * Preset import and export from/to file is a server only feature. * Device set focus is a GUI only feature. * The following channels are not implemented (status 501 is returned): ATV and DATV demodulators, Channel Analyzer NG, LoRa demodulator * The device settings and report structures contains only the sub-structure corresponding to the device type. The DeviceSettings and DeviceReport structures documented here shows all of them but only one will be or should be present at a time * The channel settings and report structures contains only the sub-structure corresponding to the channel type. The ChannelSettings and ChannelReport structures documented here shows all of them but only one will be or should be present at a time --- + * + * OpenAPI spec version: 6.0.0 + * Contact: f4exb06@gmail.com + * + * NOTE: This class is auto generated by the swagger code generator program. + * https://github.com/swagger-api/swagger-codegen.git + * Do not edit the class manually. + */ + + +#include "SWGVORDemodMCSettings.h" + +#include "SWGHelpers.h" + +#include +#include +#include +#include + +namespace SWGSDRangel { + +SWGVORDemodMCSettings::SWGVORDemodMCSettings(QString* json) { + init(); + this->fromJson(*json); +} + +SWGVORDemodMCSettings::SWGVORDemodMCSettings() { + squelch = 0.0f; + m_squelch_isSet = false; + volume = 0.0f; + m_volume_isSet = false; + audio_mute = 0; + m_audio_mute_isSet = false; + rgb_color = 0; + m_rgb_color_isSet = false; + title = nullptr; + m_title_isSet = false; + audio_device_name = nullptr; + m_audio_device_name_isSet = false; + stream_index = 0; + m_stream_index_isSet = false; + use_reverse_api = 0; + m_use_reverse_api_isSet = false; + reverse_api_address = nullptr; + m_reverse_api_address_isSet = false; + reverse_api_port = 0; + m_reverse_api_port_isSet = false; + reverse_api_device_index = 0; + m_reverse_api_device_index_isSet = false; + reverse_api_channel_index = 0; + m_reverse_api_channel_index_isSet = false; + ident_threshold = 0; + m_ident_threshold_isSet = false; + mag_dec_adjust = 0; + m_mag_dec_adjust_isSet = false; + channel_marker = nullptr; + m_channel_marker_isSet = false; + rollup_state = nullptr; + m_rollup_state_isSet = false; +} + +SWGVORDemodMCSettings::~SWGVORDemodMCSettings() { + this->cleanup(); +} + +void +SWGVORDemodMCSettings::init() { + squelch = 0.0f; + m_squelch_isSet = false; + volume = 0.0f; + m_volume_isSet = false; + audio_mute = 0; + m_audio_mute_isSet = false; + rgb_color = 0; + m_rgb_color_isSet = false; + title = new QString(""); + m_title_isSet = false; + audio_device_name = new QString(""); + m_audio_device_name_isSet = false; + stream_index = 0; + m_stream_index_isSet = false; + use_reverse_api = 0; + m_use_reverse_api_isSet = false; + reverse_api_address = new QString(""); + m_reverse_api_address_isSet = false; + reverse_api_port = 0; + m_reverse_api_port_isSet = false; + reverse_api_device_index = 0; + m_reverse_api_device_index_isSet = false; + reverse_api_channel_index = 0; + m_reverse_api_channel_index_isSet = false; + ident_threshold = 0; + m_ident_threshold_isSet = false; + mag_dec_adjust = 0; + m_mag_dec_adjust_isSet = false; + channel_marker = new SWGChannelMarker(); + m_channel_marker_isSet = false; + rollup_state = new SWGRollupState(); + m_rollup_state_isSet = false; +} + +void +SWGVORDemodMCSettings::cleanup() { + + + + + if(title != nullptr) { + delete title; + } + if(audio_device_name != nullptr) { + delete audio_device_name; + } + + + if(reverse_api_address != nullptr) { + delete reverse_api_address; + } + + + + + + if(channel_marker != nullptr) { + delete channel_marker; + } + if(rollup_state != nullptr) { + delete rollup_state; + } +} + +SWGVORDemodMCSettings* +SWGVORDemodMCSettings::fromJson(QString &json) { + QByteArray array (json.toStdString().c_str()); + QJsonDocument doc = QJsonDocument::fromJson(array); + QJsonObject jsonObject = doc.object(); + this->fromJsonObject(jsonObject); + return this; +} + +void +SWGVORDemodMCSettings::fromJsonObject(QJsonObject &pJson) { + ::SWGSDRangel::setValue(&squelch, pJson["squelch"], "float", ""); + + ::SWGSDRangel::setValue(&volume, pJson["volume"], "float", ""); + + ::SWGSDRangel::setValue(&audio_mute, pJson["audioMute"], "qint32", ""); + + ::SWGSDRangel::setValue(&rgb_color, pJson["rgbColor"], "qint32", ""); + + ::SWGSDRangel::setValue(&title, pJson["title"], "QString", "QString"); + + ::SWGSDRangel::setValue(&audio_device_name, pJson["audioDeviceName"], "QString", "QString"); + + ::SWGSDRangel::setValue(&stream_index, pJson["streamIndex"], "qint32", ""); + + ::SWGSDRangel::setValue(&use_reverse_api, pJson["useReverseAPI"], "qint32", ""); + + ::SWGSDRangel::setValue(&reverse_api_address, pJson["reverseAPIAddress"], "QString", "QString"); + + ::SWGSDRangel::setValue(&reverse_api_port, pJson["reverseAPIPort"], "qint32", ""); + + ::SWGSDRangel::setValue(&reverse_api_device_index, pJson["reverseAPIDeviceIndex"], "qint32", ""); + + ::SWGSDRangel::setValue(&reverse_api_channel_index, pJson["reverseAPIChannelIndex"], "qint32", ""); + + ::SWGSDRangel::setValue(&ident_threshold, pJson["identThreshold"], "qint32", ""); + + ::SWGSDRangel::setValue(&mag_dec_adjust, pJson["magDecAdjust"], "qint32", ""); + + ::SWGSDRangel::setValue(&channel_marker, pJson["channelMarker"], "SWGChannelMarker", "SWGChannelMarker"); + + ::SWGSDRangel::setValue(&rollup_state, pJson["rollupState"], "SWGRollupState", "SWGRollupState"); + +} + +QString +SWGVORDemodMCSettings::asJson () +{ + QJsonObject* obj = this->asJsonObject(); + + QJsonDocument doc(*obj); + QByteArray bytes = doc.toJson(); + delete obj; + return QString(bytes); +} + +QJsonObject* +SWGVORDemodMCSettings::asJsonObject() { + QJsonObject* obj = new QJsonObject(); + if(m_squelch_isSet){ + obj->insert("squelch", QJsonValue(squelch)); + } + if(m_volume_isSet){ + obj->insert("volume", QJsonValue(volume)); + } + if(m_audio_mute_isSet){ + obj->insert("audioMute", QJsonValue(audio_mute)); + } + if(m_rgb_color_isSet){ + obj->insert("rgbColor", QJsonValue(rgb_color)); + } + if(title != nullptr && *title != QString("")){ + toJsonValue(QString("title"), title, obj, QString("QString")); + } + if(audio_device_name != nullptr && *audio_device_name != QString("")){ + toJsonValue(QString("audioDeviceName"), audio_device_name, obj, QString("QString")); + } + if(m_stream_index_isSet){ + obj->insert("streamIndex", QJsonValue(stream_index)); + } + if(m_use_reverse_api_isSet){ + obj->insert("useReverseAPI", QJsonValue(use_reverse_api)); + } + if(reverse_api_address != nullptr && *reverse_api_address != QString("")){ + toJsonValue(QString("reverseAPIAddress"), reverse_api_address, obj, QString("QString")); + } + if(m_reverse_api_port_isSet){ + obj->insert("reverseAPIPort", QJsonValue(reverse_api_port)); + } + if(m_reverse_api_device_index_isSet){ + obj->insert("reverseAPIDeviceIndex", QJsonValue(reverse_api_device_index)); + } + if(m_reverse_api_channel_index_isSet){ + obj->insert("reverseAPIChannelIndex", QJsonValue(reverse_api_channel_index)); + } + if(m_ident_threshold_isSet){ + obj->insert("identThreshold", QJsonValue(ident_threshold)); + } + if(m_mag_dec_adjust_isSet){ + obj->insert("magDecAdjust", QJsonValue(mag_dec_adjust)); + } + if((channel_marker != nullptr) && (channel_marker->isSet())){ + toJsonValue(QString("channelMarker"), channel_marker, obj, QString("SWGChannelMarker")); + } + if((rollup_state != nullptr) && (rollup_state->isSet())){ + toJsonValue(QString("rollupState"), rollup_state, obj, QString("SWGRollupState")); + } + + return obj; +} + +float +SWGVORDemodMCSettings::getSquelch() { + return squelch; +} +void +SWGVORDemodMCSettings::setSquelch(float squelch) { + this->squelch = squelch; + this->m_squelch_isSet = true; +} + +float +SWGVORDemodMCSettings::getVolume() { + return volume; +} +void +SWGVORDemodMCSettings::setVolume(float volume) { + this->volume = volume; + this->m_volume_isSet = true; +} + +qint32 +SWGVORDemodMCSettings::getAudioMute() { + return audio_mute; +} +void +SWGVORDemodMCSettings::setAudioMute(qint32 audio_mute) { + this->audio_mute = audio_mute; + this->m_audio_mute_isSet = true; +} + +qint32 +SWGVORDemodMCSettings::getRgbColor() { + return rgb_color; +} +void +SWGVORDemodMCSettings::setRgbColor(qint32 rgb_color) { + this->rgb_color = rgb_color; + this->m_rgb_color_isSet = true; +} + +QString* +SWGVORDemodMCSettings::getTitle() { + return title; +} +void +SWGVORDemodMCSettings::setTitle(QString* title) { + this->title = title; + this->m_title_isSet = true; +} + +QString* +SWGVORDemodMCSettings::getAudioDeviceName() { + return audio_device_name; +} +void +SWGVORDemodMCSettings::setAudioDeviceName(QString* audio_device_name) { + this->audio_device_name = audio_device_name; + this->m_audio_device_name_isSet = true; +} + +qint32 +SWGVORDemodMCSettings::getStreamIndex() { + return stream_index; +} +void +SWGVORDemodMCSettings::setStreamIndex(qint32 stream_index) { + this->stream_index = stream_index; + this->m_stream_index_isSet = true; +} + +qint32 +SWGVORDemodMCSettings::getUseReverseApi() { + return use_reverse_api; +} +void +SWGVORDemodMCSettings::setUseReverseApi(qint32 use_reverse_api) { + this->use_reverse_api = use_reverse_api; + this->m_use_reverse_api_isSet = true; +} + +QString* +SWGVORDemodMCSettings::getReverseApiAddress() { + return reverse_api_address; +} +void +SWGVORDemodMCSettings::setReverseApiAddress(QString* reverse_api_address) { + this->reverse_api_address = reverse_api_address; + this->m_reverse_api_address_isSet = true; +} + +qint32 +SWGVORDemodMCSettings::getReverseApiPort() { + return reverse_api_port; +} +void +SWGVORDemodMCSettings::setReverseApiPort(qint32 reverse_api_port) { + this->reverse_api_port = reverse_api_port; + this->m_reverse_api_port_isSet = true; +} + +qint32 +SWGVORDemodMCSettings::getReverseApiDeviceIndex() { + return reverse_api_device_index; +} +void +SWGVORDemodMCSettings::setReverseApiDeviceIndex(qint32 reverse_api_device_index) { + this->reverse_api_device_index = reverse_api_device_index; + this->m_reverse_api_device_index_isSet = true; +} + +qint32 +SWGVORDemodMCSettings::getReverseApiChannelIndex() { + return reverse_api_channel_index; +} +void +SWGVORDemodMCSettings::setReverseApiChannelIndex(qint32 reverse_api_channel_index) { + this->reverse_api_channel_index = reverse_api_channel_index; + this->m_reverse_api_channel_index_isSet = true; +} + +qint32 +SWGVORDemodMCSettings::getIdentThreshold() { + return ident_threshold; +} +void +SWGVORDemodMCSettings::setIdentThreshold(qint32 ident_threshold) { + this->ident_threshold = ident_threshold; + this->m_ident_threshold_isSet = true; +} + +qint32 +SWGVORDemodMCSettings::getMagDecAdjust() { + return mag_dec_adjust; +} +void +SWGVORDemodMCSettings::setMagDecAdjust(qint32 mag_dec_adjust) { + this->mag_dec_adjust = mag_dec_adjust; + this->m_mag_dec_adjust_isSet = true; +} + +SWGChannelMarker* +SWGVORDemodMCSettings::getChannelMarker() { + return channel_marker; +} +void +SWGVORDemodMCSettings::setChannelMarker(SWGChannelMarker* channel_marker) { + this->channel_marker = channel_marker; + this->m_channel_marker_isSet = true; +} + +SWGRollupState* +SWGVORDemodMCSettings::getRollupState() { + return rollup_state; +} +void +SWGVORDemodMCSettings::setRollupState(SWGRollupState* rollup_state) { + this->rollup_state = rollup_state; + this->m_rollup_state_isSet = true; +} + + +bool +SWGVORDemodMCSettings::isSet(){ + bool isObjectUpdated = false; + do{ + if(m_squelch_isSet){ + isObjectUpdated = true; break; + } + if(m_volume_isSet){ + isObjectUpdated = true; break; + } + if(m_audio_mute_isSet){ + isObjectUpdated = true; break; + } + if(m_rgb_color_isSet){ + isObjectUpdated = true; break; + } + if(title && *title != QString("")){ + isObjectUpdated = true; break; + } + if(audio_device_name && *audio_device_name != QString("")){ + isObjectUpdated = true; break; + } + if(m_stream_index_isSet){ + isObjectUpdated = true; break; + } + if(m_use_reverse_api_isSet){ + isObjectUpdated = true; break; + } + if(reverse_api_address && *reverse_api_address != QString("")){ + isObjectUpdated = true; break; + } + if(m_reverse_api_port_isSet){ + isObjectUpdated = true; break; + } + if(m_reverse_api_device_index_isSet){ + isObjectUpdated = true; break; + } + if(m_reverse_api_channel_index_isSet){ + isObjectUpdated = true; break; + } + if(m_ident_threshold_isSet){ + isObjectUpdated = true; break; + } + if(m_mag_dec_adjust_isSet){ + isObjectUpdated = true; break; + } + if(channel_marker && channel_marker->isSet()){ + isObjectUpdated = true; break; + } + if(rollup_state && rollup_state->isSet()){ + isObjectUpdated = true; break; + } + }while(false); + return isObjectUpdated; +} +} + diff --git a/swagger/sdrangel/code/qt5/client/SWGVORDemodMCSettings.h b/swagger/sdrangel/code/qt5/client/SWGVORDemodMCSettings.h new file mode 100644 index 000000000..3bd0f407f --- /dev/null +++ b/swagger/sdrangel/code/qt5/client/SWGVORDemodMCSettings.h @@ -0,0 +1,151 @@ +/** + * SDRangel + * This is the web REST/JSON API of SDRangel SDR software. SDRangel is an Open Source Qt5/OpenGL 3.0+ (4.3+ in Windows) GUI and server Software Defined Radio and signal analyzer in software. It supports Airspy, BladeRF, HackRF, LimeSDR, PlutoSDR, RTL-SDR, SDRplay RSP1 and FunCube --- Limitations and specifcities: * In SDRangel GUI the first Rx device set cannot be deleted. Conversely the server starts with no device sets and its number of device sets can be reduced to zero by as many calls as necessary to /sdrangel/deviceset with DELETE method. * Preset import and export from/to file is a server only feature. * Device set focus is a GUI only feature. * The following channels are not implemented (status 501 is returned): ATV and DATV demodulators, Channel Analyzer NG, LoRa demodulator * The device settings and report structures contains only the sub-structure corresponding to the device type. The DeviceSettings and DeviceReport structures documented here shows all of them but only one will be or should be present at a time * The channel settings and report structures contains only the sub-structure corresponding to the channel type. The ChannelSettings and ChannelReport structures documented here shows all of them but only one will be or should be present at a time --- + * + * OpenAPI spec version: 6.0.0 + * Contact: f4exb06@gmail.com + * + * NOTE: This class is auto generated by the swagger code generator program. + * https://github.com/swagger-api/swagger-codegen.git + * Do not edit the class manually. + */ + +/* + * SWGVORDemodMCSettings.h + * + * VORDemodMC + */ + +#ifndef SWGVORDemodMCSettings_H_ +#define SWGVORDemodMCSettings_H_ + +#include + + +#include "SWGChannelMarker.h" +#include "SWGRollupState.h" +#include + +#include "SWGObject.h" +#include "export.h" + +namespace SWGSDRangel { + +class SWG_API SWGVORDemodMCSettings: public SWGObject { +public: + SWGVORDemodMCSettings(); + SWGVORDemodMCSettings(QString* json); + virtual ~SWGVORDemodMCSettings(); + void init(); + void cleanup(); + + virtual QString asJson () override; + virtual QJsonObject* asJsonObject() override; + virtual void fromJsonObject(QJsonObject &json) override; + virtual SWGVORDemodMCSettings* fromJson(QString &jsonString) override; + + float getSquelch(); + void setSquelch(float squelch); + + float getVolume(); + void setVolume(float volume); + + qint32 getAudioMute(); + void setAudioMute(qint32 audio_mute); + + qint32 getRgbColor(); + void setRgbColor(qint32 rgb_color); + + QString* getTitle(); + void setTitle(QString* title); + + QString* getAudioDeviceName(); + void setAudioDeviceName(QString* audio_device_name); + + qint32 getStreamIndex(); + void setStreamIndex(qint32 stream_index); + + qint32 getUseReverseApi(); + void setUseReverseApi(qint32 use_reverse_api); + + QString* getReverseApiAddress(); + void setReverseApiAddress(QString* reverse_api_address); + + qint32 getReverseApiPort(); + void setReverseApiPort(qint32 reverse_api_port); + + qint32 getReverseApiDeviceIndex(); + void setReverseApiDeviceIndex(qint32 reverse_api_device_index); + + qint32 getReverseApiChannelIndex(); + void setReverseApiChannelIndex(qint32 reverse_api_channel_index); + + qint32 getIdentThreshold(); + void setIdentThreshold(qint32 ident_threshold); + + qint32 getMagDecAdjust(); + void setMagDecAdjust(qint32 mag_dec_adjust); + + SWGChannelMarker* getChannelMarker(); + void setChannelMarker(SWGChannelMarker* channel_marker); + + SWGRollupState* getRollupState(); + void setRollupState(SWGRollupState* rollup_state); + + + virtual bool isSet() override; + +private: + float squelch; + bool m_squelch_isSet; + + float volume; + bool m_volume_isSet; + + qint32 audio_mute; + bool m_audio_mute_isSet; + + qint32 rgb_color; + bool m_rgb_color_isSet; + + QString* title; + bool m_title_isSet; + + QString* audio_device_name; + bool m_audio_device_name_isSet; + + qint32 stream_index; + bool m_stream_index_isSet; + + qint32 use_reverse_api; + bool m_use_reverse_api_isSet; + + QString* reverse_api_address; + bool m_reverse_api_address_isSet; + + qint32 reverse_api_port; + bool m_reverse_api_port_isSet; + + qint32 reverse_api_device_index; + bool m_reverse_api_device_index_isSet; + + qint32 reverse_api_channel_index; + bool m_reverse_api_channel_index_isSet; + + qint32 ident_threshold; + bool m_ident_threshold_isSet; + + qint32 mag_dec_adjust; + bool m_mag_dec_adjust_isSet; + + SWGChannelMarker* channel_marker; + bool m_channel_marker_isSet; + + SWGRollupState* rollup_state; + bool m_rollup_state_isSet; + +}; + +} + +#endif /* SWGVORDemodMCSettings_H_ */ From ca44e253ec335c5cfa0985f5d105987efe415c81 Mon Sep 17 00:00:00 2001 From: f4exb Date: Sun, 1 May 2022 11:43:23 +0200 Subject: [PATCH 086/115] v7: rename VORDemodSC classes to VORDemod classes. Part of #1223 --- plugins/channelrx/demodvorsc/readme.md | 2 +- plugins/channelrx/demodvorsc/vordemodsc.cpp | 108 +++++++++--------- plugins/channelrx/demodvorsc/vordemodsc.h | 30 ++--- .../demodvorsc/vordemodscbaseband.cpp | 32 +++--- .../channelrx/demodvorsc/vordemodscbaseband.h | 18 +-- .../channelrx/demodvorsc/vordemodscgui.cpp | 94 +++++++-------- plugins/channelrx/demodvorsc/vordemodscgui.h | 22 ++-- plugins/channelrx/demodvorsc/vordemodscgui.ui | 2 +- .../channelrx/demodvorsc/vordemodscplugin.cpp | 24 ++-- .../channelrx/demodvorsc/vordemodscplugin.h | 4 +- .../channelrx/demodvorsc/vordemodscreport.cpp | 4 +- .../channelrx/demodvorsc/vordemodscreport.h | 6 +- .../demodvorsc/vordemodscsettings.cpp | 8 +- .../channelrx/demodvorsc/vordemodscsettings.h | 4 +- .../channelrx/demodvorsc/vordemodscsink.cpp | 40 +++---- plugins/channelrx/demodvorsc/vordemodscsink.h | 4 +- .../demodvorsc/vordemodscwebapiadapter.cpp | 4 +- .../demodvorsc/vordemodscwebapiadapter.h | 2 +- plugins/feature/vorlocalizer/vorlocalizer.cpp | 4 +- .../code/qt5/client/SWGVORDemodSCReport.h | 4 +- .../code/qt5/client/SWGVORDemodSCSettings.h | 4 +- 21 files changed, 210 insertions(+), 210 deletions(-) diff --git a/plugins/channelrx/demodvorsc/readme.md b/plugins/channelrx/demodvorsc/readme.md index 7604ae07f..fb4ad3754 100644 --- a/plugins/channelrx/demodvorsc/readme.md +++ b/plugins/channelrx/demodvorsc/readme.md @@ -1,4 +1,4 @@ -

    VOR demodulator plugin - Single channel version

    +

    VOR demodulator plugin

    Introduction

    diff --git a/plugins/channelrx/demodvorsc/vordemodsc.cpp b/plugins/channelrx/demodvorsc/vordemodsc.cpp index fb30218d9..ce9720311 100644 --- a/plugins/channelrx/demodvorsc/vordemodsc.cpp +++ b/plugins/channelrx/demodvorsc/vordemodsc.cpp @@ -42,19 +42,19 @@ #include "vordemodscreport.h" #include "vordemodsc.h" -MESSAGE_CLASS_DEFINITION(VORDemodSC::MsgConfigureVORDemod, Message) +MESSAGE_CLASS_DEFINITION(VORDemod::MsgConfigureVORDemod, Message) -const char * const VORDemodSC::m_channelIdURI = "sdrangel.channel.vordemodsc"; -const char * const VORDemodSC::m_channelId = "VORDemodSC"; +const char * const VORDemod::m_channelIdURI = "sdrangel.channel.vordemodsc"; +const char * const VORDemod::m_channelId = "VORDemod"; -VORDemodSC::VORDemodSC(DeviceAPI *deviceAPI) : +VORDemod::VORDemod(DeviceAPI *deviceAPI) : ChannelAPI(m_channelIdURI, ChannelAPI::StreamSingleSink), m_deviceAPI(deviceAPI), m_basebandSampleRate(0) { setObjectName(m_channelId); - m_basebandSink = new VORDemodSCBaseband(); + m_basebandSink = new VORDemodBaseband(); m_basebandSink->setMessageQueueToChannel(getInputMessageQueue()); m_basebandSink->moveToThread(&m_thread); @@ -68,24 +68,24 @@ VORDemodSC::VORDemodSC(DeviceAPI *deviceAPI) : m_networkManager, &QNetworkAccessManager::finished, this, - &VORDemodSC::networkManagerFinished + &VORDemod::networkManagerFinished ); QObject::connect( this, &ChannelAPI::indexInDeviceSetChanged, this, - &VORDemodSC::handleIndexInDeviceSetChanged + &VORDemod::handleIndexInDeviceSetChanged ); } -VORDemodSC::~VORDemodSC() +VORDemod::~VORDemod() { - qDebug("VORDemodSC::~VORDemodSC"); + qDebug("VORDemod::~VORDemod"); QObject::disconnect( m_networkManager, &QNetworkAccessManager::finished, this, - &VORDemodSC::networkManagerFinished + &VORDemod::networkManagerFinished ); delete m_networkManager; m_deviceAPI->removeChannelSinkAPI(this); @@ -98,7 +98,7 @@ VORDemodSC::~VORDemodSC() delete m_basebandSink; } -void VORDemodSC::setDeviceAPI(DeviceAPI *deviceAPI) +void VORDemod::setDeviceAPI(DeviceAPI *deviceAPI) { if (deviceAPI != m_deviceAPI) { @@ -110,20 +110,20 @@ void VORDemodSC::setDeviceAPI(DeviceAPI *deviceAPI) } } -uint32_t VORDemodSC::getNumberOfDeviceStreams() const +uint32_t VORDemod::getNumberOfDeviceStreams() const { return m_deviceAPI->getNbSourceStreams(); } -void VORDemodSC::feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool firstOfBurst) +void VORDemod::feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool firstOfBurst) { (void) firstOfBurst; m_basebandSink->feed(begin, end); } -void VORDemodSC::start() +void VORDemod::start() { - qDebug("VORDemodSC::start"); + qDebug("VORDemod::start"); m_basebandSink->reset(); m_basebandSink->startWork(); @@ -132,24 +132,24 @@ void VORDemodSC::start() DSPSignalNotification *dspMsg = new DSPSignalNotification(m_basebandSampleRate, m_centerFrequency); m_basebandSink->getInputMessageQueue()->push(dspMsg); - VORDemodSCBaseband::MsgConfigureVORDemodBaseband *msg = VORDemodSCBaseband::MsgConfigureVORDemodBaseband::create(m_settings, true); + VORDemodBaseband::MsgConfigureVORDemodBaseband *msg = VORDemodBaseband::MsgConfigureVORDemodBaseband::create(m_settings, true); m_basebandSink->getInputMessageQueue()->push(msg); } -void VORDemodSC::stop() +void VORDemod::stop() { - qDebug("VORDemodSC::stop"); + qDebug("VORDemod::stop"); m_basebandSink->stopWork(); m_thread.quit(); m_thread.wait(); } -bool VORDemodSC::handleMessage(const Message& cmd) +bool VORDemod::handleMessage(const Message& cmd) { if (MsgConfigureVORDemod::match(cmd)) { MsgConfigureVORDemod& cfg = (MsgConfigureVORDemod&) cmd; - qDebug() << "VORDemodSC::handleMessage: MsgConfigureVORDemod"; + qDebug() << "VORDemod::handleMessage: MsgConfigureVORDemod"; applySettings(cfg.getSettings(), cfg.getForce()); return true; @@ -161,7 +161,7 @@ bool VORDemodSC::handleMessage(const Message& cmd) m_centerFrequency = notif.getCenterFrequency(); // Forward to the sink DSPSignalNotification* rep = new DSPSignalNotification(notif); // make a copy - qDebug() << "VORDemodSC::handleMessage: DSPSignalNotification"; + qDebug() << "VORDemod::handleMessage: DSPSignalNotification"; m_basebandSink->getInputMessageQueue()->push(rep); // Forward to GUI if any if (m_guiMessageQueue) { @@ -170,16 +170,16 @@ bool VORDemodSC::handleMessage(const Message& cmd) return true; } - else if (VORDemodSCReport::MsgReportRadial::match(cmd)) + else if (VORDemodReport::MsgReportRadial::match(cmd)) { - VORDemodSCReport::MsgReportRadial& report = (VORDemodSCReport::MsgReportRadial&) cmd; + VORDemodReport::MsgReportRadial& report = (VORDemodReport::MsgReportRadial&) cmd; m_radial = report.getRadial(); m_refMag = report.getRefMag(); m_varMag = report.getVarMag(); if (m_guiMessageQueue) { - VORDemodSCReport::MsgReportRadial *msg = new VORDemodSCReport::MsgReportRadial(report); + VORDemodReport::MsgReportRadial *msg = new VORDemodReport::MsgReportRadial(report); m_guiMessageQueue->push(msg); } @@ -193,14 +193,14 @@ bool VORDemodSC::handleMessage(const Message& cmd) return true; } - else if (VORDemodSCReport::MsgReportIdent::match(cmd)) + else if (VORDemodReport::MsgReportIdent::match(cmd)) { - VORDemodSCReport::MsgReportIdent& report = (VORDemodSCReport::MsgReportIdent&) cmd; + VORDemodReport::MsgReportIdent& report = (VORDemodReport::MsgReportIdent&) cmd; m_morseIdent = report.getIdent(); if (m_guiMessageQueue) { - VORDemodSCReport::MsgReportIdent *msg = new VORDemodSCReport::MsgReportIdent(report); + VORDemodReport::MsgReportIdent *msg = new VORDemodReport::MsgReportIdent(report); m_guiMessageQueue->push(msg); } @@ -220,9 +220,9 @@ bool VORDemodSC::handleMessage(const Message& cmd) } } -void VORDemodSC::setCenterFrequency(qint64 frequency) +void VORDemod::setCenterFrequency(qint64 frequency) { - VORDemodSCSettings settings = m_settings; + VORDemodSettings settings = m_settings; settings.m_inputFrequencyOffset = frequency; applySettings(settings, false); @@ -233,9 +233,9 @@ void VORDemodSC::setCenterFrequency(qint64 frequency) } } -void VORDemodSC::applySettings(const VORDemodSCSettings& settings, bool force) +void VORDemod::applySettings(const VORDemodSettings& settings, bool force) { - qDebug() << "VORDemodSC::applySettings:" + qDebug() << "VORDemod::applySettings:" << " m_inputFrequencyOffset: " << settings.m_inputFrequencyOffset << " m_navId: " << settings.m_navId << " m_volume: " << settings.m_volume @@ -290,7 +290,7 @@ void VORDemodSC::applySettings(const VORDemodSCSettings& settings, bool force) reverseAPIKeys.append("identThreshold"); } - VORDemodSCBaseband::MsgConfigureVORDemodBaseband *msg = VORDemodSCBaseband::MsgConfigureVORDemodBaseband::create(settings, force); + VORDemodBaseband::MsgConfigureVORDemodBaseband *msg = VORDemodBaseband::MsgConfigureVORDemodBaseband::create(settings, force); m_basebandSink->getInputMessageQueue()->push(msg); if (settings.m_useReverseAPI) @@ -313,12 +313,12 @@ void VORDemodSC::applySettings(const VORDemodSCSettings& settings, bool force) m_settings = settings; } -QByteArray VORDemodSC::serialize() const +QByteArray VORDemod::serialize() const { return m_settings.serialize(); } -bool VORDemodSC::deserialize(const QByteArray& data) +bool VORDemod::deserialize(const QByteArray& data) { if (m_settings.deserialize(data)) { @@ -335,7 +335,7 @@ bool VORDemodSC::deserialize(const QByteArray& data) } } -int VORDemodSC::webapiSettingsGet( +int VORDemod::webapiSettingsGet( SWGSDRangel::SWGChannelSettings& response, QString& errorMessage) { @@ -346,20 +346,20 @@ int VORDemodSC::webapiSettingsGet( return 200; } -int VORDemodSC::webapiSettingsPutPatch( +int VORDemod::webapiSettingsPutPatch( bool force, const QStringList& channelSettingsKeys, SWGSDRangel::SWGChannelSettings& response, QString& errorMessage) { (void) errorMessage; - VORDemodSCSettings settings = m_settings; + VORDemodSettings settings = m_settings; webapiUpdateChannelSettings(settings, channelSettingsKeys, response); MsgConfigureVORDemod *msg = MsgConfigureVORDemod::create(settings, force); m_inputMessageQueue.push(msg); - qDebug("VORDemodSC::webapiSettingsPutPatch: forward to GUI: %p", m_guiMessageQueue); + qDebug("VORDemod::webapiSettingsPutPatch: forward to GUI: %p", m_guiMessageQueue); if (m_guiMessageQueue) // forward to GUI if any { MsgConfigureVORDemod *msgToGUI = MsgConfigureVORDemod::create(settings, force); @@ -371,8 +371,8 @@ int VORDemodSC::webapiSettingsPutPatch( return 200; } -void VORDemodSC::webapiUpdateChannelSettings( - VORDemodSCSettings& settings, +void VORDemod::webapiUpdateChannelSettings( + VORDemodSettings& settings, const QStringList& channelSettingsKeys, SWGSDRangel::SWGChannelSettings& response) { @@ -429,7 +429,7 @@ void VORDemodSC::webapiUpdateChannelSettings( } } -int VORDemodSC::webapiReportGet( +int VORDemod::webapiReportGet( SWGSDRangel::SWGChannelReport& response, QString& errorMessage) { @@ -440,7 +440,7 @@ int VORDemodSC::webapiReportGet( return 200; } -void VORDemodSC::webapiFormatChannelSettings(SWGSDRangel::SWGChannelSettings& response, const VORDemodSCSettings& settings) +void VORDemod::webapiFormatChannelSettings(SWGSDRangel::SWGChannelSettings& response, const VORDemodSettings& settings) { response.getVorDemodScSettings()->setInputFrequencyOffset(settings.m_inputFrequencyOffset); response.getVorDemodScSettings()->setNavId(settings.m_navId); @@ -504,7 +504,7 @@ void VORDemodSC::webapiFormatChannelSettings(SWGSDRangel::SWGChannelSettings& re } } -void VORDemodSC::webapiFormatChannelReport(SWGSDRangel::SWGChannelReport& response) +void VORDemod::webapiFormatChannelReport(SWGSDRangel::SWGChannelReport& response) { double magsqAvg, magsqPeak; int nbMagsqSamples; @@ -532,7 +532,7 @@ void VORDemodSC::webapiFormatChannelReport(SWGSDRangel::SWGChannelReport& respon } } -void VORDemodSC::webapiReverseSendSettings(QList& channelSettingsKeys, const VORDemodSCSettings& settings, bool force) +void VORDemod::webapiReverseSendSettings(QList& channelSettingsKeys, const VORDemodSettings& settings, bool force) { SWGSDRangel::SWGChannelSettings *swgChannelSettings = new SWGSDRangel::SWGChannelSettings(); webapiFormatChannelSettings(channelSettingsKeys, swgChannelSettings, settings, force); @@ -557,10 +557,10 @@ void VORDemodSC::webapiReverseSendSettings(QList& channelSettingsKeys, delete swgChannelSettings; } -void VORDemodSC::sendChannelSettings( +void VORDemod::sendChannelSettings( const QList& pipes, QList& channelSettingsKeys, - const VORDemodSCSettings& settings, + const VORDemodSettings& settings, bool force) { for (const auto& pipe : pipes) @@ -582,7 +582,7 @@ void VORDemodSC::sendChannelSettings( } } -void VORDemodSC::sendChannelReport(QList& messagePipes) +void VORDemod::sendChannelReport(QList& messagePipes) { for (const auto& pipe : messagePipes) { @@ -601,17 +601,17 @@ void VORDemodSC::sendChannelReport(QList& messagePipes) } } -void VORDemodSC::webapiFormatChannelSettings( +void VORDemod::webapiFormatChannelSettings( QList& channelSettingsKeys, SWGSDRangel::SWGChannelSettings *swgChannelSettings, - const VORDemodSCSettings& settings, + const VORDemodSettings& settings, bool force ) { swgChannelSettings->setDirection(0); // Single sink (Rx) swgChannelSettings->setOriginatorChannelIndex(getIndexInDeviceSet()); swgChannelSettings->setOriginatorDeviceSetIndex(getDeviceSetIndex()); - swgChannelSettings->setChannelType(new QString("VORDemodSC")); + swgChannelSettings->setChannelType(new QString("VORDemod")); swgChannelSettings->setVorDemodScSettings(new SWGSDRangel::SWGVORDemodSCSettings()); SWGSDRangel::SWGVORDemodSCSettings *swgVORDemodSCSettings = swgChannelSettings->getVorDemodScSettings(); @@ -663,13 +663,13 @@ void VORDemodSC::webapiFormatChannelSettings( } } -void VORDemodSC::networkManagerFinished(QNetworkReply *reply) +void VORDemod::networkManagerFinished(QNetworkReply *reply) { QNetworkReply::NetworkError replyError = reply->error(); if (replyError) { - qWarning() << "VORDemodSC::networkManagerFinished:" + qWarning() << "VORDemod::networkManagerFinished:" << " error(" << (int) replyError << "): " << replyError << ": " << reply->errorString(); @@ -678,13 +678,13 @@ void VORDemodSC::networkManagerFinished(QNetworkReply *reply) { QString answer = reply->readAll(); answer.chop(1); // remove last \n - qDebug("VORDemodSC::networkManagerFinished: reply:\n%s", answer.toStdString().c_str()); + qDebug("VORDemod::networkManagerFinished: reply:\n%s", answer.toStdString().c_str()); } reply->deleteLater(); } -void VORDemodSC::handleIndexInDeviceSetChanged(int index) +void VORDemod::handleIndexInDeviceSetChanged(int index) { if (index < 0) { return; diff --git a/plugins/channelrx/demodvorsc/vordemodsc.h b/plugins/channelrx/demodvorsc/vordemodsc.h index 3741eb873..5dc5bf848 100644 --- a/plugins/channelrx/demodvorsc/vordemodsc.h +++ b/plugins/channelrx/demodvorsc/vordemodsc.h @@ -37,33 +37,33 @@ class QThread; class DeviceAPI; class ObjectPipe; -class VORDemodSC : public BasebandSampleSink, public ChannelAPI { +class VORDemod : public BasebandSampleSink, public ChannelAPI { public: class MsgConfigureVORDemod : public Message { MESSAGE_CLASS_DECLARATION public: - const VORDemodSCSettings& getSettings() const { return m_settings; } + const VORDemodSettings& getSettings() const { return m_settings; } bool getForce() const { return m_force; } - static MsgConfigureVORDemod* create(const VORDemodSCSettings& settings, bool force) + static MsgConfigureVORDemod* create(const VORDemodSettings& settings, bool force) { return new MsgConfigureVORDemod(settings, force); } private: - VORDemodSCSettings m_settings; + VORDemodSettings m_settings; bool m_force; - MsgConfigureVORDemod(const VORDemodSCSettings& settings, bool force) : + MsgConfigureVORDemod(const VORDemodSettings& settings, bool force) : Message(), m_settings(settings), m_force(force) { } }; - VORDemodSC(DeviceAPI *deviceAPI); - virtual ~VORDemodSC(); + VORDemod(DeviceAPI *deviceAPI); + virtual ~VORDemod(); virtual void destroy() { delete this; } virtual void setDeviceAPI(DeviceAPI *deviceAPI); virtual DeviceAPI *getDeviceAPI() { return m_deviceAPI; } @@ -111,10 +111,10 @@ public: static void webapiFormatChannelSettings( SWGSDRangel::SWGChannelSettings& response, - const VORDemodSCSettings& settings); + const VORDemodSettings& settings); static void webapiUpdateChannelSettings( - VORDemodSCSettings& settings, + VORDemodSettings& settings, const QStringList& channelSettingsKeys, SWGSDRangel::SWGChannelSettings& response); @@ -134,8 +134,8 @@ public: private: DeviceAPI *m_deviceAPI; QThread m_thread; - VORDemodSCBaseband* m_basebandSink; - VORDemodSCSettings m_settings; + VORDemodBaseband* m_basebandSink; + VORDemodSettings m_settings; int m_basebandSampleRate; //!< stored from device message used when starting baseband sink qint64 m_centerFrequency; @@ -148,20 +148,20 @@ private: QNetworkRequest m_networkRequest; virtual bool handleMessage(const Message& cmd); - void applySettings(const VORDemodSCSettings& settings, bool force = false); + void applySettings(const VORDemodSettings& settings, bool force = false); void webapiFormatChannelReport(SWGSDRangel::SWGChannelReport& response); - void webapiReverseSendSettings(QList& channelSettingsKeys, const VORDemodSCSettings& settings, bool force); + void webapiReverseSendSettings(QList& channelSettingsKeys, const VORDemodSettings& settings, bool force); void sendChannelSettings( const QList& pipes, QList& channelSettingsKeys, - const VORDemodSCSettings& settings, + const VORDemodSettings& settings, bool force ); void sendChannelReport(QList& messagePipes); void webapiFormatChannelSettings( QList& channelSettingsKeys, SWGSDRangel::SWGChannelSettings *swgChannelSettings, - const VORDemodSCSettings& settings, + const VORDemodSettings& settings, bool force ); diff --git a/plugins/channelrx/demodvorsc/vordemodscbaseband.cpp b/plugins/channelrx/demodvorsc/vordemodscbaseband.cpp index a5715cd97..a67cd9800 100644 --- a/plugins/channelrx/demodvorsc/vordemodscbaseband.cpp +++ b/plugins/channelrx/demodvorsc/vordemodscbaseband.cpp @@ -25,14 +25,14 @@ #include "vordemodscbaseband.h" #include "vordemodscreport.h" -MESSAGE_CLASS_DEFINITION(VORDemodSCBaseband::MsgConfigureVORDemodBaseband, Message) +MESSAGE_CLASS_DEFINITION(VORDemodBaseband::MsgConfigureVORDemodBaseband, Message) -VORDemodSCBaseband::VORDemodSCBaseband() : +VORDemodBaseband::VORDemodBaseband() : m_messageQueueToGUI(nullptr), m_running(false), m_mutex(QMutex::Recursive) { - qDebug("VORDemodSCBaseband::VORDemodSCBaseband"); + qDebug("VORDemodBaseband::VORDemodBaseband"); m_sampleFifo.setSize(SampleSinkFifo::getSizePolicy(48000)); m_channelizer = new DownChannelizer(&m_sink); @@ -41,14 +41,14 @@ VORDemodSCBaseband::VORDemodSCBaseband() : m_channelSampleRate = 0; } -VORDemodSCBaseband::~VORDemodSCBaseband() +VORDemodBaseband::~VORDemodBaseband() { m_inputMessageQueue.clear(); DSPEngine::instance()->getAudioDeviceManager()->removeAudioSink(m_sink.getAudioFifo()); delete m_channelizer; } -void VORDemodSCBaseband::reset() +void VORDemodBaseband::reset() { QMutexLocker mutexLocker(&m_mutex); m_inputMessageQueue.clear(); @@ -56,21 +56,21 @@ void VORDemodSCBaseband::reset() m_channelSampleRate = 0; } -void VORDemodSCBaseband::startWork() +void VORDemodBaseband::startWork() { QMutexLocker mutexLocker(&m_mutex); QObject::connect( &m_sampleFifo, &SampleSinkFifo::dataReady, this, - &VORDemodSCBaseband::handleData, + &VORDemodBaseband::handleData, Qt::QueuedConnection ); connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages())); m_running = true; } -void VORDemodSCBaseband::stopWork() +void VORDemodBaseband::stopWork() { QMutexLocker mutexLocker(&m_mutex); disconnect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages())); @@ -78,17 +78,17 @@ void VORDemodSCBaseband::stopWork() &m_sampleFifo, &SampleSinkFifo::dataReady, this, - &VORDemodSCBaseband::handleData + &VORDemodBaseband::handleData ); m_running = false; } -void VORDemodSCBaseband::feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end) +void VORDemodBaseband::feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end) { m_sampleFifo.write(begin, end); } -void VORDemodSCBaseband::handleData() +void VORDemodBaseband::handleData() { QMutexLocker mutexLocker(&m_mutex); @@ -115,7 +115,7 @@ void VORDemodSCBaseband::handleData() } } -void VORDemodSCBaseband::handleInputMessages() +void VORDemodBaseband::handleInputMessages() { Message* message; @@ -127,13 +127,13 @@ void VORDemodSCBaseband::handleInputMessages() } } -bool VORDemodSCBaseband::handleMessage(const Message& cmd) +bool VORDemodBaseband::handleMessage(const Message& cmd) { if (MsgConfigureVORDemodBaseband::match(cmd)) { QMutexLocker mutexLocker(&m_mutex); MsgConfigureVORDemodBaseband& cfg = (MsgConfigureVORDemodBaseband&) cmd; - qDebug() << "VORDemodSCBaseband::handleMessage: MsgConfigureVORDemodBaseband"; + qDebug() << "VORDemodBaseband::handleMessage: MsgConfigureVORDemodBaseband"; applySettings(cfg.getSettings(), cfg.getForce()); @@ -143,7 +143,7 @@ bool VORDemodSCBaseband::handleMessage(const Message& cmd) { QMutexLocker mutexLocker(&m_mutex); DSPSignalNotification& notif = (DSPSignalNotification&) cmd; - qDebug() << "VORDemodSCBaseband::handleMessage: DSPSignalNotification: basebandSampleRate: " << notif.getSampleRate() << " centerFrequency: " << notif.getCenterFrequency(); + qDebug() << "VORDemodBaseband::handleMessage: DSPSignalNotification: basebandSampleRate: " << notif.getSampleRate() << " centerFrequency: " << notif.getCenterFrequency(); m_sampleFifo.setSize(SampleSinkFifo::getSizePolicy(notif.getSampleRate())); m_channelizer->setBasebandSampleRate(notif.getSampleRate()); m_sink.applyChannelSettings(m_channelizer->getChannelSampleRate(), m_channelizer->getChannelFrequencyOffset()); @@ -162,7 +162,7 @@ bool VORDemodSCBaseband::handleMessage(const Message& cmd) } } -void VORDemodSCBaseband::applySettings(const VORDemodSCSettings& settings, bool force) +void VORDemodBaseband::applySettings(const VORDemodSettings& settings, bool force) { if ((settings.m_inputFrequencyOffset != m_settings.m_inputFrequencyOffset) || force) { diff --git a/plugins/channelrx/demodvorsc/vordemodscbaseband.h b/plugins/channelrx/demodvorsc/vordemodscbaseband.h index 1fab48b7b..16df9cf87 100644 --- a/plugins/channelrx/demodvorsc/vordemodscbaseband.h +++ b/plugins/channelrx/demodvorsc/vordemodscbaseband.h @@ -30,7 +30,7 @@ class DownChannelizer; -class VORDemodSCBaseband : public QObject +class VORDemodBaseband : public QObject { Q_OBJECT public: @@ -38,27 +38,27 @@ public: MESSAGE_CLASS_DECLARATION public: - const VORDemodSCSettings& getSettings() const { return m_settings; } + const VORDemodSettings& getSettings() const { return m_settings; } bool getForce() const { return m_force; } - static MsgConfigureVORDemodBaseband* create(const VORDemodSCSettings& settings, bool force) + static MsgConfigureVORDemodBaseband* create(const VORDemodSettings& settings, bool force) { return new MsgConfigureVORDemodBaseband(settings, force); } private: - VORDemodSCSettings m_settings; + VORDemodSettings m_settings; bool m_force; - MsgConfigureVORDemodBaseband(const VORDemodSCSettings& settings, bool force) : + MsgConfigureVORDemodBaseband(const VORDemodSettings& settings, bool force) : Message(), m_settings(settings), m_force(force) { } }; - VORDemodSCBaseband(); - ~VORDemodSCBaseband(); + VORDemodBaseband(); + ~VORDemodBaseband(); void reset(); void startWork(); void stopWork(); @@ -79,13 +79,13 @@ private: int m_channelSampleRate; VORDemodSCSink m_sink; MessageQueue m_inputMessageQueue; //!< Queue for asynchronous inbound communication - VORDemodSCSettings m_settings; + VORDemodSettings m_settings; MessageQueue *m_messageQueueToGUI; bool m_running; QMutex m_mutex; bool handleMessage(const Message& cmd); - void applySettings(const VORDemodSCSettings& settings, bool force = false); + void applySettings(const VORDemodSettings& settings, bool force = false); private slots: void handleInputMessages(); diff --git a/plugins/channelrx/demodvorsc/vordemodscgui.cpp b/plugins/channelrx/demodvorsc/vordemodscgui.cpp index dd657e541..7f73c241d 100644 --- a/plugins/channelrx/demodvorsc/vordemodscgui.cpp +++ b/plugins/channelrx/demodvorsc/vordemodscgui.cpp @@ -42,30 +42,30 @@ #include "vordemodscreport.h" #include "vordemodscgui.h" -VORDemodSCGUI* VORDemodSCGUI::create(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel) +VORDemodGUI* VORDemodGUI::create(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel) { - VORDemodSCGUI* gui = new VORDemodSCGUI(pluginAPI, deviceUISet, rxChannel); + VORDemodGUI* gui = new VORDemodGUI(pluginAPI, deviceUISet, rxChannel); return gui; } -void VORDemodSCGUI::destroy() +void VORDemodGUI::destroy() { delete this; } -void VORDemodSCGUI::resetToDefaults() +void VORDemodGUI::resetToDefaults() { m_settings.resetToDefaults(); displaySettings(); applySettings(true); } -QByteArray VORDemodSCGUI::serialize() const +QByteArray VORDemodGUI::serialize() const { return m_settings.serialize(); } -bool VORDemodSCGUI::deserialize(const QByteArray& data) +bool VORDemodGUI::deserialize(const QByteArray& data) { if(m_settings.deserialize(data)) { displaySettings(); @@ -77,7 +77,7 @@ bool VORDemodSCGUI::deserialize(const QByteArray& data) } } -void VORDemodSCGUI::resizeEvent(QResizeEvent* size) +void VORDemodGUI::resizeEvent(QResizeEvent* size) { int maxWidth = getRollupContents()->maximumWidth(); int minHeight = getRollupContents()->minimumHeight() + getAdditionalHeight(); @@ -85,12 +85,12 @@ void VORDemodSCGUI::resizeEvent(QResizeEvent* size) size->accept(); } -bool VORDemodSCGUI::handleMessage(const Message& message) +bool VORDemodGUI::handleMessage(const Message& message) { - if (VORDemodSC::MsgConfigureVORDemod::match(message)) + if (VORDemod::MsgConfigureVORDemod::match(message)) { - qDebug("VORDemodSCGUI::handleMessage: VORDemodSC::MsgConfigureVORDemod"); - const VORDemodSC::MsgConfigureVORDemod& cfg = (VORDemodSC::MsgConfigureVORDemod&) message; + qDebug("VORDemodGUI::handleMessage: VORDemod::MsgConfigureVORDemod"); + const VORDemod::MsgConfigureVORDemod& cfg = (VORDemod::MsgConfigureVORDemod&) message; m_settings = cfg.getSettings(); blockApplySettings(true); displaySettings(); @@ -107,9 +107,9 @@ bool VORDemodSCGUI::handleMessage(const Message& message) updateAbsoluteCenterFrequency(); return true; } - else if (VORDemodSCReport::MsgReportRadial::match(message)) + else if (VORDemodReport::MsgReportRadial::match(message)) { - VORDemodSCReport::MsgReportRadial& report = (VORDemodSCReport::MsgReportRadial&) message; + VORDemodReport::MsgReportRadial& report = (VORDemodReport::MsgReportRadial&) message; // Display radial and signal magnitudes Real varMagDB = std::round(20.0*std::log10(report.getVarMag())); @@ -143,9 +143,9 @@ bool VORDemodSCGUI::handleMessage(const Message& message) return true; } - else if (VORDemodSCReport::MsgReportIdent::match(message)) + else if (VORDemodReport::MsgReportIdent::match(message)) { - VORDemodSCReport::MsgReportIdent& report = (VORDemodSCReport::MsgReportIdent&) message; + VORDemodReport::MsgReportIdent& report = (VORDemodReport::MsgReportIdent&) message; QString ident = report.getIdent(); QString identString = Morse::toString(ident); // Convert Morse to a string @@ -172,7 +172,7 @@ bool VORDemodSCGUI::handleMessage(const Message& message) return false; } -void VORDemodSCGUI::handleInputMessages() +void VORDemodGUI::handleInputMessages() { Message* message; @@ -185,19 +185,19 @@ void VORDemodSCGUI::handleInputMessages() } } -void VORDemodSCGUI::channelMarkerChangedByCursor() +void VORDemodGUI::channelMarkerChangedByCursor() { ui->deltaFrequency->setValue(m_channelMarker.getCenterFrequency()); m_settings.m_inputFrequencyOffset = m_channelMarker.getCenterFrequency(); applySettings(); } -void VORDemodSCGUI::channelMarkerHighlightedByCursor() +void VORDemodGUI::channelMarkerHighlightedByCursor() { setHighlighted(m_channelMarker.getHighlighted()); } -void VORDemodSCGUI::on_deltaFrequency_changed(qint64 value) +void VORDemodGUI::on_deltaFrequency_changed(qint64 value) { m_channelMarker.setCenterFrequency(value); m_settings.m_inputFrequencyOffset = m_channelMarker.getCenterFrequency(); @@ -205,34 +205,34 @@ void VORDemodSCGUI::on_deltaFrequency_changed(qint64 value) applySettings(); } -void VORDemodSCGUI::on_thresh_valueChanged(int value) +void VORDemodGUI::on_thresh_valueChanged(int value) { ui->threshText->setText(QString("%1").arg(value / 10.0, 0, 'f', 1)); m_settings.m_identThreshold = value / 10.0; applySettings(); } -void VORDemodSCGUI::on_volume_valueChanged(int value) +void VORDemodGUI::on_volume_valueChanged(int value) { ui->volumeText->setText(QString("%1").arg(value / 10.0, 0, 'f', 1)); m_settings.m_volume = value / 10.0; applySettings(); } -void VORDemodSCGUI::on_squelch_valueChanged(int value) +void VORDemodGUI::on_squelch_valueChanged(int value) { ui->squelchText->setText(QString("%1 dB").arg(value)); m_settings.m_squelch = value; applySettings(); } -void VORDemodSCGUI::on_audioMute_toggled(bool checked) +void VORDemodGUI::on_audioMute_toggled(bool checked) { m_settings.m_audioMute = checked; applySettings(); } -void VORDemodSCGUI::onWidgetRolled(QWidget* widget, bool rollDown) +void VORDemodGUI::onWidgetRolled(QWidget* widget, bool rollDown) { (void) widget; (void) rollDown; @@ -241,7 +241,7 @@ void VORDemodSCGUI::onWidgetRolled(QWidget* widget, bool rollDown) applySettings(); } -void VORDemodSCGUI::onMenuDialogCalled(const QPoint &p) +void VORDemodGUI::onMenuDialogCalled(const QPoint &p) { if (m_contextMenuType == ContextMenuChannelSettings) { @@ -288,9 +288,9 @@ void VORDemodSCGUI::onMenuDialogCalled(const QPoint &p) resetContextMenuType(); } -VORDemodSCGUI::VORDemodSCGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel, QWidget* parent) : +VORDemodGUI::VORDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel, QWidget* parent) : ChannelGUI(parent), - ui(new Ui::VORDemodSCGUI), + ui(new Ui::VORDemodGUI), m_pluginAPI(pluginAPI), m_deviceUISet(deviceUISet), m_channelMarker(this), @@ -308,7 +308,7 @@ VORDemodSCGUI::VORDemodSCGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Bas connect(rollupContents, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &))); - m_vorDemod = reinterpret_cast(rxChannel); + m_vorDemod = reinterpret_cast(rxChannel); m_vorDemod->setMessageQueueToGUI(getInputMessageQueue()); connect(&MainCore::instance()->getMasterTimer(), SIGNAL(timeout()), this, SLOT(tick())); // 50 ms @@ -323,7 +323,7 @@ VORDemodSCGUI::VORDemodSCGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Bas m_channelMarker.blockSignals(true); m_channelMarker.setColor(Qt::yellow); - m_channelMarker.setBandwidth(2*VORDemodSCSettings::VORDEMOD_CHANNEL_BANDWIDTH); + m_channelMarker.setBandwidth(2*VORDemodSettings::VORDEMOD_CHANNEL_BANDWIDTH); m_channelMarker.setCenterFrequency(0); m_channelMarker.setTitle("VOR Demodulator"); m_channelMarker.blockSignals(false); @@ -344,30 +344,30 @@ VORDemodSCGUI::VORDemodSCGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Bas applySettings(true); } -VORDemodSCGUI::~VORDemodSCGUI() +VORDemodGUI::~VORDemodGUI() { delete ui; } -void VORDemodSCGUI::blockApplySettings(bool block) +void VORDemodGUI::blockApplySettings(bool block) { m_doApplySettings = !block; } -void VORDemodSCGUI::applySettings(bool force) +void VORDemodGUI::applySettings(bool force) { if (m_doApplySettings) { - VORDemodSC::MsgConfigureVORDemod* message = VORDemodSC::MsgConfigureVORDemod::create( m_settings, force); + VORDemod::MsgConfigureVORDemod* message = VORDemod::MsgConfigureVORDemod::create( m_settings, force); m_vorDemod->getInputMessageQueue()->push(message); } } -void VORDemodSCGUI::displaySettings() +void VORDemodGUI::displaySettings() { m_channelMarker.blockSignals(true); m_channelMarker.setCenterFrequency(m_settings.m_inputFrequencyOffset); - m_channelMarker.setBandwidth(2*VORDemodSCSettings::VORDEMOD_CHANNEL_BANDWIDTH); + m_channelMarker.setBandwidth(2*VORDemodSettings::VORDEMOD_CHANNEL_BANDWIDTH); m_channelMarker.setTitle(m_settings.m_title); m_channelMarker.blockSignals(false); m_channelMarker.setColor(m_settings.m_rgbColor); // activate signal on the last setting only @@ -398,21 +398,21 @@ void VORDemodSCGUI::displaySettings() blockApplySettings(false); } -void VORDemodSCGUI::leaveEvent(QEvent* event) +void VORDemodGUI::leaveEvent(QEvent* event) { m_channelMarker.setHighlighted(false); ChannelGUI::leaveEvent(event); } -void VORDemodSCGUI::enterEvent(QEvent* event) +void VORDemodGUI::enterEvent(QEvent* event) { m_channelMarker.setHighlighted(true); ChannelGUI::enterEvent(event); } -void VORDemodSCGUI::audioSelect() +void VORDemodGUI::audioSelect() { - qDebug("VORDemodSCGUI::audioSelect"); + qDebug("VORDemodGUI::audioSelect"); AudioSelectDialog audioSelect(DSPEngine::instance()->getAudioDeviceManager(), m_settings.m_audioDeviceName); audioSelect.exec(); @@ -423,7 +423,7 @@ void VORDemodSCGUI::audioSelect() } } -void VORDemodSCGUI::tick() +void VORDemodGUI::tick() { double magsqAvg, magsqPeak; int nbMagsqSamples; @@ -459,16 +459,16 @@ void VORDemodSCGUI::tick() m_tickCount++; } -void VORDemodSCGUI::makeUIConnections() +void VORDemodGUI::makeUIConnections() { - QObject::connect(ui->deltaFrequency, &ValueDialZ::changed, this, &VORDemodSCGUI::on_deltaFrequency_changed); - QObject::connect(ui->thresh, &QDial::valueChanged, this, &VORDemodSCGUI::on_thresh_valueChanged); - QObject::connect(ui->volume, &QDial::valueChanged, this, &VORDemodSCGUI::on_volume_valueChanged); - QObject::connect(ui->squelch, &QDial::valueChanged, this, &VORDemodSCGUI::on_squelch_valueChanged); - QObject::connect(ui->audioMute, &QToolButton::toggled, this, &VORDemodSCGUI::on_audioMute_toggled); + QObject::connect(ui->deltaFrequency, &ValueDialZ::changed, this, &VORDemodGUI::on_deltaFrequency_changed); + QObject::connect(ui->thresh, &QDial::valueChanged, this, &VORDemodGUI::on_thresh_valueChanged); + QObject::connect(ui->volume, &QDial::valueChanged, this, &VORDemodGUI::on_volume_valueChanged); + QObject::connect(ui->squelch, &QDial::valueChanged, this, &VORDemodGUI::on_squelch_valueChanged); + QObject::connect(ui->audioMute, &QToolButton::toggled, this, &VORDemodGUI::on_audioMute_toggled); } -void VORDemodSCGUI::updateAbsoluteCenterFrequency() +void VORDemodGUI::updateAbsoluteCenterFrequency() { setStatusFrequency(m_deviceCenterFrequency + m_settings.m_inputFrequencyOffset); } diff --git a/plugins/channelrx/demodvorsc/vordemodscgui.h b/plugins/channelrx/demodvorsc/vordemodscgui.h index 53e23bac4..11a553dfa 100644 --- a/plugins/channelrx/demodvorsc/vordemodscgui.h +++ b/plugins/channelrx/demodvorsc/vordemodscgui.h @@ -29,19 +29,19 @@ class PluginAPI; class DeviceUISet; class BasebandSampleSink; -class VORDemodSC; -class VORDemodSCGUI; +class VORDemod; +class VORDemodGUI; namespace Ui { - class VORDemodSCGUI; + class VORDemodGUI; } -class VORDemodSCGUI; +class VORDemodGUI; -class VORDemodSCGUI : public ChannelGUI { +class VORDemodGUI : public ChannelGUI { Q_OBJECT public: - static VORDemodSCGUI* create(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel); + static VORDemodGUI* create(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel); virtual void destroy(); void resetToDefaults(); @@ -68,23 +68,23 @@ protected: void resizeEvent(QResizeEvent* size); private: - Ui::VORDemodSCGUI* ui; + Ui::VORDemodGUI* ui; PluginAPI* m_pluginAPI; DeviceUISet* m_deviceUISet; ChannelMarker m_channelMarker; RollupState m_rollupState; - VORDemodSCSettings m_settings; + VORDemodSettings m_settings; qint64 m_deviceCenterFrequency; bool m_doApplySettings; - VORDemodSC* m_vorDemod; + VORDemod* m_vorDemod; bool m_squelchOpen; int m_basebandSampleRate; uint32_t m_tickCount; MessageQueue m_inputMessageQueue; - explicit VORDemodSCGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel, QWidget* parent = 0); - virtual ~VORDemodSCGUI(); + explicit VORDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel, QWidget* parent = 0); + virtual ~VORDemodGUI(); void blockApplySettings(bool block); void applySettings(bool force = false); diff --git a/plugins/channelrx/demodvorsc/vordemodscgui.ui b/plugins/channelrx/demodvorsc/vordemodscgui.ui index 2e14ac898..c1dd647e0 100644 --- a/plugins/channelrx/demodvorsc/vordemodscgui.ui +++ b/plugins/channelrx/demodvorsc/vordemodscgui.ui @@ -1,6 +1,6 @@ - VORDemodSCGUI + VORDemodGUI diff --git a/plugins/channelrx/demodvorsc/vordemodscplugin.cpp b/plugins/channelrx/demodvorsc/vordemodscplugin.cpp index 7794fbc66..c27969448 100644 --- a/plugins/channelrx/demodvorsc/vordemodscplugin.cpp +++ b/plugins/channelrx/demodvorsc/vordemodscplugin.cpp @@ -26,8 +26,8 @@ #include "vordemodscwebapiadapter.h" #include "vordemodscplugin.h" -const PluginDescriptor VORDemodSCPlugin::m_pluginDescriptor = { - VORDemodSC::m_channelId, +const PluginDescriptor VORDemodPlugin::m_pluginDescriptor = { + VORDemod::m_channelId, QStringLiteral("VOR Single Channel Demodulator"), QStringLiteral("6.20.2"), QStringLiteral("(c) Jon Beniston, M7RCE"), @@ -36,29 +36,29 @@ const PluginDescriptor VORDemodSCPlugin::m_pluginDescriptor = { QStringLiteral("https://github.com/f4exb/sdrangel") }; -VORDemodSCPlugin::VORDemodSCPlugin(QObject* parent) : +VORDemodPlugin::VORDemodPlugin(QObject* parent) : QObject(parent), m_pluginAPI(0) { } -const PluginDescriptor& VORDemodSCPlugin::getPluginDescriptor() const +const PluginDescriptor& VORDemodPlugin::getPluginDescriptor() const { return m_pluginDescriptor; } -void VORDemodSCPlugin::initPlugin(PluginAPI* pluginAPI) +void VORDemodPlugin::initPlugin(PluginAPI* pluginAPI) { m_pluginAPI = pluginAPI; - m_pluginAPI->registerRxChannel(VORDemodSC::m_channelIdURI, VORDemodSC::m_channelId, this); + m_pluginAPI->registerRxChannel(VORDemod::m_channelIdURI, VORDemod::m_channelId, this); } -void VORDemodSCPlugin::createRxChannel(DeviceAPI *deviceAPI, BasebandSampleSink **bs, ChannelAPI **cs) const +void VORDemodPlugin::createRxChannel(DeviceAPI *deviceAPI, BasebandSampleSink **bs, ChannelAPI **cs) const { if (bs || cs) { - VORDemodSC *instance = new VORDemodSC(deviceAPI); + VORDemod *instance = new VORDemod(deviceAPI); if (bs) { *bs = instance; @@ -71,7 +71,7 @@ void VORDemodSCPlugin::createRxChannel(DeviceAPI *deviceAPI, BasebandSampleSink } #ifdef SERVER_MODE -ChannelGUI* VORDemodSCPlugin::createRxChannelGUI( +ChannelGUI* VORDemodPlugin::createRxChannelGUI( DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel) const { @@ -80,13 +80,13 @@ ChannelGUI* VORDemodSCPlugin::createRxChannelGUI( return 0; } #else -ChannelGUI* VORDemodSCPlugin::createRxChannelGUI(DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel) const +ChannelGUI* VORDemodPlugin::createRxChannelGUI(DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel) const { - return VORDemodSCGUI::create(m_pluginAPI, deviceUISet, rxChannel); + return VORDemodGUI::create(m_pluginAPI, deviceUISet, rxChannel); } #endif -ChannelWebAPIAdapter* VORDemodSCPlugin::createChannelWebAPIAdapter() const +ChannelWebAPIAdapter* VORDemodPlugin::createChannelWebAPIAdapter() const { return new VORDemodSCWebAPIAdapter(); } diff --git a/plugins/channelrx/demodvorsc/vordemodscplugin.h b/plugins/channelrx/demodvorsc/vordemodscplugin.h index 7bbb55c86..122af6fab 100644 --- a/plugins/channelrx/demodvorsc/vordemodscplugin.h +++ b/plugins/channelrx/demodvorsc/vordemodscplugin.h @@ -25,13 +25,13 @@ class DeviceUISet; class BasebandSampleSink; -class VORDemodSCPlugin : public QObject, PluginInterface { +class VORDemodPlugin : public QObject, PluginInterface { Q_OBJECT Q_INTERFACES(PluginInterface) Q_PLUGIN_METADATA(IID "sdrangel.channel.vordemodsc") public: - explicit VORDemodSCPlugin(QObject* parent = NULL); + explicit VORDemodPlugin(QObject* parent = NULL); const PluginDescriptor& getPluginDescriptor() const; void initPlugin(PluginAPI* pluginAPI); diff --git a/plugins/channelrx/demodvorsc/vordemodscreport.cpp b/plugins/channelrx/demodvorsc/vordemodscreport.cpp index 1bc6842c8..cb9fc4ee1 100644 --- a/plugins/channelrx/demodvorsc/vordemodscreport.cpp +++ b/plugins/channelrx/demodvorsc/vordemodscreport.cpp @@ -17,5 +17,5 @@ #include "vordemodscreport.h" -MESSAGE_CLASS_DEFINITION(VORDemodSCReport::MsgReportRadial, Message) -MESSAGE_CLASS_DEFINITION(VORDemodSCReport::MsgReportIdent, Message) +MESSAGE_CLASS_DEFINITION(VORDemodReport::MsgReportRadial, Message) +MESSAGE_CLASS_DEFINITION(VORDemodReport::MsgReportIdent, Message) diff --git a/plugins/channelrx/demodvorsc/vordemodscreport.h b/plugins/channelrx/demodvorsc/vordemodscreport.h index 1da87bf76..8a053620d 100644 --- a/plugins/channelrx/demodvorsc/vordemodscreport.h +++ b/plugins/channelrx/demodvorsc/vordemodscreport.h @@ -23,7 +23,7 @@ #include "util/message.h" -class VORDemodSCReport : public QObject +class VORDemodReport : public QObject { Q_OBJECT public: @@ -75,8 +75,8 @@ public: }; public: - VORDemodSCReport() {} - ~VORDemodSCReport() {} + VORDemodReport() {} + ~VORDemodReport() {} }; #endif // INCLUDE_VORDEMODSCREPORT_H diff --git a/plugins/channelrx/demodvorsc/vordemodscsettings.cpp b/plugins/channelrx/demodvorsc/vordemodscsettings.cpp index e4380ad4a..f4f0952db 100644 --- a/plugins/channelrx/demodvorsc/vordemodscsettings.cpp +++ b/plugins/channelrx/demodvorsc/vordemodscsettings.cpp @@ -23,14 +23,14 @@ #include "settings/serializable.h" #include "vordemodscsettings.h" -VORDemodSCSettings::VORDemodSCSettings() : +VORDemodSettings::VORDemodSettings() : m_channelMarker(nullptr), m_rollupState(nullptr) { resetToDefaults(); } -void VORDemodSCSettings::resetToDefaults() +void VORDemodSettings::resetToDefaults() { m_inputFrequencyOffset = 0; m_navId = -1; @@ -54,7 +54,7 @@ void VORDemodSCSettings::resetToDefaults() m_varThresholdDB = -90.0; } -QByteArray VORDemodSCSettings::serialize() const +QByteArray VORDemodSettings::serialize() const { SimpleSerializer s(1); s.writeS32(1, m_inputFrequencyOffset); @@ -89,7 +89,7 @@ QByteArray VORDemodSCSettings::serialize() const return s.final(); } -bool VORDemodSCSettings::deserialize(const QByteArray& data) +bool VORDemodSettings::deserialize(const QByteArray& data) { SimpleDeserializer d(data); diff --git a/plugins/channelrx/demodvorsc/vordemodscsettings.h b/plugins/channelrx/demodvorsc/vordemodscsettings.h index 8e19f5483..b26650a02 100644 --- a/plugins/channelrx/demodvorsc/vordemodscsettings.h +++ b/plugins/channelrx/demodvorsc/vordemodscsettings.h @@ -24,7 +24,7 @@ class Serializable; -struct VORDemodSCSettings +struct VORDemodSettings { qint32 m_inputFrequencyOffset; int m_navId; //!< VOR unique identifier when set by VOR localizer feature @@ -58,7 +58,7 @@ struct VORDemodSCSettings // May as well make it a common audio rate, to possibly avoid decimation static const int VORDEMOD_CHANNEL_SAMPLE_RATE = 48000; - VORDemodSCSettings(); + VORDemodSettings(); void resetToDefaults(); void setChannelMarker(Serializable *channelMarker) { m_channelMarker = channelMarker; } void setRollupState(Serializable *rollupState) { m_rollupState = rollupState; } diff --git a/plugins/channelrx/demodvorsc/vordemodscsink.cpp b/plugins/channelrx/demodvorsc/vordemodscsink.cpp index 0ca67c9d1..c94fb45f8 100644 --- a/plugins/channelrx/demodvorsc/vordemodscsink.cpp +++ b/plugins/channelrx/demodvorsc/vordemodscsink.cpp @@ -33,7 +33,7 @@ VORDemodSCSink::VORDemodSCSink() : m_channelFrequencyOffset(0), - m_channelSampleRate(VORDemodSCSettings::VORDEMOD_CHANNEL_SAMPLE_RATE), + m_channelSampleRate(VORDemodSettings::VORDEMOD_CHANNEL_SAMPLE_RATE), m_audioSampleRate(48000), m_squelchCount(0), m_squelchOpen(false), @@ -47,8 +47,8 @@ VORDemodSCSink::VORDemodSCSink() : m_movingAverageIdent(5000), m_prevBit(0), m_bitTime(0), - m_varGoertzel(30, VORDemodSCSettings::VORDEMOD_CHANNEL_SAMPLE_RATE), - m_refGoertzel(30, VORDemodSCSettings::VORDEMOD_CHANNEL_SAMPLE_RATE) + m_varGoertzel(30, VORDemodSettings::VORDEMOD_CHANNEL_SAMPLE_RATE), + m_refGoertzel(30, VORDemodSettings::VORDEMOD_CHANNEL_SAMPLE_RATE) { m_audioBuffer.resize(1<<14); m_audioBufferFill = 0; @@ -197,7 +197,7 @@ void VORDemodSCSink::processOneSample(Complex &ci) // Calculate phase of 30Hz variable AM signal double varPhase; double varMag; - if (m_varGoertzel.size() == VORDemodSCSettings::VORDEMOD_CHANNEL_SAMPLE_RATE - 1) + if (m_varGoertzel.size() == VORDemodSettings::VORDEMOD_CHANNEL_SAMPLE_RATE - 1) { m_varGoertzel.goertzel(mag); varPhase = Units::radiansToDegrees(m_varGoertzel.phase()); @@ -220,13 +220,13 @@ void VORDemodSCSink::processOneSample(Complex &ci) m_refPrev = fmfilt; // Calculate phase of 30Hz reference FM signal - if (m_refGoertzel.size() == VORDemodSCSettings::VORDEMOD_CHANNEL_SAMPLE_RATE - 1) + if (m_refGoertzel.size() == VORDemodSettings::VORDEMOD_CHANNEL_SAMPLE_RATE - 1) { m_refGoertzel.goertzel(phi); float phaseDeg = Units::radiansToDegrees(m_refGoertzel.phase()); double refMag = m_refGoertzel.mag(); int groupDelay = (301-1)/2; - float filterPhaseShift = 360.0*30.0*groupDelay/VORDemodSCSettings::VORDEMOD_CHANNEL_SAMPLE_RATE; + float filterPhaseShift = 360.0*30.0*groupDelay/VORDemodSettings::VORDEMOD_CHANNEL_SAMPLE_RATE; float shiftedPhase = phaseDeg + filterPhaseShift; // Calculate difference in phase, which is the radial @@ -240,7 +240,7 @@ void VORDemodSCSink::processOneSample(Complex &ci) if (getMessageQueueToChannel()) { - VORDemodSCReport::MsgReportRadial *msg = VORDemodSCReport::MsgReportRadial::create(phaseDifference, refMag, varMag); + VORDemodReport::MsgReportRadial *msg = VORDemodReport::MsgReportRadial::create(phaseDifference, refMag, varMag); getMessageQueueToChannel()->push(msg); } @@ -295,7 +295,7 @@ void VORDemodSCSink::processOneSample(Complex &ci) if (getMessageQueueToChannel()) { - VORDemodSCReport::MsgReportIdent *msg = VORDemodSCReport::MsgReportIdent::create(m_ident); + VORDemodReport::MsgReportIdent *msg = VORDemodReport::MsgReportIdent::create(m_ident); getMessageQueueToChannel()->push(msg); } @@ -336,7 +336,7 @@ void VORDemodSCSink::processOneSample(Complex &ci) if (getMessageQueueToChannel()) { - VORDemodSCReport::MsgReportIdent *msg = VORDemodSCReport::MsgReportIdent::create(m_ident); + VORDemodReport::MsgReportIdent *msg = VORDemodReport::MsgReportIdent::create(m_ident); getMessageQueueToChannel()->push(msg); } @@ -362,17 +362,17 @@ void VORDemodSCSink::applyChannelSettings(int channelSampleRate, int channelFreq if ((m_channelSampleRate != channelSampleRate) || force) { - m_interpolator.create(16, channelSampleRate, VORDemodSCSettings::VORDEMOD_CHANNEL_BANDWIDTH); + m_interpolator.create(16, channelSampleRate, VORDemodSettings::VORDEMOD_CHANNEL_BANDWIDTH); m_interpolatorDistanceRemain = 0; - m_interpolatorDistance = (Real) channelSampleRate / (Real) VORDemodSCSettings::VORDEMOD_CHANNEL_SAMPLE_RATE; + m_interpolatorDistance = (Real) channelSampleRate / (Real) VORDemodSettings::VORDEMOD_CHANNEL_SAMPLE_RATE; - m_samplesPerDot7wpm = VORDemodSCSettings::VORDEMOD_CHANNEL_SAMPLE_RATE*60/(50*7); - m_samplesPerDot10wpm = VORDemodSCSettings::VORDEMOD_CHANNEL_SAMPLE_RATE*60/(50*10); + m_samplesPerDot7wpm = VORDemodSettings::VORDEMOD_CHANNEL_SAMPLE_RATE*60/(50*7); + m_samplesPerDot10wpm = VORDemodSettings::VORDEMOD_CHANNEL_SAMPLE_RATE*60/(50*10); - m_ncoIdent.setFreq(-1020, VORDemodSCSettings::VORDEMOD_CHANNEL_SAMPLE_RATE); // +-50Hz source offset allowed - m_ncoRef.setFreq(-9960, VORDemodSCSettings::VORDEMOD_CHANNEL_SAMPLE_RATE); - m_lowpassIdent.create(301, VORDemodSCSettings::VORDEMOD_CHANNEL_SAMPLE_RATE, 100.0f); - m_lowpassRef.create(301, VORDemodSCSettings::VORDEMOD_CHANNEL_SAMPLE_RATE, 600.0f); // Max deviation is 480Hz + m_ncoIdent.setFreq(-1020, VORDemodSettings::VORDEMOD_CHANNEL_SAMPLE_RATE); // +-50Hz source offset allowed + m_ncoRef.setFreq(-9960, VORDemodSettings::VORDEMOD_CHANNEL_SAMPLE_RATE); + m_lowpassIdent.create(301, VORDemodSettings::VORDEMOD_CHANNEL_SAMPLE_RATE, 100.0f); + m_lowpassRef.create(301, VORDemodSettings::VORDEMOD_CHANNEL_SAMPLE_RATE, 600.0f); // Max deviation is 480Hz m_movingAverageIdent.resize(m_samplesPerDot10wpm/5); // Needs to be short enough for noise floor calculation m_binSampleCnt = 0; @@ -388,7 +388,7 @@ void VORDemodSCSink::applyChannelSettings(int channelSampleRate, int channelFreq m_channelFrequencyOffset = channelFrequencyOffset; } -void VORDemodSCSink::applySettings(const VORDemodSCSettings& settings, bool force) +void VORDemodSCSink::applySettings(const VORDemodSettings& settings, bool force) { qDebug() << "VORDemodSCSink::applySettings:" << " m_volume: " << settings.m_volume @@ -415,9 +415,9 @@ void VORDemodSCSink::applyAudioSampleRate(int sampleRate) qDebug("VORDemodSCSink::applyAudioSampleRate: sampleRate: %d m_channelSampleRate: %d", sampleRate, m_channelSampleRate); // (ICAO Annex 10 3.3.6.3) - Optional voice audio is 300Hz to 3kHz - m_audioInterpolator.create(16, VORDemodSCSettings::VORDEMOD_CHANNEL_SAMPLE_RATE, 3000.0f); + m_audioInterpolator.create(16, VORDemodSettings::VORDEMOD_CHANNEL_SAMPLE_RATE, 3000.0f); m_audioInterpolatorDistanceRemain = 0; - m_audioInterpolatorDistance = (Real) VORDemodSCSettings::VORDEMOD_CHANNEL_SAMPLE_RATE / (Real) sampleRate; + m_audioInterpolatorDistance = (Real) VORDemodSettings::VORDEMOD_CHANNEL_SAMPLE_RATE / (Real) sampleRate; m_bandpass.create(301, sampleRate, 300.0f, 3000.0f); m_audioFifo.setSize(sampleRate); m_squelchDelayLine.resize(sampleRate/5); diff --git a/plugins/channelrx/demodvorsc/vordemodscsink.h b/plugins/channelrx/demodvorsc/vordemodscsink.h index 79abd6c39..6cc2a34b1 100644 --- a/plugins/channelrx/demodvorsc/vordemodscsink.h +++ b/plugins/channelrx/demodvorsc/vordemodscsink.h @@ -42,7 +42,7 @@ public: virtual void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end); void applyChannelSettings(int channelSampleRate, int channelFrequencyOffset, bool force = false); - void applySettings(const VORDemodSCSettings& settings, bool force = false); + void applySettings(const VORDemodSettings& settings, bool force = false); void setMessageQueueToChannel(MessageQueue *messageQueue) { m_messageQueueToChannel = messageQueue; } void applyAudioSampleRate(int sampleRate); @@ -83,7 +83,7 @@ private: double m_magsqPeak; }; - VORDemodSCSettings m_settings; + VORDemodSettings m_settings; int m_channelSampleRate; int m_audioSampleRate; diff --git a/plugins/channelrx/demodvorsc/vordemodscwebapiadapter.cpp b/plugins/channelrx/demodvorsc/vordemodscwebapiadapter.cpp index e94aa6aa0..117654b64 100644 --- a/plugins/channelrx/demodvorsc/vordemodscwebapiadapter.cpp +++ b/plugins/channelrx/demodvorsc/vordemodscwebapiadapter.cpp @@ -33,7 +33,7 @@ int VORDemodSCWebAPIAdapter::webapiSettingsGet( (void) errorMessage; response.setVorDemodScSettings(new SWGSDRangel::SWGVORDemodSCSettings()); response.getVorDemodScSettings()->init(); - VORDemodSC::webapiFormatChannelSettings(response, m_settings); + VORDemod::webapiFormatChannelSettings(response, m_settings); return 200; } @@ -46,7 +46,7 @@ int VORDemodSCWebAPIAdapter::webapiSettingsPutPatch( { (void) force; (void) errorMessage; - VORDemodSC::webapiUpdateChannelSettings(m_settings, channelSettingsKeys, response); + VORDemod::webapiUpdateChannelSettings(m_settings, channelSettingsKeys, response); return 200; } diff --git a/plugins/channelrx/demodvorsc/vordemodscwebapiadapter.h b/plugins/channelrx/demodvorsc/vordemodscwebapiadapter.h index 285c768aa..8cf1ae192 100644 --- a/plugins/channelrx/demodvorsc/vordemodscwebapiadapter.h +++ b/plugins/channelrx/demodvorsc/vordemodscwebapiadapter.h @@ -44,7 +44,7 @@ public: QString& errorMessage); private: - VORDemodSCSettings m_settings; + VORDemodSettings m_settings; }; #endif // INCLUDE_VORDEMODSC_WEBAPIADAPTER_H diff --git a/plugins/feature/vorlocalizer/vorlocalizer.cpp b/plugins/feature/vorlocalizer/vorlocalizer.cpp index f618cc580..49f200264 100644 --- a/plugins/feature/vorlocalizer/vorlocalizer.cpp +++ b/plugins/feature/vorlocalizer/vorlocalizer.cpp @@ -152,7 +152,7 @@ bool VORLocalizer::handleMessage(const Message& cmd) SWGSDRangel::SWGChannelReport* swgChannelReport = report.getSWGReport(); QString *channelType = swgChannelReport->getChannelType(); - if (*channelType == "VORDemodSC") + if (*channelType == "VORDemod") { SWGSDRangel::SWGVORDemodSCReport *swgVORDemodSCReport = swgChannelReport->getVorDemodScReport(); int navId = swgVORDemodSCReport->getNavId(); @@ -165,7 +165,7 @@ bool VORLocalizer::handleMessage(const Message& cmd) m_vorSinglePlans[navId] : false; - // qDebug() << "VORLocalizer::handleMessage: MainCore::MsgChannelReport(VORDemodSC): " + // qDebug() << "VORLocalizer::handleMessage: MainCore::MsgChannelReport(VORDemod): " // << "navId:" << navId // << "singlePlanProvided" << m_vorSinglePlans.contains(navId) // << "singlePlan:" << singlePlan; diff --git a/swagger/sdrangel/code/qt5/client/SWGVORDemodSCReport.h b/swagger/sdrangel/code/qt5/client/SWGVORDemodSCReport.h index e0afd6b71..fbb7e9497 100644 --- a/swagger/sdrangel/code/qt5/client/SWGVORDemodSCReport.h +++ b/swagger/sdrangel/code/qt5/client/SWGVORDemodSCReport.h @@ -1,6 +1,6 @@ /** * SDRangel - * This is the web REST/JSON API of SDRangel SDR software. SDRangel is an Open Source Qt5/OpenGL 3.0+ (4.3+ in Windows) GUI and server Software Defined Radio and signal analyzer in software. It supports Airspy, BladeRF, HackRF, LimeSDR, PlutoSDR, RTL-SDR, SDRplay RSP1 and FunCube --- Limitations and specifcities: * In SDRangel GUI the first Rx device set cannot be deleted. Conversely the server starts with no device sets and its number of device sets can be reduced to zero by as many calls as necessary to /sdrangel/deviceset with DELETE method. * Preset import and export from/to file is a server only feature. * Device set focus is a GUI only feature. * The following channels are not implemented (status 501 is returned): ATV and DATV demodulators, Channel Analyzer NG, LoRa demodulator * The device settings and report structures contains only the sub-structure corresponding to the device type. The DeviceSettings and DeviceReport structures documented here shows all of them but only one will be or should be present at a time * The channel settings and report structures contains only the sub-structure corresponding to the channel type. The ChannelSettings and ChannelReport structures documented here shows all of them but only one will be or should be present at a time --- + * This is the web REST/JSON API of SDRangel SDR software. SDRangel is an Open Source Qt5/OpenGL 3.0+ (4.3+ in Windows) GUI and server Software Defined Radio and signal analyzer in software. It supports Airspy, BladeRF, HackRF, LimeSDR, PlutoSDR, RTL-SDR, SDRplay RSP1 and FunCube --- Limitations and specifcities: * In SDRangel GUI the first Rx device set cannot be deleted. Conversely the server starts with no device sets and its number of device sets can be reduced to zero by as many calls as necessary to /sdrangel/deviceset with DELETE method. * Preset import and export from/to file is a server only feature. * Device set focus is a GUI only feature. * The following channels are not implemented (status 501 is returned): ATV and DATV demodulators, Channel Analyzer NG, LoRa demodulator * The device settings and report structures contains only the sub-structure corresponding to the device type. The DeviceSettings and DeviceReport structures documented here shows all of them but only one will be or should be present at a time * The channel settings and report structures contains only the sub-structure corresponding to the channel type. The ChannelSettings and ChannelReport structures documented here shows all of them but only one will be or should be present at a time --- * * OpenAPI spec version: 6.0.0 * Contact: f4exb06@gmail.com @@ -13,7 +13,7 @@ /* * SWGVORDemodSCReport.h * - * VORDemodSC + * VORDemod */ #ifndef SWGVORDemodSCReport_H_ diff --git a/swagger/sdrangel/code/qt5/client/SWGVORDemodSCSettings.h b/swagger/sdrangel/code/qt5/client/SWGVORDemodSCSettings.h index 9a7c2fb7f..c7acf6446 100644 --- a/swagger/sdrangel/code/qt5/client/SWGVORDemodSCSettings.h +++ b/swagger/sdrangel/code/qt5/client/SWGVORDemodSCSettings.h @@ -1,6 +1,6 @@ /** * SDRangel - * This is the web REST/JSON API of SDRangel SDR software. SDRangel is an Open Source Qt5/OpenGL 3.0+ (4.3+ in Windows) GUI and server Software Defined Radio and signal analyzer in software. It supports Airspy, BladeRF, HackRF, LimeSDR, PlutoSDR, RTL-SDR, SDRplay RSP1 and FunCube --- Limitations and specifcities: * In SDRangel GUI the first Rx device set cannot be deleted. Conversely the server starts with no device sets and its number of device sets can be reduced to zero by as many calls as necessary to /sdrangel/deviceset with DELETE method. * Preset import and export from/to file is a server only feature. * Device set focus is a GUI only feature. * The following channels are not implemented (status 501 is returned): ATV and DATV demodulators, Channel Analyzer NG, LoRa demodulator * The device settings and report structures contains only the sub-structure corresponding to the device type. The DeviceSettings and DeviceReport structures documented here shows all of them but only one will be or should be present at a time * The channel settings and report structures contains only the sub-structure corresponding to the channel type. The ChannelSettings and ChannelReport structures documented here shows all of them but only one will be or should be present at a time --- + * This is the web REST/JSON API of SDRangel SDR software. SDRangel is an Open Source Qt5/OpenGL 3.0+ (4.3+ in Windows) GUI and server Software Defined Radio and signal analyzer in software. It supports Airspy, BladeRF, HackRF, LimeSDR, PlutoSDR, RTL-SDR, SDRplay RSP1 and FunCube --- Limitations and specifcities: * In SDRangel GUI the first Rx device set cannot be deleted. Conversely the server starts with no device sets and its number of device sets can be reduced to zero by as many calls as necessary to /sdrangel/deviceset with DELETE method. * Preset import and export from/to file is a server only feature. * Device set focus is a GUI only feature. * The following channels are not implemented (status 501 is returned): ATV and DATV demodulators, Channel Analyzer NG, LoRa demodulator * The device settings and report structures contains only the sub-structure corresponding to the device type. The DeviceSettings and DeviceReport structures documented here shows all of them but only one will be or should be present at a time * The channel settings and report structures contains only the sub-structure corresponding to the channel type. The ChannelSettings and ChannelReport structures documented here shows all of them but only one will be or should be present at a time --- * * OpenAPI spec version: 6.0.0 * Contact: f4exb06@gmail.com @@ -13,7 +13,7 @@ /* * SWGVORDemodSCSettings.h * - * VORDemodSC + * VORDemod */ #ifndef SWGVORDemodSCSettings_H_ From 42980850ce0279f56dfee592bc4916c7468e0919 Mon Sep 17 00:00:00 2001 From: f4exb Date: Sun, 1 May 2022 12:15:03 +0200 Subject: [PATCH 087/115] v7: VORDemodSC to VORDemod conversion in swagger. Part of #1223 --- plugins/channelrx/demodvorsc/vordemodsc.cpp | 154 ++++++------- .../channelrx/demodvorsc/vordemodscplugin.h | 2 +- .../demodvorsc/vordemodscwebapiadapter.cpp | 4 +- plugins/feature/vorlocalizer/vorlocalizer.cpp | 42 ++-- sdrbase/resources/webapi/doc/html2/index.html | 18 +- .../doc/swagger/include/ChannelReport.yaml | 4 +- .../doc/swagger/include/ChannelSettings.yaml | 4 +- .../webapi/doc/swagger/include/VORDemod.yaml | 56 ++++- sdrbase/webapi/webapirequestmapper.cpp | 8 +- .../api/swagger/include/ChannelReport.yaml | 4 +- .../api/swagger/include/ChannelSettings.yaml | 4 +- .../{VORDemodSC.yaml => VORDemod.yaml} | 8 +- swagger/sdrangel/code/html2/index.html | 18 +- .../code/qt5/client/SWGChannelReport.cpp | 32 +-- .../code/qt5/client/SWGChannelReport.h | 10 +- .../code/qt5/client/SWGChannelSettings.cpp | 32 +-- .../code/qt5/client/SWGChannelSettings.h | 10 +- .../code/qt5/client/SWGModelFactory.h | 12 +- .../code/qt5/client/SWGVORDemodReport.cpp | 209 ++++++++++++++++++ .../code/qt5/client/SWGVORDemodReport.h | 55 +++++ .../code/qt5/client/SWGVORDemodSettings.cpp | 69 ++++-- .../code/qt5/client/SWGVORDemodSettings.h | 18 +- 22 files changed, 554 insertions(+), 219 deletions(-) rename swagger/sdrangel/api/swagger/include/{VORDemodSC.yaml => VORDemod.yaml} (96%) diff --git a/plugins/channelrx/demodvorsc/vordemodsc.cpp b/plugins/channelrx/demodvorsc/vordemodsc.cpp index ce9720311..33cc8b5ab 100644 --- a/plugins/channelrx/demodvorsc/vordemodsc.cpp +++ b/plugins/channelrx/demodvorsc/vordemodsc.cpp @@ -44,7 +44,7 @@ MESSAGE_CLASS_DEFINITION(VORDemod::MsgConfigureVORDemod, Message) -const char * const VORDemod::m_channelIdURI = "sdrangel.channel.vordemodsc"; +const char * const VORDemod::m_channelIdURI = "sdrangel.channel.vordemod"; const char * const VORDemod::m_channelId = "VORDemod"; VORDemod::VORDemod(DeviceAPI *deviceAPI) : @@ -340,8 +340,8 @@ int VORDemod::webapiSettingsGet( QString& errorMessage) { (void) errorMessage; - response.setVorDemodScSettings(new SWGSDRangel::SWGVORDemodSCSettings()); - response.getVorDemodScSettings()->init(); + response.setVorDemodSettings(new SWGSDRangel::SWGVORDemodSettings()); + response.getVorDemodSettings()->init(); webapiFormatChannelSettings(response, m_settings); return 200; } @@ -377,55 +377,55 @@ void VORDemod::webapiUpdateChannelSettings( SWGSDRangel::SWGChannelSettings& response) { if (channelSettingsKeys.contains("inputFrequencyOffset")) { - settings.m_inputFrequencyOffset = response.getVorDemodScSettings()->getInputFrequencyOffset(); + settings.m_inputFrequencyOffset = response.getVorDemodSettings()->getInputFrequencyOffset(); } if (channelSettingsKeys.contains("navId")) { - settings.m_navId = response.getVorDemodScSettings()->getNavId(); + settings.m_navId = response.getVorDemodSettings()->getNavId(); } if (channelSettingsKeys.contains("audioMute")) { - settings.m_audioMute = response.getVorDemodScSettings()->getAudioMute() != 0; + settings.m_audioMute = response.getVorDemodSettings()->getAudioMute() != 0; } if (channelSettingsKeys.contains("rgbColor")) { - settings.m_rgbColor = response.getVorDemodScSettings()->getRgbColor(); + settings.m_rgbColor = response.getVorDemodSettings()->getRgbColor(); } if (channelSettingsKeys.contains("squelch")) { - settings.m_squelch = response.getVorDemodScSettings()->getSquelch(); + settings.m_squelch = response.getVorDemodSettings()->getSquelch(); } if (channelSettingsKeys.contains("title")) { - settings.m_title = *response.getVorDemodScSettings()->getTitle(); + settings.m_title = *response.getVorDemodSettings()->getTitle(); } if (channelSettingsKeys.contains("volume")) { - settings.m_volume = response.getVorDemodScSettings()->getVolume(); + settings.m_volume = response.getVorDemodSettings()->getVolume(); } if (channelSettingsKeys.contains("audioDeviceName")) { - settings.m_audioDeviceName = *response.getVorDemodScSettings()->getAudioDeviceName(); + settings.m_audioDeviceName = *response.getVorDemodSettings()->getAudioDeviceName(); } if (channelSettingsKeys.contains("streamIndex")) { - settings.m_streamIndex = response.getVorDemodScSettings()->getStreamIndex(); + settings.m_streamIndex = response.getVorDemodSettings()->getStreamIndex(); } if (channelSettingsKeys.contains("useReverseAPI")) { - settings.m_useReverseAPI = response.getVorDemodScSettings()->getUseReverseApi() != 0; + settings.m_useReverseAPI = response.getVorDemodSettings()->getUseReverseApi() != 0; } if (channelSettingsKeys.contains("reverseAPIAddress")) { - settings.m_reverseAPIAddress = *response.getVorDemodScSettings()->getReverseApiAddress(); + settings.m_reverseAPIAddress = *response.getVorDemodSettings()->getReverseApiAddress(); } if (channelSettingsKeys.contains("reverseAPIPort")) { - settings.m_reverseAPIPort = response.getVorDemodScSettings()->getReverseApiPort(); + settings.m_reverseAPIPort = response.getVorDemodSettings()->getReverseApiPort(); } if (channelSettingsKeys.contains("reverseAPIDeviceIndex")) { - settings.m_reverseAPIDeviceIndex = response.getVorDemodScSettings()->getReverseApiDeviceIndex(); + settings.m_reverseAPIDeviceIndex = response.getVorDemodSettings()->getReverseApiDeviceIndex(); } if (channelSettingsKeys.contains("reverseAPIChannelIndex")) { - settings.m_reverseAPIChannelIndex = response.getVorDemodScSettings()->getReverseApiChannelIndex(); + settings.m_reverseAPIChannelIndex = response.getVorDemodSettings()->getReverseApiChannelIndex(); } if (channelSettingsKeys.contains("identThreshold")) { - settings.m_identThreshold = response.getVorDemodScSettings()->getIdentThreshold(); + settings.m_identThreshold = response.getVorDemodSettings()->getIdentThreshold(); } if (settings.m_channelMarker && channelSettingsKeys.contains("channelMarker")) { - settings.m_channelMarker->updateFrom(channelSettingsKeys, response.getVorDemodScSettings()->getChannelMarker()); + settings.m_channelMarker->updateFrom(channelSettingsKeys, response.getVorDemodSettings()->getChannelMarker()); } if (settings.m_rollupState && channelSettingsKeys.contains("rollupState")) { - settings.m_rollupState->updateFrom(channelSettingsKeys, response.getVorDemodScSettings()->getRollupState()); + settings.m_rollupState->updateFrom(channelSettingsKeys, response.getVorDemodSettings()->getRollupState()); } } @@ -434,72 +434,72 @@ int VORDemod::webapiReportGet( QString& errorMessage) { (void) errorMessage; - response.setVorDemodScReport(new SWGSDRangel::SWGVORDemodSCReport()); - response.getVorDemodScReport()->init(); + response.setVorDemodReport(new SWGSDRangel::SWGVORDemodReport()); + response.getVorDemodReport()->init(); webapiFormatChannelReport(response); return 200; } void VORDemod::webapiFormatChannelSettings(SWGSDRangel::SWGChannelSettings& response, const VORDemodSettings& settings) { - response.getVorDemodScSettings()->setInputFrequencyOffset(settings.m_inputFrequencyOffset); - response.getVorDemodScSettings()->setNavId(settings.m_navId); - response.getVorDemodScSettings()->setAudioMute(settings.m_audioMute ? 1 : 0); - response.getVorDemodScSettings()->setRgbColor(settings.m_rgbColor); - response.getVorDemodScSettings()->setSquelch(settings.m_squelch); - response.getVorDemodScSettings()->setVolume(settings.m_volume); + response.getVorDemodSettings()->setInputFrequencyOffset(settings.m_inputFrequencyOffset); + response.getVorDemodSettings()->setNavId(settings.m_navId); + response.getVorDemodSettings()->setAudioMute(settings.m_audioMute ? 1 : 0); + response.getVorDemodSettings()->setRgbColor(settings.m_rgbColor); + response.getVorDemodSettings()->setSquelch(settings.m_squelch); + response.getVorDemodSettings()->setVolume(settings.m_volume); - if (response.getVorDemodScSettings()->getTitle()) { - *response.getVorDemodScSettings()->getTitle() = settings.m_title; + if (response.getVorDemodSettings()->getTitle()) { + *response.getVorDemodSettings()->getTitle() = settings.m_title; } else { - response.getVorDemodScSettings()->setTitle(new QString(settings.m_title)); + response.getVorDemodSettings()->setTitle(new QString(settings.m_title)); } - if (response.getVorDemodScSettings()->getAudioDeviceName()) { - *response.getVorDemodScSettings()->getAudioDeviceName() = settings.m_audioDeviceName; + if (response.getVorDemodSettings()->getAudioDeviceName()) { + *response.getVorDemodSettings()->getAudioDeviceName() = settings.m_audioDeviceName; } else { - response.getVorDemodScSettings()->setAudioDeviceName(new QString(settings.m_audioDeviceName)); + response.getVorDemodSettings()->setAudioDeviceName(new QString(settings.m_audioDeviceName)); } - response.getVorDemodScSettings()->setStreamIndex(settings.m_streamIndex); - response.getVorDemodScSettings()->setUseReverseApi(settings.m_useReverseAPI ? 1 : 0); + response.getVorDemodSettings()->setStreamIndex(settings.m_streamIndex); + response.getVorDemodSettings()->setUseReverseApi(settings.m_useReverseAPI ? 1 : 0); - if (response.getVorDemodScSettings()->getReverseApiAddress()) { - *response.getVorDemodScSettings()->getReverseApiAddress() = settings.m_reverseAPIAddress; + if (response.getVorDemodSettings()->getReverseApiAddress()) { + *response.getVorDemodSettings()->getReverseApiAddress() = settings.m_reverseAPIAddress; } else { - response.getVorDemodScSettings()->setReverseApiAddress(new QString(settings.m_reverseAPIAddress)); + response.getVorDemodSettings()->setReverseApiAddress(new QString(settings.m_reverseAPIAddress)); } - response.getVorDemodScSettings()->setReverseApiPort(settings.m_reverseAPIPort); - response.getVorDemodScSettings()->setReverseApiDeviceIndex(settings.m_reverseAPIDeviceIndex); - response.getVorDemodScSettings()->setReverseApiChannelIndex(settings.m_reverseAPIChannelIndex); - response.getVorDemodScSettings()->setIdentThreshold(settings.m_identThreshold); + response.getVorDemodSettings()->setReverseApiPort(settings.m_reverseAPIPort); + response.getVorDemodSettings()->setReverseApiDeviceIndex(settings.m_reverseAPIDeviceIndex); + response.getVorDemodSettings()->setReverseApiChannelIndex(settings.m_reverseAPIChannelIndex); + response.getVorDemodSettings()->setIdentThreshold(settings.m_identThreshold); if (settings.m_channelMarker) { - if (response.getVorDemodScSettings()->getChannelMarker()) + if (response.getVorDemodSettings()->getChannelMarker()) { - settings.m_channelMarker->formatTo(response.getVorDemodScSettings()->getChannelMarker()); + settings.m_channelMarker->formatTo(response.getVorDemodSettings()->getChannelMarker()); } else { SWGSDRangel::SWGChannelMarker *swgChannelMarker = new SWGSDRangel::SWGChannelMarker(); settings.m_channelMarker->formatTo(swgChannelMarker); - response.getVorDemodScSettings()->setChannelMarker(swgChannelMarker); + response.getVorDemodSettings()->setChannelMarker(swgChannelMarker); } } if (settings.m_rollupState) { - if (response.getVorDemodScSettings()->getRollupState()) + if (response.getVorDemodSettings()->getRollupState()) { - settings.m_rollupState->formatTo(response.getVorDemodScSettings()->getRollupState()); + settings.m_rollupState->formatTo(response.getVorDemodSettings()->getRollupState()); } else { SWGSDRangel::SWGRollupState *swgRollupState = new SWGSDRangel::SWGRollupState(); settings.m_rollupState->formatTo(swgRollupState); - response.getVorDemodScSettings()->setRollupState(swgRollupState); + response.getVorDemodSettings()->setRollupState(swgRollupState); } } } @@ -510,25 +510,25 @@ void VORDemod::webapiFormatChannelReport(SWGSDRangel::SWGChannelReport& response int nbMagsqSamples; getMagSqLevels(magsqAvg, magsqPeak, nbMagsqSamples); - response.getVorDemodScReport()->setChannelPowerDb(CalcDb::dbPower(magsqAvg)); - response.getVorDemodScReport()->setSquelch(m_basebandSink->getSquelchOpen() ? 1 : 0); - response.getVorDemodScReport()->setAudioSampleRate(m_basebandSink->getAudioSampleRate()); - response.getVorDemodScReport()->setNavId(m_settings.m_navId); - response.getVorDemodScReport()->setRadial(m_radial); - response.getVorDemodScReport()->setRefMag(m_refMag); - response.getVorDemodScReport()->setVarMag(m_varMag); + response.getVorDemodReport()->setChannelPowerDb(CalcDb::dbPower(magsqAvg)); + response.getVorDemodReport()->setSquelch(m_basebandSink->getSquelchOpen() ? 1 : 0); + response.getVorDemodReport()->setAudioSampleRate(m_basebandSink->getAudioSampleRate()); + response.getVorDemodReport()->setNavId(m_settings.m_navId); + response.getVorDemodReport()->setRadial(m_radial); + response.getVorDemodReport()->setRefMag(m_refMag); + response.getVorDemodReport()->setVarMag(m_varMag); float refMagDB = std::round(20.0*std::log10(m_refMag)); float varMagDB = std::round(20.0*std::log10(m_varMag)); bool validRefMag = refMagDB > m_settings.m_refThresholdDB; bool validVarMag = varMagDB > m_settings.m_varThresholdDB; - response.getVorDemodScReport()->setValidRadial(validRefMag && validVarMag ? 1 : 0); - response.getVorDemodScReport()->setValidRefMag(validRefMag ? 1 : 0); - response.getVorDemodScReport()->setValidVarMag(validVarMag ? 1 : 0); + response.getVorDemodReport()->setValidRadial(validRefMag && validVarMag ? 1 : 0); + response.getVorDemodReport()->setValidRefMag(validRefMag ? 1 : 0); + response.getVorDemodReport()->setValidVarMag(validVarMag ? 1 : 0); - if (response.getVorDemodScReport()->getMorseIdent()) { - *response.getVorDemodScReport()->getMorseIdent() = m_morseIdent; + if (response.getVorDemodReport()->getMorseIdent()) { + *response.getVorDemodReport()->getMorseIdent() = m_morseIdent; } else { - response.getVorDemodScReport()->setMorseIdent(new QString(m_morseIdent)); + response.getVorDemodReport()->setMorseIdent(new QString(m_morseIdent)); } } @@ -593,7 +593,7 @@ void VORDemod::sendChannelReport(QList& messagePipes) SWGSDRangel::SWGChannelReport *swgChannelReport = new SWGSDRangel::SWGChannelReport(); swgChannelReport->setDirection(0); swgChannelReport->setChannelType(new QString(m_channelId)); - swgChannelReport->setVorDemodScReport(new SWGSDRangel::SWGVORDemodSCReport()); + swgChannelReport->setVorDemodReport(new SWGSDRangel::SWGVORDemodReport()); webapiFormatChannelReport(*swgChannelReport); MainCore::MsgChannelReport *msg = MainCore::MsgChannelReport::create(this, swgChannelReport); messageQueue->push(msg); @@ -612,54 +612,54 @@ void VORDemod::webapiFormatChannelSettings( swgChannelSettings->setOriginatorChannelIndex(getIndexInDeviceSet()); swgChannelSettings->setOriginatorDeviceSetIndex(getDeviceSetIndex()); swgChannelSettings->setChannelType(new QString("VORDemod")); - swgChannelSettings->setVorDemodScSettings(new SWGSDRangel::SWGVORDemodSCSettings()); - SWGSDRangel::SWGVORDemodSCSettings *swgVORDemodSCSettings = swgChannelSettings->getVorDemodScSettings(); + swgChannelSettings->setVorDemodSettings(new SWGSDRangel::SWGVORDemodSettings()); + SWGSDRangel::SWGVORDemodSettings *swgVORDemodSettings = swgChannelSettings->getVorDemodSettings(); // transfer data that has been modified. When force is on transfer all data except reverse API data if (channelSettingsKeys.contains("inputFrequencyOffset") || force) { - swgVORDemodSCSettings->setInputFrequencyOffset(settings.m_inputFrequencyOffset); + swgVORDemodSettings->setInputFrequencyOffset(settings.m_inputFrequencyOffset); } if (channelSettingsKeys.contains("navId") || force) { - swgVORDemodSCSettings->setNavId(settings.m_navId); + swgVORDemodSettings->setNavId(settings.m_navId); } if (channelSettingsKeys.contains("audioMute") || force) { - swgVORDemodSCSettings->setAudioMute(settings.m_audioMute ? 1 : 0); + swgVORDemodSettings->setAudioMute(settings.m_audioMute ? 1 : 0); } if (channelSettingsKeys.contains("rgbColor") || force) { - swgVORDemodSCSettings->setRgbColor(settings.m_rgbColor); + swgVORDemodSettings->setRgbColor(settings.m_rgbColor); } if (channelSettingsKeys.contains("squelch") || force) { - swgVORDemodSCSettings->setSquelch(settings.m_squelch); + swgVORDemodSettings->setSquelch(settings.m_squelch); } if (channelSettingsKeys.contains("title") || force) { - swgVORDemodSCSettings->setTitle(new QString(settings.m_title)); + swgVORDemodSettings->setTitle(new QString(settings.m_title)); } if (channelSettingsKeys.contains("volume") || force) { - swgVORDemodSCSettings->setVolume(settings.m_volume); + swgVORDemodSettings->setVolume(settings.m_volume); } if (channelSettingsKeys.contains("audioDeviceName") || force) { - swgVORDemodSCSettings->setAudioDeviceName(new QString(settings.m_audioDeviceName)); + swgVORDemodSettings->setAudioDeviceName(new QString(settings.m_audioDeviceName)); } if (channelSettingsKeys.contains("streamIndex") || force) { - swgVORDemodSCSettings->setStreamIndex(settings.m_streamIndex); + swgVORDemodSettings->setStreamIndex(settings.m_streamIndex); } if (channelSettingsKeys.contains("identThreshold") || force) { - swgVORDemodSCSettings->setAudioMute(settings.m_identThreshold); + swgVORDemodSettings->setAudioMute(settings.m_identThreshold); } if (settings.m_channelMarker && (channelSettingsKeys.contains("channelMarker") || force)) { SWGSDRangel::SWGChannelMarker *swgChannelMarker = new SWGSDRangel::SWGChannelMarker(); settings.m_channelMarker->formatTo(swgChannelMarker); - swgVORDemodSCSettings->setChannelMarker(swgChannelMarker); + swgVORDemodSettings->setChannelMarker(swgChannelMarker); } if (settings.m_rollupState && (channelSettingsKeys.contains("rollupState") || force)) { SWGSDRangel::SWGRollupState *swgRollupState = new SWGSDRangel::SWGRollupState(); settings.m_rollupState->formatTo(swgRollupState); - swgVORDemodSCSettings->setRollupState(swgRollupState); + swgVORDemodSettings->setRollupState(swgRollupState); } } diff --git a/plugins/channelrx/demodvorsc/vordemodscplugin.h b/plugins/channelrx/demodvorsc/vordemodscplugin.h index 122af6fab..1519a9067 100644 --- a/plugins/channelrx/demodvorsc/vordemodscplugin.h +++ b/plugins/channelrx/demodvorsc/vordemodscplugin.h @@ -28,7 +28,7 @@ class BasebandSampleSink; class VORDemodPlugin : public QObject, PluginInterface { Q_OBJECT Q_INTERFACES(PluginInterface) - Q_PLUGIN_METADATA(IID "sdrangel.channel.vordemodsc") + Q_PLUGIN_METADATA(IID "sdrangel.channel.vordemod") public: explicit VORDemodPlugin(QObject* parent = NULL); diff --git a/plugins/channelrx/demodvorsc/vordemodscwebapiadapter.cpp b/plugins/channelrx/demodvorsc/vordemodscwebapiadapter.cpp index 117654b64..864984ad2 100644 --- a/plugins/channelrx/demodvorsc/vordemodscwebapiadapter.cpp +++ b/plugins/channelrx/demodvorsc/vordemodscwebapiadapter.cpp @@ -31,8 +31,8 @@ int VORDemodSCWebAPIAdapter::webapiSettingsGet( QString& errorMessage) { (void) errorMessage; - response.setVorDemodScSettings(new SWGSDRangel::SWGVORDemodSCSettings()); - response.getVorDemodScSettings()->init(); + response.setVorDemodSettings(new SWGSDRangel::SWGVORDemodSettings()); + response.getVorDemodSettings()->init(); VORDemod::webapiFormatChannelSettings(response, m_settings); return 200; diff --git a/plugins/feature/vorlocalizer/vorlocalizer.cpp b/plugins/feature/vorlocalizer/vorlocalizer.cpp index 49f200264..30d8d5d58 100644 --- a/plugins/feature/vorlocalizer/vorlocalizer.cpp +++ b/plugins/feature/vorlocalizer/vorlocalizer.cpp @@ -154,8 +154,8 @@ bool VORLocalizer::handleMessage(const Message& cmd) if (*channelType == "VORDemod") { - SWGSDRangel::SWGVORDemodSCReport *swgVORDemodSCReport = swgChannelReport->getVorDemodScReport(); - int navId = swgVORDemodSCReport->getNavId(); + SWGSDRangel::SWGVORDemodReport *swgVORDemodReport = swgChannelReport->getVorDemodReport(); + int navId = swgVORDemodReport->getNavId(); if (navId < 0) { // disregard message for unallocated channels return true; @@ -172,38 +172,38 @@ bool VORLocalizer::handleMessage(const Message& cmd) if (m_vorChannelReports.contains(navId)) { - m_vorChannelReports[navId].m_radial = swgVORDemodSCReport->getRadial(); - m_vorChannelReports[navId].m_refMag = swgVORDemodSCReport->getRefMag(); - m_vorChannelReports[navId].m_varMag = swgVORDemodSCReport->getVarMag(); - m_vorChannelReports[navId].m_validRadial = swgVORDemodSCReport->getValidRadial() != 0; - m_vorChannelReports[navId].m_validRefMag = swgVORDemodSCReport->getValidRefMag() != 0; - m_vorChannelReports[navId].m_validVarMag = swgVORDemodSCReport->getValidVarMag() != 0; - m_vorChannelReports[navId].m_morseIdent = *swgVORDemodSCReport->getMorseIdent(); + m_vorChannelReports[navId].m_radial = swgVORDemodReport->getRadial(); + m_vorChannelReports[navId].m_refMag = swgVORDemodReport->getRefMag(); + m_vorChannelReports[navId].m_varMag = swgVORDemodReport->getVarMag(); + m_vorChannelReports[navId].m_validRadial = swgVORDemodReport->getValidRadial() != 0; + m_vorChannelReports[navId].m_validRefMag = swgVORDemodReport->getValidRefMag() != 0; + m_vorChannelReports[navId].m_validVarMag = swgVORDemodReport->getValidVarMag() != 0; + m_vorChannelReports[navId].m_morseIdent = *swgVORDemodReport->getMorseIdent(); } else { m_vorChannelReports[navId] = VORChannelReport{ - swgVORDemodSCReport->getRadial(), - swgVORDemodSCReport->getRefMag(), - swgVORDemodSCReport->getVarMag(), + swgVORDemodReport->getRadial(), + swgVORDemodReport->getRefMag(), + swgVORDemodReport->getVarMag(), AverageUtil(), AverageUtil(), AverageUtil(), - swgVORDemodSCReport->getValidRadial() != 0, - swgVORDemodSCReport->getValidRefMag() != 0, - swgVORDemodSCReport->getValidVarMag() != 0, - *swgVORDemodSCReport->getMorseIdent() + swgVORDemodReport->getValidRadial() != 0, + swgVORDemodReport->getValidRefMag() != 0, + swgVORDemodReport->getValidVarMag() != 0, + *swgVORDemodReport->getMorseIdent() }; } if (m_vorChannelReports[navId].m_validRadial) { - m_vorChannelReports[navId].m_radialAvg(swgVORDemodSCReport->getRadial()); + m_vorChannelReports[navId].m_radialAvg(swgVORDemodReport->getRadial()); } if (m_vorChannelReports[navId].m_validRefMag) { - m_vorChannelReports[navId].m_refMagAvg(swgVORDemodSCReport->getRefMag()); + m_vorChannelReports[navId].m_refMagAvg(swgVORDemodReport->getRefMag()); } if (m_vorChannelReports[navId].m_validVarMag) { - m_vorChannelReports[navId].m_varMagAvg(swgVORDemodSCReport->getVarMag()); + m_vorChannelReports[navId].m_varMagAvg(swgVORDemodReport->getVarMag()); } if (getMessageQueueToGUI()) @@ -373,7 +373,7 @@ void VORLocalizer::updateChannels() { ChannelAPI *channel = deviceSet->getChannelAt(chi); - if (channel->getURI() == "sdrangel.channel.vordemodsc") + if (channel->getURI() == "sdrangel.channel.vordemod") { if (!m_availableChannels.contains(channel)) { @@ -667,7 +667,7 @@ void VORLocalizer::handleChannelAdded(int deviceSetIndex, ChannelAPI *channel) DeviceSet *deviceSet = MainCore::instance()->getDeviceSets()[deviceSetIndex]; DSPDeviceSourceEngine *deviceSourceEngine = deviceSet->m_deviceSourceEngine; - if (deviceSourceEngine && (channel->getURI() == "sdrangel.channel.vordemodsc")) + if (deviceSourceEngine && (channel->getURI() == "sdrangel.channel.vordemod")) { DeviceSampleSource *deviceSource = deviceSourceEngine->getSource(); quint64 deviceCenterFrequency = deviceSource->getCenterFrequency(); diff --git a/sdrbase/resources/webapi/doc/html2/index.html b/sdrbase/resources/webapi/doc/html2/index.html index 72adae4bc..6d00be163 100644 --- a/sdrbase/resources/webapi/doc/html2/index.html +++ b/sdrbase/resources/webapi/doc/html2/index.html @@ -3380,8 +3380,8 @@ margin-bottom: 20px; "VORDemodMCReport" : { "$ref" : "#/definitions/VORDemodMCReport" }, - "VORDemodSCReport" : { - "$ref" : "#/definitions/VORDemodSCReport" + "VORDemodReport" : { + "$ref" : "#/definitions/VORDemodReport" }, "WFMDemodReport" : { "$ref" : "#/definitions/WFMDemodReport" @@ -3541,8 +3541,8 @@ margin-bottom: 20px; "VORDemodMCSettings" : { "$ref" : "#/definitions/VORDemodMCSettings" }, - "VORDemodSCSettings" : { - "$ref" : "#/definitions/VORDemodSCSettings" + "VORDemodSettings" : { + "$ref" : "#/definitions/VORDemodSettings" }, "WFMDemodSettings" : { "$ref" : "#/definitions/WFMDemodSettings" @@ -13693,7 +13693,7 @@ margin-bottom: 20px; }, "description" : "VORDemodMC" }; - defs.VORDemodSCReport = { + defs.VORDemodReport = { "properties" : { "channelPowerDB" : { "type" : "number", @@ -13747,9 +13747,9 @@ margin-bottom: 20px; "description" : "current identification morse code transcript" } }, - "description" : "VORDemodSC" + "description" : "VORDemod" }; - defs.VORDemodSCSettings = { + defs.VORDemodSettings = { "properties" : { "inputFrequencyOffset" : { "type" : "integer", @@ -13811,7 +13811,7 @@ margin-bottom: 20px; "$ref" : "#/definitions/RollupState" } }, - "description" : "VORDemodSC" + "description" : "VORDemod" }; defs.VORLocalizerActions = { "properties" : { @@ -52552,7 +52552,7 @@ except ApiException as e:
    - Generated 2022-05-01T10:49:23.866+02:00 + Generated 2022-05-01T11:46:55.368+02:00
    diff --git a/sdrbase/resources/webapi/doc/swagger/include/ChannelReport.yaml b/sdrbase/resources/webapi/doc/swagger/include/ChannelReport.yaml index 37d2961e2..ffe69ddeb 100644 --- a/sdrbase/resources/webapi/doc/swagger/include/ChannelReport.yaml +++ b/sdrbase/resources/webapi/doc/swagger/include/ChannelReport.yaml @@ -79,8 +79,8 @@ ChannelReport: $ref: "/doc/swagger/include/UDPSink.yaml#/UDPSinkReport" VORDemodMCReport: $ref: "/doc/swagger/include/VORDemodMC.yaml#/VORDemodMCReport" - VORDemodSCReport: - $ref: "/doc/swagger/include/VORDemodSC.yaml#/VORDemodSCReport" + VORDemodReport: + $ref: "/doc/swagger/include/VORDemod.yaml#/VORDemodReport" WFMDemodReport: $ref: "/doc/swagger/include/WFMDemod.yaml#/WFMDemodReport" WFMModReport: diff --git a/sdrbase/resources/webapi/doc/swagger/include/ChannelSettings.yaml b/sdrbase/resources/webapi/doc/swagger/include/ChannelSettings.yaml index ddd206777..6bf8bb473 100644 --- a/sdrbase/resources/webapi/doc/swagger/include/ChannelSettings.yaml +++ b/sdrbase/resources/webapi/doc/swagger/include/ChannelSettings.yaml @@ -103,8 +103,8 @@ ChannelSettings: $ref: "/doc/swagger/include/UDPSink.yaml#/UDPSinkSettings" VORDemodMCSettings: $ref: "/doc/swagger/include/VORDemodMC.yaml#/VORDemodMCSettings" - VORDemodSCSettings: - $ref: "/doc/swagger/include/VORDemodSC.yaml#/VORDemodSCSettings" + VORDemodSettings: + $ref: "/doc/swagger/include/VORDemod.yaml#/VORDemodSettings" WFMDemodSettings: $ref: "/doc/swagger/include/WFMDemod.yaml#/WFMDemodSettings" WFMModSettings: diff --git a/sdrbase/resources/webapi/doc/swagger/include/VORDemod.yaml b/sdrbase/resources/webapi/doc/swagger/include/VORDemod.yaml index 6dbc27998..5ca22187b 100644 --- a/sdrbase/resources/webapi/doc/swagger/include/VORDemod.yaml +++ b/sdrbase/resources/webapi/doc/swagger/include/VORDemod.yaml @@ -1,6 +1,12 @@ -VORDemodMCSettings: - description: VORDemodMC +VORDemodSettings: + description: VORDemod properties: + inputFrequencyOffset: + type: integer + format: int64 + navId: + description: VOR unique identifier when set by VOR localizer feature + type: integer squelch: description: power squelch threshold in decibels type: number @@ -33,16 +39,13 @@ VORDemodMCSettings: identThreshold: description: Morse code ident threshold (linear SNR) type: integer - magDecAdjust: - description: Adjust radial lines on map for magnetic declination of VOR - type: integer channelMarker: $ref: "/doc/swagger/include/ChannelMarker.yaml#/ChannelMarker" rollupState: $ref: "/doc/swagger/include/RollupState.yaml#/RollupState" -VORDemodMCReport: - description: VORDemodMC +VORDemodReport: + description: VORDemod properties: channelPowerDB: description: power received in channel (dB) @@ -53,3 +56,42 @@ VORDemodMCReport: type: integer audioSampleRate: type: integer + volume: + type: number + format: float + navId: + description: VOR unique identifier when set by VOR localizer feature + type: integer + radial: + description: current detected radial (degrees) + type: number + format: float + refMag: + description: current reference signal magnitude (dB) + type: number + format: float + varMag: + description: current variable signal magnitude (dB) + type: number + format: float + validRadial: + type: integer + description: > + Radial validity estimation + * 0 - Radial is invalid + * 1 - Radial is valid + validRefMag: + type: integer + description: > + Reference signal magnitude validity + * 0 - Magnitude below threshold + * 1 - Magnitude above threshold + validVarMag: + type: integer + description: > + Variable signal magnitude validity + * 0 - Magnitude below threshold + * 1 - Magnitude above threshold + morseIdent: + description: current identification morse code transcript + type: string diff --git a/sdrbase/webapi/webapirequestmapper.cpp b/sdrbase/webapi/webapirequestmapper.cpp index 206f09281..05694994b 100644 --- a/sdrbase/webapi/webapirequestmapper.cpp +++ b/sdrbase/webapi/webapirequestmapper.cpp @@ -4364,8 +4364,8 @@ bool WebAPIRequestMapper::getChannelSettings( } else if (channelSettingsKey == "VORDemodSettings") { - channelSettings->setVorDemodScSettings(new SWGSDRangel::SWGVORDemodSCSettings()); - channelSettings->getVorDemodScSettings()->fromJsonObject(settingsJsonObject); + channelSettings->setVorDemodSettings(new SWGSDRangel::SWGVORDemodSettings()); + channelSettings->getVorDemodSettings()->fromJsonObject(settingsJsonObject); } else if (channelSettingsKey == "WFMDemodSettings") { @@ -5083,7 +5083,7 @@ void WebAPIRequestMapper::resetChannelSettings(SWGSDRangel::SWGChannelSettings& channelSettings.setUdpSourceSettings(nullptr); channelSettings.setUdpSinkSettings(nullptr); channelSettings.setVorDemodMcSettings(nullptr); - channelSettings.setVorDemodScSettings(nullptr); + channelSettings.setVorDemodSettings(nullptr); channelSettings.setWfmDemodSettings(nullptr); channelSettings.setWfmModSettings(nullptr); } @@ -5115,7 +5115,7 @@ void WebAPIRequestMapper::resetChannelReport(SWGSDRangel::SWGChannelReport& chan channelReport.setUdpSourceReport(nullptr); channelReport.setUdpSinkReport(nullptr); channelReport.setVorDemodMcReport(nullptr); - channelReport.setVorDemodScReport(nullptr); + channelReport.setVorDemodReport(nullptr); channelReport.setWfmDemodReport(nullptr); channelReport.setWfmModReport(nullptr); } diff --git a/swagger/sdrangel/api/swagger/include/ChannelReport.yaml b/swagger/sdrangel/api/swagger/include/ChannelReport.yaml index a8585f123..f4fc3ca98 100644 --- a/swagger/sdrangel/api/swagger/include/ChannelReport.yaml +++ b/swagger/sdrangel/api/swagger/include/ChannelReport.yaml @@ -79,8 +79,8 @@ ChannelReport: $ref: "http://swgserver:8081/api/swagger/include/UDPSink.yaml#/UDPSinkReport" VORDemodMCReport: $ref: "http://swgserver:8081/api/swagger/include/VORDemodMC.yaml#/VORDemodMCReport" - VORDemodSCReport: - $ref: "http://swgserver:8081/api/swagger/include/VORDemodSC.yaml#/VORDemodSCReport" + VORDemodReport: + $ref: "http://swgserver:8081/api/swagger/include/VORDemod.yaml#/VORDemodReport" WFMDemodReport: $ref: "http://swgserver:8081/api/swagger/include/WFMDemod.yaml#/WFMDemodReport" WFMModReport: diff --git a/swagger/sdrangel/api/swagger/include/ChannelSettings.yaml b/swagger/sdrangel/api/swagger/include/ChannelSettings.yaml index 59fd529a4..0f9648469 100644 --- a/swagger/sdrangel/api/swagger/include/ChannelSettings.yaml +++ b/swagger/sdrangel/api/swagger/include/ChannelSettings.yaml @@ -103,8 +103,8 @@ ChannelSettings: $ref: "http://swgserver:8081/api/swagger/include/UDPSink.yaml#/UDPSinkSettings" VORDemodMCSettings: $ref: "http://swgserver:8081/api/swagger/include/VORDemodMC.yaml#/VORDemodMCSettings" - VORDemodSCSettings: - $ref: "http://swgserver:8081/api/swagger/include/VORDemodSC.yaml#/VORDemodSCSettings" + VORDemodSettings: + $ref: "http://swgserver:8081/api/swagger/include/VORDemod.yaml#/VORDemodSettings" WFMDemodSettings: $ref: "http://swgserver:8081/api/swagger/include/WFMDemod.yaml#/WFMDemodSettings" WFMModSettings: diff --git a/swagger/sdrangel/api/swagger/include/VORDemodSC.yaml b/swagger/sdrangel/api/swagger/include/VORDemod.yaml similarity index 96% rename from swagger/sdrangel/api/swagger/include/VORDemodSC.yaml rename to swagger/sdrangel/api/swagger/include/VORDemod.yaml index 6b675ec74..2c40fce37 100644 --- a/swagger/sdrangel/api/swagger/include/VORDemodSC.yaml +++ b/swagger/sdrangel/api/swagger/include/VORDemod.yaml @@ -1,5 +1,5 @@ -VORDemodSCSettings: - description: VORDemodSC +VORDemodSettings: + description: VORDemod properties: inputFrequencyOffset: type: integer @@ -44,8 +44,8 @@ VORDemodSCSettings: rollupState: $ref: "http://swgserver:8081/api/swagger/include/RollupState.yaml#/RollupState" -VORDemodSCReport: - description: VORDemodSC +VORDemodReport: + description: VORDemod properties: channelPowerDB: description: power received in channel (dB) diff --git a/swagger/sdrangel/code/html2/index.html b/swagger/sdrangel/code/html2/index.html index 72adae4bc..6d00be163 100644 --- a/swagger/sdrangel/code/html2/index.html +++ b/swagger/sdrangel/code/html2/index.html @@ -3380,8 +3380,8 @@ margin-bottom: 20px; "VORDemodMCReport" : { "$ref" : "#/definitions/VORDemodMCReport" }, - "VORDemodSCReport" : { - "$ref" : "#/definitions/VORDemodSCReport" + "VORDemodReport" : { + "$ref" : "#/definitions/VORDemodReport" }, "WFMDemodReport" : { "$ref" : "#/definitions/WFMDemodReport" @@ -3541,8 +3541,8 @@ margin-bottom: 20px; "VORDemodMCSettings" : { "$ref" : "#/definitions/VORDemodMCSettings" }, - "VORDemodSCSettings" : { - "$ref" : "#/definitions/VORDemodSCSettings" + "VORDemodSettings" : { + "$ref" : "#/definitions/VORDemodSettings" }, "WFMDemodSettings" : { "$ref" : "#/definitions/WFMDemodSettings" @@ -13693,7 +13693,7 @@ margin-bottom: 20px; }, "description" : "VORDemodMC" }; - defs.VORDemodSCReport = { + defs.VORDemodReport = { "properties" : { "channelPowerDB" : { "type" : "number", @@ -13747,9 +13747,9 @@ margin-bottom: 20px; "description" : "current identification morse code transcript" } }, - "description" : "VORDemodSC" + "description" : "VORDemod" }; - defs.VORDemodSCSettings = { + defs.VORDemodSettings = { "properties" : { "inputFrequencyOffset" : { "type" : "integer", @@ -13811,7 +13811,7 @@ margin-bottom: 20px; "$ref" : "#/definitions/RollupState" } }, - "description" : "VORDemodSC" + "description" : "VORDemod" }; defs.VORLocalizerActions = { "properties" : { @@ -52552,7 +52552,7 @@ except ApiException as e:
    - Generated 2022-05-01T10:49:23.866+02:00 + Generated 2022-05-01T11:46:55.368+02:00
    diff --git a/swagger/sdrangel/code/qt5/client/SWGChannelReport.cpp b/swagger/sdrangel/code/qt5/client/SWGChannelReport.cpp index aa0eaf3bc..e14c302eb 100644 --- a/swagger/sdrangel/code/qt5/client/SWGChannelReport.cpp +++ b/swagger/sdrangel/code/qt5/client/SWGChannelReport.cpp @@ -100,8 +100,8 @@ SWGChannelReport::SWGChannelReport() { m_udp_sink_report_isSet = false; vor_demod_mc_report = nullptr; m_vor_demod_mc_report_isSet = false; - vor_demod_sc_report = nullptr; - m_vor_demod_sc_report_isSet = false; + vor_demod_report = nullptr; + m_vor_demod_report_isSet = false; wfm_demod_report = nullptr; m_wfm_demod_report_isSet = false; wfm_mod_report = nullptr; @@ -186,8 +186,8 @@ SWGChannelReport::init() { m_udp_sink_report_isSet = false; vor_demod_mc_report = new SWGVORDemodMCReport(); m_vor_demod_mc_report_isSet = false; - vor_demod_sc_report = new SWGVORDemodSCReport(); - m_vor_demod_sc_report_isSet = false; + vor_demod_report = new SWGVORDemodReport(); + m_vor_demod_report_isSet = false; wfm_demod_report = new SWGWFMDemodReport(); m_wfm_demod_report_isSet = false; wfm_mod_report = new SWGWFMModReport(); @@ -302,8 +302,8 @@ SWGChannelReport::cleanup() { if(vor_demod_mc_report != nullptr) { delete vor_demod_mc_report; } - if(vor_demod_sc_report != nullptr) { - delete vor_demod_sc_report; + if(vor_demod_report != nullptr) { + delete vor_demod_report; } if(wfm_demod_report != nullptr) { delete wfm_demod_report; @@ -396,7 +396,7 @@ SWGChannelReport::fromJsonObject(QJsonObject &pJson) { ::SWGSDRangel::setValue(&vor_demod_mc_report, pJson["VORDemodMCReport"], "SWGVORDemodMCReport", "SWGVORDemodMCReport"); - ::SWGSDRangel::setValue(&vor_demod_sc_report, pJson["VORDemodSCReport"], "SWGVORDemodSCReport", "SWGVORDemodSCReport"); + ::SWGSDRangel::setValue(&vor_demod_report, pJson["VORDemodReport"], "SWGVORDemodReport", "SWGVORDemodReport"); ::SWGSDRangel::setValue(&wfm_demod_report, pJson["WFMDemodReport"], "SWGWFMDemodReport", "SWGWFMDemodReport"); @@ -526,8 +526,8 @@ SWGChannelReport::asJsonObject() { if((vor_demod_mc_report != nullptr) && (vor_demod_mc_report->isSet())){ toJsonValue(QString("VORDemodMCReport"), vor_demod_mc_report, obj, QString("SWGVORDemodMCReport")); } - if((vor_demod_sc_report != nullptr) && (vor_demod_sc_report->isSet())){ - toJsonValue(QString("VORDemodSCReport"), vor_demod_sc_report, obj, QString("SWGVORDemodSCReport")); + if((vor_demod_report != nullptr) && (vor_demod_report->isSet())){ + toJsonValue(QString("VORDemodReport"), vor_demod_report, obj, QString("SWGVORDemodReport")); } if((wfm_demod_report != nullptr) && (wfm_demod_report->isSet())){ toJsonValue(QString("WFMDemodReport"), wfm_demod_report, obj, QString("SWGWFMDemodReport")); @@ -899,14 +899,14 @@ SWGChannelReport::setVorDemodMcReport(SWGVORDemodMCReport* vor_demod_mc_report) this->m_vor_demod_mc_report_isSet = true; } -SWGVORDemodSCReport* -SWGChannelReport::getVorDemodScReport() { - return vor_demod_sc_report; +SWGVORDemodReport* +SWGChannelReport::getVorDemodReport() { + return vor_demod_report; } void -SWGChannelReport::setVorDemodScReport(SWGVORDemodSCReport* vor_demod_sc_report) { - this->vor_demod_sc_report = vor_demod_sc_report; - this->m_vor_demod_sc_report_isSet = true; +SWGChannelReport::setVorDemodReport(SWGVORDemodReport* vor_demod_report) { + this->vor_demod_report = vor_demod_report; + this->m_vor_demod_report_isSet = true; } SWGWFMDemodReport* @@ -1042,7 +1042,7 @@ SWGChannelReport::isSet(){ if(vor_demod_mc_report && vor_demod_mc_report->isSet()){ isObjectUpdated = true; break; } - if(vor_demod_sc_report && vor_demod_sc_report->isSet()){ + if(vor_demod_report && vor_demod_report->isSet()){ isObjectUpdated = true; break; } if(wfm_demod_report && wfm_demod_report->isSet()){ diff --git a/swagger/sdrangel/code/qt5/client/SWGChannelReport.h b/swagger/sdrangel/code/qt5/client/SWGChannelReport.h index 87de98329..023ec6d43 100644 --- a/swagger/sdrangel/code/qt5/client/SWGChannelReport.h +++ b/swagger/sdrangel/code/qt5/client/SWGChannelReport.h @@ -56,7 +56,7 @@ #include "SWGUDPSinkReport.h" #include "SWGUDPSourceReport.h" #include "SWGVORDemodMCReport.h" -#include "SWGVORDemodSCReport.h" +#include "SWGVORDemodReport.h" #include "SWGWFMDemodReport.h" #include "SWGWFMModReport.h" #include @@ -187,8 +187,8 @@ public: SWGVORDemodMCReport* getVorDemodMcReport(); void setVorDemodMcReport(SWGVORDemodMCReport* vor_demod_mc_report); - SWGVORDemodSCReport* getVorDemodScReport(); - void setVorDemodScReport(SWGVORDemodSCReport* vor_demod_sc_report); + SWGVORDemodReport* getVorDemodReport(); + void setVorDemodReport(SWGVORDemodReport* vor_demod_report); SWGWFMDemodReport* getWfmDemodReport(); void setWfmDemodReport(SWGWFMDemodReport* wfm_demod_report); @@ -308,8 +308,8 @@ private: SWGVORDemodMCReport* vor_demod_mc_report; bool m_vor_demod_mc_report_isSet; - SWGVORDemodSCReport* vor_demod_sc_report; - bool m_vor_demod_sc_report_isSet; + SWGVORDemodReport* vor_demod_report; + bool m_vor_demod_report_isSet; SWGWFMDemodReport* wfm_demod_report; bool m_wfm_demod_report_isSet; diff --git a/swagger/sdrangel/code/qt5/client/SWGChannelSettings.cpp b/swagger/sdrangel/code/qt5/client/SWGChannelSettings.cpp index e396533bf..abc0dcd94 100644 --- a/swagger/sdrangel/code/qt5/client/SWGChannelSettings.cpp +++ b/swagger/sdrangel/code/qt5/client/SWGChannelSettings.cpp @@ -122,8 +122,8 @@ SWGChannelSettings::SWGChannelSettings() { m_udp_sink_settings_isSet = false; vor_demod_mc_settings = nullptr; m_vor_demod_mc_settings_isSet = false; - vor_demod_sc_settings = nullptr; - m_vor_demod_sc_settings_isSet = false; + vor_demod_settings = nullptr; + m_vor_demod_settings_isSet = false; wfm_demod_settings = nullptr; m_wfm_demod_settings_isSet = false; wfm_mod_settings = nullptr; @@ -230,8 +230,8 @@ SWGChannelSettings::init() { m_udp_sink_settings_isSet = false; vor_demod_mc_settings = new SWGVORDemodMCSettings(); m_vor_demod_mc_settings_isSet = false; - vor_demod_sc_settings = new SWGVORDemodSCSettings(); - m_vor_demod_sc_settings_isSet = false; + vor_demod_settings = new SWGVORDemodSettings(); + m_vor_demod_settings_isSet = false; wfm_demod_settings = new SWGWFMDemodSettings(); m_wfm_demod_settings_isSet = false; wfm_mod_settings = new SWGWFMModSettings(); @@ -375,8 +375,8 @@ SWGChannelSettings::cleanup() { if(vor_demod_mc_settings != nullptr) { delete vor_demod_mc_settings; } - if(vor_demod_sc_settings != nullptr) { - delete vor_demod_sc_settings; + if(vor_demod_settings != nullptr) { + delete vor_demod_settings; } if(wfm_demod_settings != nullptr) { delete wfm_demod_settings; @@ -491,7 +491,7 @@ SWGChannelSettings::fromJsonObject(QJsonObject &pJson) { ::SWGSDRangel::setValue(&vor_demod_mc_settings, pJson["VORDemodMCSettings"], "SWGVORDemodMCSettings", "SWGVORDemodMCSettings"); - ::SWGSDRangel::setValue(&vor_demod_sc_settings, pJson["VORDemodSCSettings"], "SWGVORDemodSCSettings", "SWGVORDemodSCSettings"); + ::SWGSDRangel::setValue(&vor_demod_settings, pJson["VORDemodSettings"], "SWGVORDemodSettings", "SWGVORDemodSettings"); ::SWGSDRangel::setValue(&wfm_demod_settings, pJson["WFMDemodSettings"], "SWGWFMDemodSettings", "SWGWFMDemodSettings"); @@ -654,8 +654,8 @@ SWGChannelSettings::asJsonObject() { if((vor_demod_mc_settings != nullptr) && (vor_demod_mc_settings->isSet())){ toJsonValue(QString("VORDemodMCSettings"), vor_demod_mc_settings, obj, QString("SWGVORDemodMCSettings")); } - if((vor_demod_sc_settings != nullptr) && (vor_demod_sc_settings->isSet())){ - toJsonValue(QString("VORDemodSCSettings"), vor_demod_sc_settings, obj, QString("SWGVORDemodSCSettings")); + if((vor_demod_settings != nullptr) && (vor_demod_settings->isSet())){ + toJsonValue(QString("VORDemodSettings"), vor_demod_settings, obj, QString("SWGVORDemodSettings")); } if((wfm_demod_settings != nullptr) && (wfm_demod_settings->isSet())){ toJsonValue(QString("WFMDemodSettings"), wfm_demod_settings, obj, QString("SWGWFMDemodSettings")); @@ -1137,14 +1137,14 @@ SWGChannelSettings::setVorDemodMcSettings(SWGVORDemodMCSettings* vor_demod_mc_se this->m_vor_demod_mc_settings_isSet = true; } -SWGVORDemodSCSettings* -SWGChannelSettings::getVorDemodScSettings() { - return vor_demod_sc_settings; +SWGVORDemodSettings* +SWGChannelSettings::getVorDemodSettings() { + return vor_demod_settings; } void -SWGChannelSettings::setVorDemodScSettings(SWGVORDemodSCSettings* vor_demod_sc_settings) { - this->vor_demod_sc_settings = vor_demod_sc_settings; - this->m_vor_demod_sc_settings_isSet = true; +SWGChannelSettings::setVorDemodSettings(SWGVORDemodSettings* vor_demod_settings) { + this->vor_demod_settings = vor_demod_settings; + this->m_vor_demod_settings_isSet = true; } SWGWFMDemodSettings* @@ -1313,7 +1313,7 @@ SWGChannelSettings::isSet(){ if(vor_demod_mc_settings && vor_demod_mc_settings->isSet()){ isObjectUpdated = true; break; } - if(vor_demod_sc_settings && vor_demod_sc_settings->isSet()){ + if(vor_demod_settings && vor_demod_settings->isSet()){ isObjectUpdated = true; break; } if(wfm_demod_settings && wfm_demod_settings->isSet()){ diff --git a/swagger/sdrangel/code/qt5/client/SWGChannelSettings.h b/swagger/sdrangel/code/qt5/client/SWGChannelSettings.h index 8f761dd7e..a87e34202 100644 --- a/swagger/sdrangel/code/qt5/client/SWGChannelSettings.h +++ b/swagger/sdrangel/code/qt5/client/SWGChannelSettings.h @@ -65,7 +65,7 @@ #include "SWGUDPSinkSettings.h" #include "SWGUDPSourceSettings.h" #include "SWGVORDemodMCSettings.h" -#include "SWGVORDemodSCSettings.h" +#include "SWGVORDemodSettings.h" #include "SWGWFMDemodSettings.h" #include "SWGWFMModSettings.h" #include @@ -229,8 +229,8 @@ public: SWGVORDemodMCSettings* getVorDemodMcSettings(); void setVorDemodMcSettings(SWGVORDemodMCSettings* vor_demod_mc_settings); - SWGVORDemodSCSettings* getVorDemodScSettings(); - void setVorDemodScSettings(SWGVORDemodSCSettings* vor_demod_sc_settings); + SWGVORDemodSettings* getVorDemodSettings(); + void setVorDemodSettings(SWGVORDemodSettings* vor_demod_settings); SWGWFMDemodSettings* getWfmDemodSettings(); void setWfmDemodSettings(SWGWFMDemodSettings* wfm_demod_settings); @@ -383,8 +383,8 @@ private: SWGVORDemodMCSettings* vor_demod_mc_settings; bool m_vor_demod_mc_settings_isSet; - SWGVORDemodSCSettings* vor_demod_sc_settings; - bool m_vor_demod_sc_settings_isSet; + SWGVORDemodSettings* vor_demod_settings; + bool m_vor_demod_settings_isSet; SWGWFMDemodSettings* wfm_demod_settings; bool m_wfm_demod_settings_isSet; diff --git a/swagger/sdrangel/code/qt5/client/SWGModelFactory.h b/swagger/sdrangel/code/qt5/client/SWGModelFactory.h index b237ccd2c..e5f64bc64 100644 --- a/swagger/sdrangel/code/qt5/client/SWGModelFactory.h +++ b/swagger/sdrangel/code/qt5/client/SWGModelFactory.h @@ -304,8 +304,8 @@ #include "SWGUSRPOutputSettings.h" #include "SWGVORDemodMCReport.h" #include "SWGVORDemodMCSettings.h" -#include "SWGVORDemodSCReport.h" -#include "SWGVORDemodSCSettings.h" +#include "SWGVORDemodReport.h" +#include "SWGVORDemodSettings.h" #include "SWGVORLocalizerActions.h" #include "SWGVORLocalizerReport.h" #include "SWGVORLocalizerSettings.h" @@ -1773,13 +1773,13 @@ namespace SWGSDRangel { obj->init(); return obj; } - if(QString("SWGVORDemodSCReport").compare(type) == 0) { - SWGVORDemodSCReport *obj = new SWGVORDemodSCReport(); + if(QString("SWGVORDemodReport").compare(type) == 0) { + SWGVORDemodReport *obj = new SWGVORDemodReport(); obj->init(); return obj; } - if(QString("SWGVORDemodSCSettings").compare(type) == 0) { - SWGVORDemodSCSettings *obj = new SWGVORDemodSCSettings(); + if(QString("SWGVORDemodSettings").compare(type) == 0) { + SWGVORDemodSettings *obj = new SWGVORDemodSettings(); obj->init(); return obj; } diff --git a/swagger/sdrangel/code/qt5/client/SWGVORDemodReport.cpp b/swagger/sdrangel/code/qt5/client/SWGVORDemodReport.cpp index b94efbd83..8d3af8628 100644 --- a/swagger/sdrangel/code/qt5/client/SWGVORDemodReport.cpp +++ b/swagger/sdrangel/code/qt5/client/SWGVORDemodReport.cpp @@ -34,6 +34,24 @@ SWGVORDemodReport::SWGVORDemodReport() { m_squelch_isSet = false; audio_sample_rate = 0; m_audio_sample_rate_isSet = false; + volume = 0.0f; + m_volume_isSet = false; + nav_id = 0; + m_nav_id_isSet = false; + radial = 0.0f; + m_radial_isSet = false; + ref_mag = 0.0f; + m_ref_mag_isSet = false; + var_mag = 0.0f; + m_var_mag_isSet = false; + valid_radial = 0; + m_valid_radial_isSet = false; + valid_ref_mag = 0; + m_valid_ref_mag_isSet = false; + valid_var_mag = 0; + m_valid_var_mag_isSet = false; + morse_ident = nullptr; + m_morse_ident_isSet = false; } SWGVORDemodReport::~SWGVORDemodReport() { @@ -48,6 +66,24 @@ SWGVORDemodReport::init() { m_squelch_isSet = false; audio_sample_rate = 0; m_audio_sample_rate_isSet = false; + volume = 0.0f; + m_volume_isSet = false; + nav_id = 0; + m_nav_id_isSet = false; + radial = 0.0f; + m_radial_isSet = false; + ref_mag = 0.0f; + m_ref_mag_isSet = false; + var_mag = 0.0f; + m_var_mag_isSet = false; + valid_radial = 0; + m_valid_radial_isSet = false; + valid_ref_mag = 0; + m_valid_ref_mag_isSet = false; + valid_var_mag = 0; + m_valid_var_mag_isSet = false; + morse_ident = new QString(""); + m_morse_ident_isSet = false; } void @@ -55,6 +91,17 @@ SWGVORDemodReport::cleanup() { + + + + + + + + + if(morse_ident != nullptr) { + delete morse_ident; + } } SWGVORDemodReport* @@ -74,6 +121,24 @@ SWGVORDemodReport::fromJsonObject(QJsonObject &pJson) { ::SWGSDRangel::setValue(&audio_sample_rate, pJson["audioSampleRate"], "qint32", ""); + ::SWGSDRangel::setValue(&volume, pJson["volume"], "float", ""); + + ::SWGSDRangel::setValue(&nav_id, pJson["navId"], "qint32", ""); + + ::SWGSDRangel::setValue(&radial, pJson["radial"], "float", ""); + + ::SWGSDRangel::setValue(&ref_mag, pJson["refMag"], "float", ""); + + ::SWGSDRangel::setValue(&var_mag, pJson["varMag"], "float", ""); + + ::SWGSDRangel::setValue(&valid_radial, pJson["validRadial"], "qint32", ""); + + ::SWGSDRangel::setValue(&valid_ref_mag, pJson["validRefMag"], "qint32", ""); + + ::SWGSDRangel::setValue(&valid_var_mag, pJson["validVarMag"], "qint32", ""); + + ::SWGSDRangel::setValue(&morse_ident, pJson["morseIdent"], "QString", "QString"); + } QString @@ -99,6 +164,33 @@ SWGVORDemodReport::asJsonObject() { if(m_audio_sample_rate_isSet){ obj->insert("audioSampleRate", QJsonValue(audio_sample_rate)); } + if(m_volume_isSet){ + obj->insert("volume", QJsonValue(volume)); + } + if(m_nav_id_isSet){ + obj->insert("navId", QJsonValue(nav_id)); + } + if(m_radial_isSet){ + obj->insert("radial", QJsonValue(radial)); + } + if(m_ref_mag_isSet){ + obj->insert("refMag", QJsonValue(ref_mag)); + } + if(m_var_mag_isSet){ + obj->insert("varMag", QJsonValue(var_mag)); + } + if(m_valid_radial_isSet){ + obj->insert("validRadial", QJsonValue(valid_radial)); + } + if(m_valid_ref_mag_isSet){ + obj->insert("validRefMag", QJsonValue(valid_ref_mag)); + } + if(m_valid_var_mag_isSet){ + obj->insert("validVarMag", QJsonValue(valid_var_mag)); + } + if(morse_ident != nullptr && *morse_ident != QString("")){ + toJsonValue(QString("morseIdent"), morse_ident, obj, QString("QString")); + } return obj; } @@ -133,6 +225,96 @@ SWGVORDemodReport::setAudioSampleRate(qint32 audio_sample_rate) { this->m_audio_sample_rate_isSet = true; } +float +SWGVORDemodReport::getVolume() { + return volume; +} +void +SWGVORDemodReport::setVolume(float volume) { + this->volume = volume; + this->m_volume_isSet = true; +} + +qint32 +SWGVORDemodReport::getNavId() { + return nav_id; +} +void +SWGVORDemodReport::setNavId(qint32 nav_id) { + this->nav_id = nav_id; + this->m_nav_id_isSet = true; +} + +float +SWGVORDemodReport::getRadial() { + return radial; +} +void +SWGVORDemodReport::setRadial(float radial) { + this->radial = radial; + this->m_radial_isSet = true; +} + +float +SWGVORDemodReport::getRefMag() { + return ref_mag; +} +void +SWGVORDemodReport::setRefMag(float ref_mag) { + this->ref_mag = ref_mag; + this->m_ref_mag_isSet = true; +} + +float +SWGVORDemodReport::getVarMag() { + return var_mag; +} +void +SWGVORDemodReport::setVarMag(float var_mag) { + this->var_mag = var_mag; + this->m_var_mag_isSet = true; +} + +qint32 +SWGVORDemodReport::getValidRadial() { + return valid_radial; +} +void +SWGVORDemodReport::setValidRadial(qint32 valid_radial) { + this->valid_radial = valid_radial; + this->m_valid_radial_isSet = true; +} + +qint32 +SWGVORDemodReport::getValidRefMag() { + return valid_ref_mag; +} +void +SWGVORDemodReport::setValidRefMag(qint32 valid_ref_mag) { + this->valid_ref_mag = valid_ref_mag; + this->m_valid_ref_mag_isSet = true; +} + +qint32 +SWGVORDemodReport::getValidVarMag() { + return valid_var_mag; +} +void +SWGVORDemodReport::setValidVarMag(qint32 valid_var_mag) { + this->valid_var_mag = valid_var_mag; + this->m_valid_var_mag_isSet = true; +} + +QString* +SWGVORDemodReport::getMorseIdent() { + return morse_ident; +} +void +SWGVORDemodReport::setMorseIdent(QString* morse_ident) { + this->morse_ident = morse_ident; + this->m_morse_ident_isSet = true; +} + bool SWGVORDemodReport::isSet(){ @@ -147,6 +329,33 @@ SWGVORDemodReport::isSet(){ if(m_audio_sample_rate_isSet){ isObjectUpdated = true; break; } + if(m_volume_isSet){ + isObjectUpdated = true; break; + } + if(m_nav_id_isSet){ + isObjectUpdated = true; break; + } + if(m_radial_isSet){ + isObjectUpdated = true; break; + } + if(m_ref_mag_isSet){ + isObjectUpdated = true; break; + } + if(m_var_mag_isSet){ + isObjectUpdated = true; break; + } + if(m_valid_radial_isSet){ + isObjectUpdated = true; break; + } + if(m_valid_ref_mag_isSet){ + isObjectUpdated = true; break; + } + if(m_valid_var_mag_isSet){ + isObjectUpdated = true; break; + } + if(morse_ident && *morse_ident != QString("")){ + isObjectUpdated = true; break; + } }while(false); return isObjectUpdated; } diff --git a/swagger/sdrangel/code/qt5/client/SWGVORDemodReport.h b/swagger/sdrangel/code/qt5/client/SWGVORDemodReport.h index b66202648..c231977e0 100644 --- a/swagger/sdrangel/code/qt5/client/SWGVORDemodReport.h +++ b/swagger/sdrangel/code/qt5/client/SWGVORDemodReport.h @@ -22,6 +22,7 @@ #include +#include #include "SWGObject.h" #include "export.h" @@ -50,6 +51,33 @@ public: qint32 getAudioSampleRate(); void setAudioSampleRate(qint32 audio_sample_rate); + float getVolume(); + void setVolume(float volume); + + qint32 getNavId(); + void setNavId(qint32 nav_id); + + float getRadial(); + void setRadial(float radial); + + float getRefMag(); + void setRefMag(float ref_mag); + + float getVarMag(); + void setVarMag(float var_mag); + + qint32 getValidRadial(); + void setValidRadial(qint32 valid_radial); + + qint32 getValidRefMag(); + void setValidRefMag(qint32 valid_ref_mag); + + qint32 getValidVarMag(); + void setValidVarMag(qint32 valid_var_mag); + + QString* getMorseIdent(); + void setMorseIdent(QString* morse_ident); + virtual bool isSet() override; @@ -63,6 +91,33 @@ private: qint32 audio_sample_rate; bool m_audio_sample_rate_isSet; + float volume; + bool m_volume_isSet; + + qint32 nav_id; + bool m_nav_id_isSet; + + float radial; + bool m_radial_isSet; + + float ref_mag; + bool m_ref_mag_isSet; + + float var_mag; + bool m_var_mag_isSet; + + qint32 valid_radial; + bool m_valid_radial_isSet; + + qint32 valid_ref_mag; + bool m_valid_ref_mag_isSet; + + qint32 valid_var_mag; + bool m_valid_var_mag_isSet; + + QString* morse_ident; + bool m_morse_ident_isSet; + }; } diff --git a/swagger/sdrangel/code/qt5/client/SWGVORDemodSettings.cpp b/swagger/sdrangel/code/qt5/client/SWGVORDemodSettings.cpp index af4fc8e3f..b70af3b6a 100644 --- a/swagger/sdrangel/code/qt5/client/SWGVORDemodSettings.cpp +++ b/swagger/sdrangel/code/qt5/client/SWGVORDemodSettings.cpp @@ -28,6 +28,10 @@ SWGVORDemodSettings::SWGVORDemodSettings(QString* json) { } SWGVORDemodSettings::SWGVORDemodSettings() { + input_frequency_offset = 0L; + m_input_frequency_offset_isSet = false; + nav_id = 0; + m_nav_id_isSet = false; squelch = 0.0f; m_squelch_isSet = false; volume = 0.0f; @@ -54,8 +58,6 @@ SWGVORDemodSettings::SWGVORDemodSettings() { m_reverse_api_channel_index_isSet = false; ident_threshold = 0; m_ident_threshold_isSet = false; - mag_dec_adjust = 0; - m_mag_dec_adjust_isSet = false; channel_marker = nullptr; m_channel_marker_isSet = false; rollup_state = nullptr; @@ -68,6 +70,10 @@ SWGVORDemodSettings::~SWGVORDemodSettings() { void SWGVORDemodSettings::init() { + input_frequency_offset = 0L; + m_input_frequency_offset_isSet = false; + nav_id = 0; + m_nav_id_isSet = false; squelch = 0.0f; m_squelch_isSet = false; volume = 0.0f; @@ -94,8 +100,6 @@ SWGVORDemodSettings::init() { m_reverse_api_channel_index_isSet = false; ident_threshold = 0; m_ident_threshold_isSet = false; - mag_dec_adjust = 0; - m_mag_dec_adjust_isSet = false; channel_marker = new SWGChannelMarker(); m_channel_marker_isSet = false; rollup_state = new SWGRollupState(); @@ -108,6 +112,8 @@ SWGVORDemodSettings::cleanup() { + + if(title != nullptr) { delete title; } @@ -123,7 +129,6 @@ SWGVORDemodSettings::cleanup() { - if(channel_marker != nullptr) { delete channel_marker; } @@ -143,6 +148,10 @@ SWGVORDemodSettings::fromJson(QString &json) { void SWGVORDemodSettings::fromJsonObject(QJsonObject &pJson) { + ::SWGSDRangel::setValue(&input_frequency_offset, pJson["inputFrequencyOffset"], "qint64", ""); + + ::SWGSDRangel::setValue(&nav_id, pJson["navId"], "qint32", ""); + ::SWGSDRangel::setValue(&squelch, pJson["squelch"], "float", ""); ::SWGSDRangel::setValue(&volume, pJson["volume"], "float", ""); @@ -169,8 +178,6 @@ SWGVORDemodSettings::fromJsonObject(QJsonObject &pJson) { ::SWGSDRangel::setValue(&ident_threshold, pJson["identThreshold"], "qint32", ""); - ::SWGSDRangel::setValue(&mag_dec_adjust, pJson["magDecAdjust"], "qint32", ""); - ::SWGSDRangel::setValue(&channel_marker, pJson["channelMarker"], "SWGChannelMarker", "SWGChannelMarker"); ::SWGSDRangel::setValue(&rollup_state, pJson["rollupState"], "SWGRollupState", "SWGRollupState"); @@ -191,6 +198,12 @@ SWGVORDemodSettings::asJson () QJsonObject* SWGVORDemodSettings::asJsonObject() { QJsonObject* obj = new QJsonObject(); + if(m_input_frequency_offset_isSet){ + obj->insert("inputFrequencyOffset", QJsonValue(input_frequency_offset)); + } + if(m_nav_id_isSet){ + obj->insert("navId", QJsonValue(nav_id)); + } if(m_squelch_isSet){ obj->insert("squelch", QJsonValue(squelch)); } @@ -230,9 +243,6 @@ SWGVORDemodSettings::asJsonObject() { if(m_ident_threshold_isSet){ obj->insert("identThreshold", QJsonValue(ident_threshold)); } - if(m_mag_dec_adjust_isSet){ - obj->insert("magDecAdjust", QJsonValue(mag_dec_adjust)); - } if((channel_marker != nullptr) && (channel_marker->isSet())){ toJsonValue(QString("channelMarker"), channel_marker, obj, QString("SWGChannelMarker")); } @@ -243,6 +253,26 @@ SWGVORDemodSettings::asJsonObject() { return obj; } +qint64 +SWGVORDemodSettings::getInputFrequencyOffset() { + return input_frequency_offset; +} +void +SWGVORDemodSettings::setInputFrequencyOffset(qint64 input_frequency_offset) { + this->input_frequency_offset = input_frequency_offset; + this->m_input_frequency_offset_isSet = true; +} + +qint32 +SWGVORDemodSettings::getNavId() { + return nav_id; +} +void +SWGVORDemodSettings::setNavId(qint32 nav_id) { + this->nav_id = nav_id; + this->m_nav_id_isSet = true; +} + float SWGVORDemodSettings::getSquelch() { return squelch; @@ -373,16 +403,6 @@ SWGVORDemodSettings::setIdentThreshold(qint32 ident_threshold) { this->m_ident_threshold_isSet = true; } -qint32 -SWGVORDemodSettings::getMagDecAdjust() { - return mag_dec_adjust; -} -void -SWGVORDemodSettings::setMagDecAdjust(qint32 mag_dec_adjust) { - this->mag_dec_adjust = mag_dec_adjust; - this->m_mag_dec_adjust_isSet = true; -} - SWGChannelMarker* SWGVORDemodSettings::getChannelMarker() { return channel_marker; @@ -408,6 +428,12 @@ bool SWGVORDemodSettings::isSet(){ bool isObjectUpdated = false; do{ + if(m_input_frequency_offset_isSet){ + isObjectUpdated = true; break; + } + if(m_nav_id_isSet){ + isObjectUpdated = true; break; + } if(m_squelch_isSet){ isObjectUpdated = true; break; } @@ -447,9 +473,6 @@ SWGVORDemodSettings::isSet(){ if(m_ident_threshold_isSet){ isObjectUpdated = true; break; } - if(m_mag_dec_adjust_isSet){ - isObjectUpdated = true; break; - } if(channel_marker && channel_marker->isSet()){ isObjectUpdated = true; break; } diff --git a/swagger/sdrangel/code/qt5/client/SWGVORDemodSettings.h b/swagger/sdrangel/code/qt5/client/SWGVORDemodSettings.h index 5801c69ea..234111ee1 100644 --- a/swagger/sdrangel/code/qt5/client/SWGVORDemodSettings.h +++ b/swagger/sdrangel/code/qt5/client/SWGVORDemodSettings.h @@ -44,6 +44,12 @@ public: virtual void fromJsonObject(QJsonObject &json) override; virtual SWGVORDemodSettings* fromJson(QString &jsonString) override; + qint64 getInputFrequencyOffset(); + void setInputFrequencyOffset(qint64 input_frequency_offset); + + qint32 getNavId(); + void setNavId(qint32 nav_id); + float getSquelch(); void setSquelch(float squelch); @@ -83,9 +89,6 @@ public: qint32 getIdentThreshold(); void setIdentThreshold(qint32 ident_threshold); - qint32 getMagDecAdjust(); - void setMagDecAdjust(qint32 mag_dec_adjust); - SWGChannelMarker* getChannelMarker(); void setChannelMarker(SWGChannelMarker* channel_marker); @@ -96,6 +99,12 @@ public: virtual bool isSet() override; private: + qint64 input_frequency_offset; + bool m_input_frequency_offset_isSet; + + qint32 nav_id; + bool m_nav_id_isSet; + float squelch; bool m_squelch_isSet; @@ -135,9 +144,6 @@ private: qint32 ident_threshold; bool m_ident_threshold_isSet; - qint32 mag_dec_adjust; - bool m_mag_dec_adjust_isSet; - SWGChannelMarker* channel_marker; bool m_channel_marker_isSet; From 7d450c6e42f8a508df868d5010aa3e7e6c7edc0a Mon Sep 17 00:00:00 2001 From: f4exb Date: Sun, 1 May 2022 13:05:36 +0200 Subject: [PATCH 088/115] v7: rename vordemodsc files to vordemod files. Part of #1223 --- plugins/channelrx/CMakeLists.txt | 2 +- .../{demodvorsc => demodvor}/CMakeLists.txt | 54 +++++++++---------- .../{demodvorsc => demodvor}/readme.md | 0 .../vordemodsc.cpp => demodvor/vordemod.cpp} | 4 +- .../vordemodsc.h => demodvor/vordemod.h} | 4 +- .../vordemodbaseband.cpp} | 4 +- .../vordemodbaseband.h} | 2 +- .../vordemodgui.cpp} | 8 +-- .../vordemodgui.h} | 2 +- .../vordemodgui.ui} | 0 .../vordemodplugin.cpp} | 10 ++-- .../vordemodplugin.h} | 0 .../vordemodreport.cpp} | 2 +- .../vordemodreport.h} | 0 .../vordemodsettings.cpp} | 2 +- .../vordemodsettings.h} | 0 .../vordemodsink.cpp} | 6 +-- .../vordemodsink.h} | 2 +- .../vordemodwebapiadapter.cpp} | 4 +- .../vordemodwebapiadapter.h} | 2 +- .../channelrx/demodvormc/vordemodmcplugin.cpp | 2 +- 21 files changed, 55 insertions(+), 55 deletions(-) rename plugins/channelrx/{demodvorsc => demodvor}/CMakeLists.txt (52%) rename plugins/channelrx/{demodvorsc => demodvor}/readme.md (100%) rename plugins/channelrx/{demodvorsc/vordemodsc.cpp => demodvor/vordemod.cpp} (99%) rename plugins/channelrx/{demodvorsc/vordemodsc.h => demodvor/vordemod.h} (99%) rename plugins/channelrx/{demodvorsc/vordemodscbaseband.cpp => demodvor/vordemodbaseband.cpp} (99%) rename plugins/channelrx/{demodvorsc/vordemodscbaseband.h => demodvor/vordemodbaseband.h} (99%) rename plugins/channelrx/{demodvorsc/vordemodscgui.cpp => demodvor/vordemodgui.cpp} (99%) rename plugins/channelrx/{demodvorsc/vordemodscgui.h => demodvor/vordemodgui.h} (99%) rename plugins/channelrx/{demodvorsc/vordemodscgui.ui => demodvor/vordemodgui.ui} (100%) rename plugins/channelrx/{demodvorsc/vordemodscplugin.cpp => demodvor/vordemodplugin.cpp} (94%) rename plugins/channelrx/{demodvorsc/vordemodscplugin.h => demodvor/vordemodplugin.h} (100%) rename plugins/channelrx/{demodvorsc/vordemodscreport.cpp => demodvor/vordemodreport.cpp} (98%) rename plugins/channelrx/{demodvorsc/vordemodscreport.h => demodvor/vordemodreport.h} (100%) rename plugins/channelrx/{demodvorsc/vordemodscsettings.cpp => demodvor/vordemodsettings.cpp} (99%) rename plugins/channelrx/{demodvorsc/vordemodscsettings.h => demodvor/vordemodsettings.h} (100%) rename plugins/channelrx/{demodvorsc/vordemodscsink.cpp => demodvor/vordemodsink.cpp} (99%) rename plugins/channelrx/{demodvorsc/vordemodscsink.h => demodvor/vordemodsink.h} (99%) rename plugins/channelrx/{demodvorsc/vordemodscwebapiadapter.cpp => demodvor/vordemodwebapiadapter.cpp} (97%) rename plugins/channelrx/{demodvorsc/vordemodscwebapiadapter.h => demodvor/vordemodwebapiadapter.h} (98%) diff --git a/plugins/channelrx/CMakeLists.txt b/plugins/channelrx/CMakeLists.txt index 1153de171..f04fd1fb1 100644 --- a/plugins/channelrx/CMakeLists.txt +++ b/plugins/channelrx/CMakeLists.txt @@ -15,7 +15,7 @@ add_subdirectory(localsink) add_subdirectory(filesink) add_subdirectory(freqtracker) add_subdirectory(demodchirpchat) -add_subdirectory(demodvorsc) +add_subdirectory(demodvor) add_subdirectory(demodpacket) add_subdirectory(demodais) add_subdirectory(demodpager) diff --git a/plugins/channelrx/demodvorsc/CMakeLists.txt b/plugins/channelrx/demodvor/CMakeLists.txt similarity index 52% rename from plugins/channelrx/demodvorsc/CMakeLists.txt rename to plugins/channelrx/demodvor/CMakeLists.txt index d4f15de64..c488df18f 100644 --- a/plugins/channelrx/demodvorsc/CMakeLists.txt +++ b/plugins/channelrx/demodvor/CMakeLists.txt @@ -1,23 +1,23 @@ -project(vorsc) +project(vor) -set(vorsc_SOURCES - vordemodsc.cpp - vordemodscsettings.cpp - vordemodscbaseband.cpp - vordemodscsink.cpp - vordemodscplugin.cpp - vordemodscwebapiadapter.cpp - vordemodscreport.cpp +set(vor_SOURCES + vordemod.cpp + vordemodsettings.cpp + vordemodbaseband.cpp + vordemodsink.cpp + vordemodplugin.cpp + vordemodwebapiadapter.cpp + vordemodreport.cpp ) -set(vorsc_HEADERS - vordemodsc.h - vordemodscsettings.h - vordemodscbaseband.h - vordemodscsink.h - vordemodscplugin.h - vordemodscwebapiadapter.h - vordemodscreport.h +set(vor_HEADERS + vordemod.h + vordemodsettings.h + vordemodbaseband.h + vordemodsink.h + vordemodplugin.h + vordemodwebapiadapter.h + vordemodreport.h ) include_directories( @@ -25,29 +25,29 @@ include_directories( ) if(NOT SERVER_MODE) - set(vorsc_SOURCES - ${vorsc_SOURCES} - vordemodscgui.cpp - vordemodscgui.ui + set(vor_SOURCES + ${vor_SOURCES} + vordemodgui.cpp + vordemodgui.ui ) - set(vorsc_HEADERS - ${vorsc_HEADERS} - vordemodscgui.h + set(vor_HEADERS + ${vor_HEADERS} + vordemodgui.h ) - set(TARGET_NAME demodvorsc) + set(TARGET_NAME demodvor) set(TARGET_LIB "Qt5::Widgets") set(TARGET_LIB_GUI "sdrgui") set(INSTALL_FOLDER ${INSTALL_PLUGINS_DIR}) else() - set(TARGET_NAME demodvorscsrv) + set(TARGET_NAME demodvorsrv) set(TARGET_LIB "") set(TARGET_LIB_GUI "") set(INSTALL_FOLDER ${INSTALL_PLUGINSSRV_DIR}) endif() add_library(${TARGET_NAME} SHARED - ${vorsc_SOURCES} + ${vor_SOURCES} ) target_link_libraries(${TARGET_NAME} diff --git a/plugins/channelrx/demodvorsc/readme.md b/plugins/channelrx/demodvor/readme.md similarity index 100% rename from plugins/channelrx/demodvorsc/readme.md rename to plugins/channelrx/demodvor/readme.md diff --git a/plugins/channelrx/demodvorsc/vordemodsc.cpp b/plugins/channelrx/demodvor/vordemod.cpp similarity index 99% rename from plugins/channelrx/demodvorsc/vordemodsc.cpp rename to plugins/channelrx/demodvor/vordemod.cpp index 33cc8b5ab..6ada23dbc 100644 --- a/plugins/channelrx/demodvorsc/vordemodsc.cpp +++ b/plugins/channelrx/demodvor/vordemod.cpp @@ -39,8 +39,8 @@ #include "util/db.h" #include "maincore.h" -#include "vordemodscreport.h" -#include "vordemodsc.h" +#include "vordemodreport.h" +#include "vordemod.h" MESSAGE_CLASS_DEFINITION(VORDemod::MsgConfigureVORDemod, Message) diff --git a/plugins/channelrx/demodvorsc/vordemodsc.h b/plugins/channelrx/demodvor/vordemod.h similarity index 99% rename from plugins/channelrx/demodvorsc/vordemodsc.h rename to plugins/channelrx/demodvor/vordemod.h index 5dc5bf848..c1bf8dcaf 100644 --- a/plugins/channelrx/demodvorsc/vordemodsc.h +++ b/plugins/channelrx/demodvor/vordemod.h @@ -28,8 +28,8 @@ #include "channel/channelapi.h" #include "util/message.h" -#include "vordemodscbaseband.h" -#include "vordemodscsettings.h" +#include "vordemodbaseband.h" +#include "vordemodsettings.h" class QNetworkAccessManager; class QNetworkReply; diff --git a/plugins/channelrx/demodvorsc/vordemodscbaseband.cpp b/plugins/channelrx/demodvor/vordemodbaseband.cpp similarity index 99% rename from plugins/channelrx/demodvorsc/vordemodscbaseband.cpp rename to plugins/channelrx/demodvor/vordemodbaseband.cpp index a67cd9800..a7daed5c3 100644 --- a/plugins/channelrx/demodvorsc/vordemodscbaseband.cpp +++ b/plugins/channelrx/demodvor/vordemodbaseband.cpp @@ -22,8 +22,8 @@ #include "dsp/dspcommands.h" #include "dsp/downchannelizer.h" -#include "vordemodscbaseband.h" -#include "vordemodscreport.h" +#include "vordemodbaseband.h" +#include "vordemodreport.h" MESSAGE_CLASS_DEFINITION(VORDemodBaseband::MsgConfigureVORDemodBaseband, Message) diff --git a/plugins/channelrx/demodvorsc/vordemodscbaseband.h b/plugins/channelrx/demodvor/vordemodbaseband.h similarity index 99% rename from plugins/channelrx/demodvorsc/vordemodscbaseband.h rename to plugins/channelrx/demodvor/vordemodbaseband.h index 16df9cf87..39c2f37af 100644 --- a/plugins/channelrx/demodvorsc/vordemodscbaseband.h +++ b/plugins/channelrx/demodvor/vordemodbaseband.h @@ -26,7 +26,7 @@ #include "util/message.h" #include "util/messagequeue.h" -#include "vordemodscsink.h" +#include "vordemodsink.h" class DownChannelizer; diff --git a/plugins/channelrx/demodvorsc/vordemodscgui.cpp b/plugins/channelrx/demodvor/vordemodgui.cpp similarity index 99% rename from plugins/channelrx/demodvorsc/vordemodscgui.cpp rename to plugins/channelrx/demodvor/vordemodgui.cpp index 7f73c241d..30835d686 100644 --- a/plugins/channelrx/demodvorsc/vordemodscgui.cpp +++ b/plugins/channelrx/demodvor/vordemodgui.cpp @@ -37,10 +37,10 @@ #include "channel/channelwebapiutils.h" #include "maincore.h" -#include "ui_vordemodscgui.h" -#include "vordemodsc.h" -#include "vordemodscreport.h" -#include "vordemodscgui.h" +#include "ui_vordemodgui.h" +#include "vordemod.h" +#include "vordemodreport.h" +#include "vordemodgui.h" VORDemodGUI* VORDemodGUI::create(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel) { diff --git a/plugins/channelrx/demodvorsc/vordemodscgui.h b/plugins/channelrx/demodvor/vordemodgui.h similarity index 99% rename from plugins/channelrx/demodvorsc/vordemodscgui.h rename to plugins/channelrx/demodvor/vordemodgui.h index 11a553dfa..44bced0fa 100644 --- a/plugins/channelrx/demodvorsc/vordemodscgui.h +++ b/plugins/channelrx/demodvor/vordemodgui.h @@ -24,7 +24,7 @@ #include "dsp/movingaverage.h" #include "util/messagequeue.h" #include "settings/rollupstate.h" -#include "vordemodscsettings.h" +#include "vordemodsettings.h" class PluginAPI; class DeviceUISet; diff --git a/plugins/channelrx/demodvorsc/vordemodscgui.ui b/plugins/channelrx/demodvor/vordemodgui.ui similarity index 100% rename from plugins/channelrx/demodvorsc/vordemodscgui.ui rename to plugins/channelrx/demodvor/vordemodgui.ui diff --git a/plugins/channelrx/demodvorsc/vordemodscplugin.cpp b/plugins/channelrx/demodvor/vordemodplugin.cpp similarity index 94% rename from plugins/channelrx/demodvorsc/vordemodscplugin.cpp rename to plugins/channelrx/demodvor/vordemodplugin.cpp index c27969448..df9737680 100644 --- a/plugins/channelrx/demodvorsc/vordemodscplugin.cpp +++ b/plugins/channelrx/demodvor/vordemodplugin.cpp @@ -20,15 +20,15 @@ #include "plugin/pluginapi.h" #ifndef SERVER_MODE -#include "vordemodscgui.h" +#include "vordemodgui.h" #endif -#include "vordemodsc.h" -#include "vordemodscwebapiadapter.h" -#include "vordemodscplugin.h" +#include "vordemod.h" +#include "vordemodwebapiadapter.h" +#include "vordemodplugin.h" const PluginDescriptor VORDemodPlugin::m_pluginDescriptor = { VORDemod::m_channelId, - QStringLiteral("VOR Single Channel Demodulator"), + QStringLiteral("VOR Demodulator"), QStringLiteral("6.20.2"), QStringLiteral("(c) Jon Beniston, M7RCE"), QStringLiteral("https://github.com/f4exb/sdrangel"), diff --git a/plugins/channelrx/demodvorsc/vordemodscplugin.h b/plugins/channelrx/demodvor/vordemodplugin.h similarity index 100% rename from plugins/channelrx/demodvorsc/vordemodscplugin.h rename to plugins/channelrx/demodvor/vordemodplugin.h diff --git a/plugins/channelrx/demodvorsc/vordemodscreport.cpp b/plugins/channelrx/demodvor/vordemodreport.cpp similarity index 98% rename from plugins/channelrx/demodvorsc/vordemodscreport.cpp rename to plugins/channelrx/demodvor/vordemodreport.cpp index cb9fc4ee1..96fcbc7f6 100644 --- a/plugins/channelrx/demodvorsc/vordemodscreport.cpp +++ b/plugins/channelrx/demodvor/vordemodreport.cpp @@ -15,7 +15,7 @@ // along with this program. If not, see . // /////////////////////////////////////////////////////////////////////////////////// -#include "vordemodscreport.h" +#include "vordemodreport.h" MESSAGE_CLASS_DEFINITION(VORDemodReport::MsgReportRadial, Message) MESSAGE_CLASS_DEFINITION(VORDemodReport::MsgReportIdent, Message) diff --git a/plugins/channelrx/demodvorsc/vordemodscreport.h b/plugins/channelrx/demodvor/vordemodreport.h similarity index 100% rename from plugins/channelrx/demodvorsc/vordemodscreport.h rename to plugins/channelrx/demodvor/vordemodreport.h diff --git a/plugins/channelrx/demodvorsc/vordemodscsettings.cpp b/plugins/channelrx/demodvor/vordemodsettings.cpp similarity index 99% rename from plugins/channelrx/demodvorsc/vordemodscsettings.cpp rename to plugins/channelrx/demodvor/vordemodsettings.cpp index f4f0952db..d48fdd93f 100644 --- a/plugins/channelrx/demodvorsc/vordemodscsettings.cpp +++ b/plugins/channelrx/demodvor/vordemodsettings.cpp @@ -21,7 +21,7 @@ #include "dsp/dspengine.h" #include "util/simpleserializer.h" #include "settings/serializable.h" -#include "vordemodscsettings.h" +#include "vordemodsettings.h" VORDemodSettings::VORDemodSettings() : m_channelMarker(nullptr), diff --git a/plugins/channelrx/demodvorsc/vordemodscsettings.h b/plugins/channelrx/demodvor/vordemodsettings.h similarity index 100% rename from plugins/channelrx/demodvorsc/vordemodscsettings.h rename to plugins/channelrx/demodvor/vordemodsettings.h diff --git a/plugins/channelrx/demodvorsc/vordemodscsink.cpp b/plugins/channelrx/demodvor/vordemodsink.cpp similarity index 99% rename from plugins/channelrx/demodvorsc/vordemodscsink.cpp rename to plugins/channelrx/demodvor/vordemodsink.cpp index c94fb45f8..2fc56080b 100644 --- a/plugins/channelrx/demodvorsc/vordemodscsink.cpp +++ b/plugins/channelrx/demodvor/vordemodsink.cpp @@ -27,9 +27,9 @@ #include "util/morse.h" #include "util/units.h" -#include "vordemodscreport.h" -#include "vordemodscsettings.h" -#include "vordemodscsink.h" +#include "vordemodreport.h" +#include "vordemodsettings.h" +#include "vordemodsink.h" VORDemodSCSink::VORDemodSCSink() : m_channelFrequencyOffset(0), diff --git a/plugins/channelrx/demodvorsc/vordemodscsink.h b/plugins/channelrx/demodvor/vordemodsink.h similarity index 99% rename from plugins/channelrx/demodvorsc/vordemodscsink.h rename to plugins/channelrx/demodvor/vordemodsink.h index 6cc2a34b1..1086f628d 100644 --- a/plugins/channelrx/demodvorsc/vordemodscsink.h +++ b/plugins/channelrx/demodvor/vordemodsink.h @@ -30,7 +30,7 @@ #include "util/doublebufferfifo.h" #include "util/messagequeue.h" -#include "vordemodscsettings.h" +#include "vordemodsettings.h" #include diff --git a/plugins/channelrx/demodvorsc/vordemodscwebapiadapter.cpp b/plugins/channelrx/demodvor/vordemodwebapiadapter.cpp similarity index 97% rename from plugins/channelrx/demodvorsc/vordemodscwebapiadapter.cpp rename to plugins/channelrx/demodvor/vordemodwebapiadapter.cpp index 864984ad2..9b699cacd 100644 --- a/plugins/channelrx/demodvorsc/vordemodscwebapiadapter.cpp +++ b/plugins/channelrx/demodvor/vordemodwebapiadapter.cpp @@ -17,8 +17,8 @@ /////////////////////////////////////////////////////////////////////////////////// #include "SWGChannelSettings.h" -#include "vordemodsc.h" -#include "vordemodscwebapiadapter.h" +#include "vordemod.h" +#include "vordemodwebapiadapter.h" VORDemodSCWebAPIAdapter::VORDemodSCWebAPIAdapter() {} diff --git a/plugins/channelrx/demodvorsc/vordemodscwebapiadapter.h b/plugins/channelrx/demodvor/vordemodwebapiadapter.h similarity index 98% rename from plugins/channelrx/demodvorsc/vordemodscwebapiadapter.h rename to plugins/channelrx/demodvor/vordemodwebapiadapter.h index 8cf1ae192..c2b7b60c3 100644 --- a/plugins/channelrx/demodvorsc/vordemodscwebapiadapter.h +++ b/plugins/channelrx/demodvor/vordemodwebapiadapter.h @@ -20,7 +20,7 @@ #define INCLUDE_VORDEMODSC_WEBAPIADAPTER_H #include "channel/channelwebapiadapter.h" -#include "vordemodscsettings.h" +#include "vordemodsettings.h" /** * Standalone API adapter only for the settings diff --git a/plugins/channelrx/demodvormc/vordemodmcplugin.cpp b/plugins/channelrx/demodvormc/vordemodmcplugin.cpp index 7ba829772..8027a177a 100644 --- a/plugins/channelrx/demodvormc/vordemodmcplugin.cpp +++ b/plugins/channelrx/demodvormc/vordemodmcplugin.cpp @@ -28,7 +28,7 @@ const PluginDescriptor VORDemodMCPlugin::m_pluginDescriptor = { VORDemodMC::m_channelId, - QStringLiteral("VOR Demodulator"), + QStringLiteral("VOR Multi Channel Demodulator"), QStringLiteral("6.20.2"), QStringLiteral("(c) Jon Beniston, M7RCE"), QStringLiteral("https://github.com/f4exb/sdrangel"), From c646bacf8b817ef1ec3c37fe1ab6af3f0dab8104 Mon Sep 17 00:00:00 2001 From: f4exb Date: Sun, 1 May 2022 23:33:12 +0200 Subject: [PATCH 089/115] v7: cleanup of obsolete VOR demod files. Implements #1223 --- plugins/channelrx/CMakeLists.txt | 2 +- sdrbase/resources/webapi.qrc | 1 - sdrbase/resources/webapi/doc/html2/index.html | 85 +-- .../doc/swagger/include/ChannelReport.yaml | 4 +- .../doc/swagger/include/ChannelSettings.yaml | 4 +- .../doc/swagger/include/VORDemodSC.yaml | 97 ---- sdrbase/webapi/webapirequestmapper.cpp | 14 +- .../api/swagger/include/ChannelReport.yaml | 4 +- .../api/swagger/include/ChannelSettings.yaml | 4 +- swagger/sdrangel/code/html2/index.html | 85 +-- .../code/qt5/client/SWGChannelReport.cpp | 25 - .../code/qt5/client/SWGChannelReport.h | 7 - .../code/qt5/client/SWGChannelSettings.cpp | 25 - .../code/qt5/client/SWGChannelSettings.h | 7 - .../code/qt5/client/SWGModelFactory.h | 12 - .../code/qt5/client/SWGVORDemodMCReport.cpp | 154 ------ .../code/qt5/client/SWGVORDemodMCReport.h | 70 --- .../code/qt5/client/SWGVORDemodMCSettings.cpp | 463 ----------------- .../code/qt5/client/SWGVORDemodMCSettings.h | 151 ------ .../code/qt5/client/SWGVORDemodSCReport.cpp | 363 ------------- .../code/qt5/client/SWGVORDemodSCReport.h | 125 ----- .../code/qt5/client/SWGVORDemodSCSettings.cpp | 486 ------------------ .../code/qt5/client/SWGVORDemodSCSettings.h | 157 ------ 23 files changed, 18 insertions(+), 2327 deletions(-) delete mode 100644 sdrbase/resources/webapi/doc/swagger/include/VORDemodSC.yaml delete mode 100644 swagger/sdrangel/code/qt5/client/SWGVORDemodMCReport.cpp delete mode 100644 swagger/sdrangel/code/qt5/client/SWGVORDemodMCReport.h delete mode 100644 swagger/sdrangel/code/qt5/client/SWGVORDemodMCSettings.cpp delete mode 100644 swagger/sdrangel/code/qt5/client/SWGVORDemodMCSettings.h delete mode 100644 swagger/sdrangel/code/qt5/client/SWGVORDemodSCReport.cpp delete mode 100644 swagger/sdrangel/code/qt5/client/SWGVORDemodSCReport.h delete mode 100644 swagger/sdrangel/code/qt5/client/SWGVORDemodSCSettings.cpp delete mode 100644 swagger/sdrangel/code/qt5/client/SWGVORDemodSCSettings.h diff --git a/plugins/channelrx/CMakeLists.txt b/plugins/channelrx/CMakeLists.txt index f04fd1fb1..b0b6e106e 100644 --- a/plugins/channelrx/CMakeLists.txt +++ b/plugins/channelrx/CMakeLists.txt @@ -2,7 +2,7 @@ project(demod) if (Qt5Quick_FOUND AND Qt5QuickWidgets_FOUND AND Qt5Positioning_FOUND) add_subdirectory(demodadsb) - add_subdirectory(demodvormc) + # add_subdirectory(demodvormc) endif() add_subdirectory(demodam) diff --git a/sdrbase/resources/webapi.qrc b/sdrbase/resources/webapi.qrc index 4a374828d..219ac5fc7 100644 --- a/sdrbase/resources/webapi.qrc +++ b/sdrbase/resources/webapi.qrc @@ -109,7 +109,6 @@ webapi/doc/swagger/include/USRP.yaml webapi/doc/swagger/include/VORDemod.yaml webapi/doc/swagger/include/VORLocalizer.yaml - webapi/doc/swagger/include/VORDemodSC.yaml webapi/doc/swagger/include/WFMDemod.yaml webapi/doc/swagger/include/WFMMod.yaml webapi/doc/swagger/include/Xtrx.yaml diff --git a/sdrbase/resources/webapi/doc/html2/index.html b/sdrbase/resources/webapi/doc/html2/index.html index 6d00be163..51fc25d47 100644 --- a/sdrbase/resources/webapi/doc/html2/index.html +++ b/sdrbase/resources/webapi/doc/html2/index.html @@ -3377,9 +3377,6 @@ margin-bottom: 20px; "UDPSinkReport" : { "$ref" : "#/definitions/UDPSinkReport" }, - "VORDemodMCReport" : { - "$ref" : "#/definitions/VORDemodMCReport" - }, "VORDemodReport" : { "$ref" : "#/definitions/VORDemodReport" }, @@ -3538,9 +3535,6 @@ margin-bottom: 20px; "UDPSinkSettings" : { "$ref" : "#/definitions/UDPSinkSettings" }, - "VORDemodMCSettings" : { - "$ref" : "#/definitions/VORDemodMCSettings" - }, "VORDemodSettings" : { "$ref" : "#/definitions/VORDemodSettings" }, @@ -13615,83 +13609,6 @@ margin-bottom: 20px; } }, "description" : "USRP" -}; - defs.VORDemodMCReport = { - "properties" : { - "channelPowerDB" : { - "type" : "number", - "format" : "float", - "description" : "power received in channel (dB)" - }, - "squelch" : { - "type" : "integer", - "description" : "squelch status (1 if open else 0)" - }, - "audioSampleRate" : { - "type" : "integer" - } - }, - "description" : "VORDemodMC" -}; - defs.VORDemodMCSettings = { - "properties" : { - "squelch" : { - "type" : "number", - "format" : "float", - "description" : "power squelch threshold in decibels" - }, - "volume" : { - "type" : "number", - "format" : "float" - }, - "audioMute" : { - "type" : "integer" - }, - "rgbColor" : { - "type" : "integer" - }, - "title" : { - "type" : "string" - }, - "audioDeviceName" : { - "type" : "string" - }, - "streamIndex" : { - "type" : "integer", - "description" : "MIMO channel. Not relevant when connected to SI (single Rx)." - }, - "useReverseAPI" : { - "type" : "integer", - "description" : "Synchronize with reverse API (1 for yes, 0 for no)" - }, - "reverseAPIAddress" : { - "type" : "string" - }, - "reverseAPIPort" : { - "type" : "integer" - }, - "reverseAPIDeviceIndex" : { - "type" : "integer" - }, - "reverseAPIChannelIndex" : { - "type" : "integer" - }, - "identThreshold" : { - "type" : "integer", - "description" : "Morse code ident threshold (linear SNR)" - }, - "magDecAdjust" : { - "type" : "integer", - "description" : "Adjust radial lines on map for magnetic declination of VOR" - }, - "channelMarker" : { - "$ref" : "#/definitions/ChannelMarker" - }, - "rollupState" : { - "$ref" : "#/definitions/RollupState" - } - }, - "description" : "VORDemodMC" }; defs.VORDemodReport = { "properties" : { @@ -52552,7 +52469,7 @@ except ApiException as e:
    - Generated 2022-05-01T11:46:55.368+02:00 + Generated 2022-05-01T22:14:53.451+02:00
    diff --git a/sdrbase/resources/webapi/doc/swagger/include/ChannelReport.yaml b/sdrbase/resources/webapi/doc/swagger/include/ChannelReport.yaml index ffe69ddeb..df8b65951 100644 --- a/sdrbase/resources/webapi/doc/swagger/include/ChannelReport.yaml +++ b/sdrbase/resources/webapi/doc/swagger/include/ChannelReport.yaml @@ -77,8 +77,8 @@ ChannelReport: $ref: "/doc/swagger/include/UDPSource.yaml#/UDPSourceReport" UDPSinkReport: $ref: "/doc/swagger/include/UDPSink.yaml#/UDPSinkReport" - VORDemodMCReport: - $ref: "/doc/swagger/include/VORDemodMC.yaml#/VORDemodMCReport" + # VORDemodMCReport: + # $ref: "/doc/swagger/include/VORDemodMC.yaml#/VORDemodMCReport" VORDemodReport: $ref: "/doc/swagger/include/VORDemod.yaml#/VORDemodReport" WFMDemodReport: diff --git a/sdrbase/resources/webapi/doc/swagger/include/ChannelSettings.yaml b/sdrbase/resources/webapi/doc/swagger/include/ChannelSettings.yaml index 6bf8bb473..126328d40 100644 --- a/sdrbase/resources/webapi/doc/swagger/include/ChannelSettings.yaml +++ b/sdrbase/resources/webapi/doc/swagger/include/ChannelSettings.yaml @@ -101,8 +101,8 @@ ChannelSettings: $ref: "/doc/swagger/include/UDPSource.yaml#/UDPSourceSettings" UDPSinkSettings: $ref: "/doc/swagger/include/UDPSink.yaml#/UDPSinkSettings" - VORDemodMCSettings: - $ref: "/doc/swagger/include/VORDemodMC.yaml#/VORDemodMCSettings" + # VORDemodMCSettings: + # $ref: "/doc/swagger/include/VORDemodMC.yaml#/VORDemodMCSettings" VORDemodSettings: $ref: "/doc/swagger/include/VORDemod.yaml#/VORDemodSettings" WFMDemodSettings: diff --git a/sdrbase/resources/webapi/doc/swagger/include/VORDemodSC.yaml b/sdrbase/resources/webapi/doc/swagger/include/VORDemodSC.yaml deleted file mode 100644 index 1b47c5c1c..000000000 --- a/sdrbase/resources/webapi/doc/swagger/include/VORDemodSC.yaml +++ /dev/null @@ -1,97 +0,0 @@ -VORDemodSCSettings: - description: VORDemodSC - properties: - inputFrequencyOffset: - type: integer - format: int64 - navId: - description: VOR unique identifier when set by VOR localizer feature - type: integer - squelch: - description: power squelch threshold in decibels - type: number - format: float - volume: - type: number - format: float - audioMute: - type: integer - rgbColor: - type: integer - title: - type: string - audioDeviceName: - type: string - streamIndex: - description: MIMO channel. Not relevant when connected to SI (single Rx). - type: integer - useReverseAPI: - description: Synchronize with reverse API (1 for yes, 0 for no) - type: integer - reverseAPIAddress: - type: string - reverseAPIPort: - type: integer - reverseAPIDeviceIndex: - type: integer - reverseAPIChannelIndex: - type: integer - identThreshold: - description: Morse code ident threshold (linear SNR) - type: integer - channelMarker: - $ref: "/doc/swagger/include/ChannelMarker.yaml#/ChannelMarker" - rollupState: - $ref: "/doc/swagger/include/RollupState.yaml#/RollupState" - -VORDemodSCReport: - description: VORDemodSC - properties: - channelPowerDB: - description: power received in channel (dB) - type: number - format: float - squelch: - description: squelch status (1 if open else 0) - type: integer - audioSampleRate: - type: integer - volume: - type: number - format: float - navId: - description: VOR unique identifier when set by VOR localizer feature - type: integer - radial: - description: current detected radial (degrees) - type: number - format: float - refMag: - description: current reference signal magnitude (dB) - type: number - format: float - varMag: - description: current variable signal magnitude (dB) - type: number - format: float - validRadial: - type: integer - description: > - Radial validity estimation - * 0 - Radial is invalid - * 1 - Radial is valid - validRefMag: - type: integer - description: > - Reference signal magnitude validity - * 0 - Magnitude below threshold - * 1 - Magnitude above threshold - validVarMag: - type: integer - description: > - Variable signal magnitude validity - * 0 - Magnitude below threshold - * 1 - Magnitude above threshold - morseIdent: - description: current identification morse code transcript - type: string diff --git a/sdrbase/webapi/webapirequestmapper.cpp b/sdrbase/webapi/webapirequestmapper.cpp index 05694994b..ad7abb888 100644 --- a/sdrbase/webapi/webapirequestmapper.cpp +++ b/sdrbase/webapi/webapirequestmapper.cpp @@ -4357,11 +4357,11 @@ bool WebAPIRequestMapper::getChannelSettings( channelSettings->setUdpSinkSettings(new SWGSDRangel::SWGUDPSinkSettings()); channelSettings->getUdpSinkSettings()->fromJsonObject(settingsJsonObject); } - else if (channelSettingsKey == "VORDemodMCSettings") - { - channelSettings->setVorDemodMcSettings(new SWGSDRangel::SWGVORDemodMCSettings()); - channelSettings->getVorDemodMcSettings()->fromJsonObject(settingsJsonObject); - } + // else if (channelSettingsKey == "VORDemodMCSettings") + // { + // channelSettings->setVorDemodMcSettings(new SWGSDRangel::SWGVORDemodMCSettings()); + // channelSettings->getVorDemodMcSettings()->fromJsonObject(settingsJsonObject); + // } else if (channelSettingsKey == "VORDemodSettings") { channelSettings->setVorDemodSettings(new SWGSDRangel::SWGVORDemodSettings()); @@ -5082,7 +5082,7 @@ void WebAPIRequestMapper::resetChannelSettings(SWGSDRangel::SWGChannelSettings& channelSettings.setSsbModSettings(nullptr); channelSettings.setUdpSourceSettings(nullptr); channelSettings.setUdpSinkSettings(nullptr); - channelSettings.setVorDemodMcSettings(nullptr); + // channelSettings.setVorDemodMcSettings(nullptr); channelSettings.setVorDemodSettings(nullptr); channelSettings.setWfmDemodSettings(nullptr); channelSettings.setWfmModSettings(nullptr); @@ -5114,7 +5114,7 @@ void WebAPIRequestMapper::resetChannelReport(SWGSDRangel::SWGChannelReport& chan channelReport.setSsbModReport(nullptr); channelReport.setUdpSourceReport(nullptr); channelReport.setUdpSinkReport(nullptr); - channelReport.setVorDemodMcReport(nullptr); + // channelReport.setVorDemodMcReport(nullptr); channelReport.setVorDemodReport(nullptr); channelReport.setWfmDemodReport(nullptr); channelReport.setWfmModReport(nullptr); diff --git a/swagger/sdrangel/api/swagger/include/ChannelReport.yaml b/swagger/sdrangel/api/swagger/include/ChannelReport.yaml index f4fc3ca98..deda36e1b 100644 --- a/swagger/sdrangel/api/swagger/include/ChannelReport.yaml +++ b/swagger/sdrangel/api/swagger/include/ChannelReport.yaml @@ -77,8 +77,8 @@ ChannelReport: $ref: "http://swgserver:8081/api/swagger/include/UDPSource.yaml#/UDPSourceReport" UDPSinkReport: $ref: "http://swgserver:8081/api/swagger/include/UDPSink.yaml#/UDPSinkReport" - VORDemodMCReport: - $ref: "http://swgserver:8081/api/swagger/include/VORDemodMC.yaml#/VORDemodMCReport" + # VORDemodMCReport: + # $ref: "http://swgserver:8081/api/swagger/include/VORDemodMC.yaml#/VORDemodMCReport" VORDemodReport: $ref: "http://swgserver:8081/api/swagger/include/VORDemod.yaml#/VORDemodReport" WFMDemodReport: diff --git a/swagger/sdrangel/api/swagger/include/ChannelSettings.yaml b/swagger/sdrangel/api/swagger/include/ChannelSettings.yaml index 0f9648469..b7c594db1 100644 --- a/swagger/sdrangel/api/swagger/include/ChannelSettings.yaml +++ b/swagger/sdrangel/api/swagger/include/ChannelSettings.yaml @@ -101,8 +101,8 @@ ChannelSettings: $ref: "http://swgserver:8081/api/swagger/include/UDPSource.yaml#/UDPSourceSettings" UDPSinkSettings: $ref: "http://swgserver:8081/api/swagger/include/UDPSink.yaml#/UDPSinkSettings" - VORDemodMCSettings: - $ref: "http://swgserver:8081/api/swagger/include/VORDemodMC.yaml#/VORDemodMCSettings" + # VORDemodMCSettings: + # $ref: "http://swgserver:8081/api/swagger/include/VORDemodMC.yaml#/VORDemodMCSettings" VORDemodSettings: $ref: "http://swgserver:8081/api/swagger/include/VORDemod.yaml#/VORDemodSettings" WFMDemodSettings: diff --git a/swagger/sdrangel/code/html2/index.html b/swagger/sdrangel/code/html2/index.html index 6d00be163..51fc25d47 100644 --- a/swagger/sdrangel/code/html2/index.html +++ b/swagger/sdrangel/code/html2/index.html @@ -3377,9 +3377,6 @@ margin-bottom: 20px; "UDPSinkReport" : { "$ref" : "#/definitions/UDPSinkReport" }, - "VORDemodMCReport" : { - "$ref" : "#/definitions/VORDemodMCReport" - }, "VORDemodReport" : { "$ref" : "#/definitions/VORDemodReport" }, @@ -3538,9 +3535,6 @@ margin-bottom: 20px; "UDPSinkSettings" : { "$ref" : "#/definitions/UDPSinkSettings" }, - "VORDemodMCSettings" : { - "$ref" : "#/definitions/VORDemodMCSettings" - }, "VORDemodSettings" : { "$ref" : "#/definitions/VORDemodSettings" }, @@ -13615,83 +13609,6 @@ margin-bottom: 20px; } }, "description" : "USRP" -}; - defs.VORDemodMCReport = { - "properties" : { - "channelPowerDB" : { - "type" : "number", - "format" : "float", - "description" : "power received in channel (dB)" - }, - "squelch" : { - "type" : "integer", - "description" : "squelch status (1 if open else 0)" - }, - "audioSampleRate" : { - "type" : "integer" - } - }, - "description" : "VORDemodMC" -}; - defs.VORDemodMCSettings = { - "properties" : { - "squelch" : { - "type" : "number", - "format" : "float", - "description" : "power squelch threshold in decibels" - }, - "volume" : { - "type" : "number", - "format" : "float" - }, - "audioMute" : { - "type" : "integer" - }, - "rgbColor" : { - "type" : "integer" - }, - "title" : { - "type" : "string" - }, - "audioDeviceName" : { - "type" : "string" - }, - "streamIndex" : { - "type" : "integer", - "description" : "MIMO channel. Not relevant when connected to SI (single Rx)." - }, - "useReverseAPI" : { - "type" : "integer", - "description" : "Synchronize with reverse API (1 for yes, 0 for no)" - }, - "reverseAPIAddress" : { - "type" : "string" - }, - "reverseAPIPort" : { - "type" : "integer" - }, - "reverseAPIDeviceIndex" : { - "type" : "integer" - }, - "reverseAPIChannelIndex" : { - "type" : "integer" - }, - "identThreshold" : { - "type" : "integer", - "description" : "Morse code ident threshold (linear SNR)" - }, - "magDecAdjust" : { - "type" : "integer", - "description" : "Adjust radial lines on map for magnetic declination of VOR" - }, - "channelMarker" : { - "$ref" : "#/definitions/ChannelMarker" - }, - "rollupState" : { - "$ref" : "#/definitions/RollupState" - } - }, - "description" : "VORDemodMC" }; defs.VORDemodReport = { "properties" : { @@ -52552,7 +52469,7 @@ except ApiException as e:
    - Generated 2022-05-01T11:46:55.368+02:00 + Generated 2022-05-01T22:14:53.451+02:00
    diff --git a/swagger/sdrangel/code/qt5/client/SWGChannelReport.cpp b/swagger/sdrangel/code/qt5/client/SWGChannelReport.cpp index e14c302eb..330884bb2 100644 --- a/swagger/sdrangel/code/qt5/client/SWGChannelReport.cpp +++ b/swagger/sdrangel/code/qt5/client/SWGChannelReport.cpp @@ -98,8 +98,6 @@ SWGChannelReport::SWGChannelReport() { m_udp_source_report_isSet = false; udp_sink_report = nullptr; m_udp_sink_report_isSet = false; - vor_demod_mc_report = nullptr; - m_vor_demod_mc_report_isSet = false; vor_demod_report = nullptr; m_vor_demod_report_isSet = false; wfm_demod_report = nullptr; @@ -184,8 +182,6 @@ SWGChannelReport::init() { m_udp_source_report_isSet = false; udp_sink_report = new SWGUDPSinkReport(); m_udp_sink_report_isSet = false; - vor_demod_mc_report = new SWGVORDemodMCReport(); - m_vor_demod_mc_report_isSet = false; vor_demod_report = new SWGVORDemodReport(); m_vor_demod_report_isSet = false; wfm_demod_report = new SWGWFMDemodReport(); @@ -299,9 +295,6 @@ SWGChannelReport::cleanup() { if(udp_sink_report != nullptr) { delete udp_sink_report; } - if(vor_demod_mc_report != nullptr) { - delete vor_demod_mc_report; - } if(vor_demod_report != nullptr) { delete vor_demod_report; } @@ -394,8 +387,6 @@ SWGChannelReport::fromJsonObject(QJsonObject &pJson) { ::SWGSDRangel::setValue(&udp_sink_report, pJson["UDPSinkReport"], "SWGUDPSinkReport", "SWGUDPSinkReport"); - ::SWGSDRangel::setValue(&vor_demod_mc_report, pJson["VORDemodMCReport"], "SWGVORDemodMCReport", "SWGVORDemodMCReport"); - ::SWGSDRangel::setValue(&vor_demod_report, pJson["VORDemodReport"], "SWGVORDemodReport", "SWGVORDemodReport"); ::SWGSDRangel::setValue(&wfm_demod_report, pJson["WFMDemodReport"], "SWGWFMDemodReport", "SWGWFMDemodReport"); @@ -523,9 +514,6 @@ SWGChannelReport::asJsonObject() { if((udp_sink_report != nullptr) && (udp_sink_report->isSet())){ toJsonValue(QString("UDPSinkReport"), udp_sink_report, obj, QString("SWGUDPSinkReport")); } - if((vor_demod_mc_report != nullptr) && (vor_demod_mc_report->isSet())){ - toJsonValue(QString("VORDemodMCReport"), vor_demod_mc_report, obj, QString("SWGVORDemodMCReport")); - } if((vor_demod_report != nullptr) && (vor_demod_report->isSet())){ toJsonValue(QString("VORDemodReport"), vor_demod_report, obj, QString("SWGVORDemodReport")); } @@ -889,16 +877,6 @@ SWGChannelReport::setUdpSinkReport(SWGUDPSinkReport* udp_sink_report) { this->m_udp_sink_report_isSet = true; } -SWGVORDemodMCReport* -SWGChannelReport::getVorDemodMcReport() { - return vor_demod_mc_report; -} -void -SWGChannelReport::setVorDemodMcReport(SWGVORDemodMCReport* vor_demod_mc_report) { - this->vor_demod_mc_report = vor_demod_mc_report; - this->m_vor_demod_mc_report_isSet = true; -} - SWGVORDemodReport* SWGChannelReport::getVorDemodReport() { return vor_demod_report; @@ -1039,9 +1017,6 @@ SWGChannelReport::isSet(){ if(udp_sink_report && udp_sink_report->isSet()){ isObjectUpdated = true; break; } - if(vor_demod_mc_report && vor_demod_mc_report->isSet()){ - isObjectUpdated = true; break; - } if(vor_demod_report && vor_demod_report->isSet()){ isObjectUpdated = true; break; } diff --git a/swagger/sdrangel/code/qt5/client/SWGChannelReport.h b/swagger/sdrangel/code/qt5/client/SWGChannelReport.h index 023ec6d43..df9bff4a9 100644 --- a/swagger/sdrangel/code/qt5/client/SWGChannelReport.h +++ b/swagger/sdrangel/code/qt5/client/SWGChannelReport.h @@ -55,7 +55,6 @@ #include "SWGSigMFFileSinkReport.h" #include "SWGUDPSinkReport.h" #include "SWGUDPSourceReport.h" -#include "SWGVORDemodMCReport.h" #include "SWGVORDemodReport.h" #include "SWGWFMDemodReport.h" #include "SWGWFMModReport.h" @@ -184,9 +183,6 @@ public: SWGUDPSinkReport* getUdpSinkReport(); void setUdpSinkReport(SWGUDPSinkReport* udp_sink_report); - SWGVORDemodMCReport* getVorDemodMcReport(); - void setVorDemodMcReport(SWGVORDemodMCReport* vor_demod_mc_report); - SWGVORDemodReport* getVorDemodReport(); void setVorDemodReport(SWGVORDemodReport* vor_demod_report); @@ -305,9 +301,6 @@ private: SWGUDPSinkReport* udp_sink_report; bool m_udp_sink_report_isSet; - SWGVORDemodMCReport* vor_demod_mc_report; - bool m_vor_demod_mc_report_isSet; - SWGVORDemodReport* vor_demod_report; bool m_vor_demod_report_isSet; diff --git a/swagger/sdrangel/code/qt5/client/SWGChannelSettings.cpp b/swagger/sdrangel/code/qt5/client/SWGChannelSettings.cpp index abc0dcd94..6002e8c84 100644 --- a/swagger/sdrangel/code/qt5/client/SWGChannelSettings.cpp +++ b/swagger/sdrangel/code/qt5/client/SWGChannelSettings.cpp @@ -120,8 +120,6 @@ SWGChannelSettings::SWGChannelSettings() { m_udp_source_settings_isSet = false; udp_sink_settings = nullptr; m_udp_sink_settings_isSet = false; - vor_demod_mc_settings = nullptr; - m_vor_demod_mc_settings_isSet = false; vor_demod_settings = nullptr; m_vor_demod_settings_isSet = false; wfm_demod_settings = nullptr; @@ -228,8 +226,6 @@ SWGChannelSettings::init() { m_udp_source_settings_isSet = false; udp_sink_settings = new SWGUDPSinkSettings(); m_udp_sink_settings_isSet = false; - vor_demod_mc_settings = new SWGVORDemodMCSettings(); - m_vor_demod_mc_settings_isSet = false; vor_demod_settings = new SWGVORDemodSettings(); m_vor_demod_settings_isSet = false; wfm_demod_settings = new SWGWFMDemodSettings(); @@ -372,9 +368,6 @@ SWGChannelSettings::cleanup() { if(udp_sink_settings != nullptr) { delete udp_sink_settings; } - if(vor_demod_mc_settings != nullptr) { - delete vor_demod_mc_settings; - } if(vor_demod_settings != nullptr) { delete vor_demod_settings; } @@ -489,8 +482,6 @@ SWGChannelSettings::fromJsonObject(QJsonObject &pJson) { ::SWGSDRangel::setValue(&udp_sink_settings, pJson["UDPSinkSettings"], "SWGUDPSinkSettings", "SWGUDPSinkSettings"); - ::SWGSDRangel::setValue(&vor_demod_mc_settings, pJson["VORDemodMCSettings"], "SWGVORDemodMCSettings", "SWGVORDemodMCSettings"); - ::SWGSDRangel::setValue(&vor_demod_settings, pJson["VORDemodSettings"], "SWGVORDemodSettings", "SWGVORDemodSettings"); ::SWGSDRangel::setValue(&wfm_demod_settings, pJson["WFMDemodSettings"], "SWGWFMDemodSettings", "SWGWFMDemodSettings"); @@ -651,9 +642,6 @@ SWGChannelSettings::asJsonObject() { if((udp_sink_settings != nullptr) && (udp_sink_settings->isSet())){ toJsonValue(QString("UDPSinkSettings"), udp_sink_settings, obj, QString("SWGUDPSinkSettings")); } - if((vor_demod_mc_settings != nullptr) && (vor_demod_mc_settings->isSet())){ - toJsonValue(QString("VORDemodMCSettings"), vor_demod_mc_settings, obj, QString("SWGVORDemodMCSettings")); - } if((vor_demod_settings != nullptr) && (vor_demod_settings->isSet())){ toJsonValue(QString("VORDemodSettings"), vor_demod_settings, obj, QString("SWGVORDemodSettings")); } @@ -1127,16 +1115,6 @@ SWGChannelSettings::setUdpSinkSettings(SWGUDPSinkSettings* udp_sink_settings) { this->m_udp_sink_settings_isSet = true; } -SWGVORDemodMCSettings* -SWGChannelSettings::getVorDemodMcSettings() { - return vor_demod_mc_settings; -} -void -SWGChannelSettings::setVorDemodMcSettings(SWGVORDemodMCSettings* vor_demod_mc_settings) { - this->vor_demod_mc_settings = vor_demod_mc_settings; - this->m_vor_demod_mc_settings_isSet = true; -} - SWGVORDemodSettings* SWGChannelSettings::getVorDemodSettings() { return vor_demod_settings; @@ -1310,9 +1288,6 @@ SWGChannelSettings::isSet(){ if(udp_sink_settings && udp_sink_settings->isSet()){ isObjectUpdated = true; break; } - if(vor_demod_mc_settings && vor_demod_mc_settings->isSet()){ - isObjectUpdated = true; break; - } if(vor_demod_settings && vor_demod_settings->isSet()){ isObjectUpdated = true; break; } diff --git a/swagger/sdrangel/code/qt5/client/SWGChannelSettings.h b/swagger/sdrangel/code/qt5/client/SWGChannelSettings.h index a87e34202..14960de0b 100644 --- a/swagger/sdrangel/code/qt5/client/SWGChannelSettings.h +++ b/swagger/sdrangel/code/qt5/client/SWGChannelSettings.h @@ -64,7 +64,6 @@ #include "SWGSigMFFileSinkSettings.h" #include "SWGUDPSinkSettings.h" #include "SWGUDPSourceSettings.h" -#include "SWGVORDemodMCSettings.h" #include "SWGVORDemodSettings.h" #include "SWGWFMDemodSettings.h" #include "SWGWFMModSettings.h" @@ -226,9 +225,6 @@ public: SWGUDPSinkSettings* getUdpSinkSettings(); void setUdpSinkSettings(SWGUDPSinkSettings* udp_sink_settings); - SWGVORDemodMCSettings* getVorDemodMcSettings(); - void setVorDemodMcSettings(SWGVORDemodMCSettings* vor_demod_mc_settings); - SWGVORDemodSettings* getVorDemodSettings(); void setVorDemodSettings(SWGVORDemodSettings* vor_demod_settings); @@ -380,9 +376,6 @@ private: SWGUDPSinkSettings* udp_sink_settings; bool m_udp_sink_settings_isSet; - SWGVORDemodMCSettings* vor_demod_mc_settings; - bool m_vor_demod_mc_settings_isSet; - SWGVORDemodSettings* vor_demod_settings; bool m_vor_demod_settings_isSet; diff --git a/swagger/sdrangel/code/qt5/client/SWGModelFactory.h b/swagger/sdrangel/code/qt5/client/SWGModelFactory.h index e5f64bc64..6ff46686e 100644 --- a/swagger/sdrangel/code/qt5/client/SWGModelFactory.h +++ b/swagger/sdrangel/code/qt5/client/SWGModelFactory.h @@ -302,8 +302,6 @@ #include "SWGUSRPInputSettings.h" #include "SWGUSRPOutputReport.h" #include "SWGUSRPOutputSettings.h" -#include "SWGVORDemodMCReport.h" -#include "SWGVORDemodMCSettings.h" #include "SWGVORDemodReport.h" #include "SWGVORDemodSettings.h" #include "SWGVORLocalizerActions.h" @@ -1763,16 +1761,6 @@ namespace SWGSDRangel { obj->init(); return obj; } - if(QString("SWGVORDemodMCReport").compare(type) == 0) { - SWGVORDemodMCReport *obj = new SWGVORDemodMCReport(); - obj->init(); - return obj; - } - if(QString("SWGVORDemodMCSettings").compare(type) == 0) { - SWGVORDemodMCSettings *obj = new SWGVORDemodMCSettings(); - obj->init(); - return obj; - } if(QString("SWGVORDemodReport").compare(type) == 0) { SWGVORDemodReport *obj = new SWGVORDemodReport(); obj->init(); diff --git a/swagger/sdrangel/code/qt5/client/SWGVORDemodMCReport.cpp b/swagger/sdrangel/code/qt5/client/SWGVORDemodMCReport.cpp deleted file mode 100644 index b986139f7..000000000 --- a/swagger/sdrangel/code/qt5/client/SWGVORDemodMCReport.cpp +++ /dev/null @@ -1,154 +0,0 @@ -/** - * SDRangel - * This is the web REST/JSON API of SDRangel SDR software. SDRangel is an Open Source Qt5/OpenGL 3.0+ (4.3+ in Windows) GUI and server Software Defined Radio and signal analyzer in software. It supports Airspy, BladeRF, HackRF, LimeSDR, PlutoSDR, RTL-SDR, SDRplay RSP1 and FunCube --- Limitations and specifcities: * In SDRangel GUI the first Rx device set cannot be deleted. Conversely the server starts with no device sets and its number of device sets can be reduced to zero by as many calls as necessary to /sdrangel/deviceset with DELETE method. * Preset import and export from/to file is a server only feature. * Device set focus is a GUI only feature. * The following channels are not implemented (status 501 is returned): ATV and DATV demodulators, Channel Analyzer NG, LoRa demodulator * The device settings and report structures contains only the sub-structure corresponding to the device type. The DeviceSettings and DeviceReport structures documented here shows all of them but only one will be or should be present at a time * The channel settings and report structures contains only the sub-structure corresponding to the channel type. The ChannelSettings and ChannelReport structures documented here shows all of them but only one will be or should be present at a time --- - * - * OpenAPI spec version: 6.0.0 - * Contact: f4exb06@gmail.com - * - * NOTE: This class is auto generated by the swagger code generator program. - * https://github.com/swagger-api/swagger-codegen.git - * Do not edit the class manually. - */ - - -#include "SWGVORDemodMCReport.h" - -#include "SWGHelpers.h" - -#include -#include -#include -#include - -namespace SWGSDRangel { - -SWGVORDemodMCReport::SWGVORDemodMCReport(QString* json) { - init(); - this->fromJson(*json); -} - -SWGVORDemodMCReport::SWGVORDemodMCReport() { - channel_power_db = 0.0f; - m_channel_power_db_isSet = false; - squelch = 0; - m_squelch_isSet = false; - audio_sample_rate = 0; - m_audio_sample_rate_isSet = false; -} - -SWGVORDemodMCReport::~SWGVORDemodMCReport() { - this->cleanup(); -} - -void -SWGVORDemodMCReport::init() { - channel_power_db = 0.0f; - m_channel_power_db_isSet = false; - squelch = 0; - m_squelch_isSet = false; - audio_sample_rate = 0; - m_audio_sample_rate_isSet = false; -} - -void -SWGVORDemodMCReport::cleanup() { - - - -} - -SWGVORDemodMCReport* -SWGVORDemodMCReport::fromJson(QString &json) { - QByteArray array (json.toStdString().c_str()); - QJsonDocument doc = QJsonDocument::fromJson(array); - QJsonObject jsonObject = doc.object(); - this->fromJsonObject(jsonObject); - return this; -} - -void -SWGVORDemodMCReport::fromJsonObject(QJsonObject &pJson) { - ::SWGSDRangel::setValue(&channel_power_db, pJson["channelPowerDB"], "float", ""); - - ::SWGSDRangel::setValue(&squelch, pJson["squelch"], "qint32", ""); - - ::SWGSDRangel::setValue(&audio_sample_rate, pJson["audioSampleRate"], "qint32", ""); - -} - -QString -SWGVORDemodMCReport::asJson () -{ - QJsonObject* obj = this->asJsonObject(); - - QJsonDocument doc(*obj); - QByteArray bytes = doc.toJson(); - delete obj; - return QString(bytes); -} - -QJsonObject* -SWGVORDemodMCReport::asJsonObject() { - QJsonObject* obj = new QJsonObject(); - if(m_channel_power_db_isSet){ - obj->insert("channelPowerDB", QJsonValue(channel_power_db)); - } - if(m_squelch_isSet){ - obj->insert("squelch", QJsonValue(squelch)); - } - if(m_audio_sample_rate_isSet){ - obj->insert("audioSampleRate", QJsonValue(audio_sample_rate)); - } - - return obj; -} - -float -SWGVORDemodMCReport::getChannelPowerDb() { - return channel_power_db; -} -void -SWGVORDemodMCReport::setChannelPowerDb(float channel_power_db) { - this->channel_power_db = channel_power_db; - this->m_channel_power_db_isSet = true; -} - -qint32 -SWGVORDemodMCReport::getSquelch() { - return squelch; -} -void -SWGVORDemodMCReport::setSquelch(qint32 squelch) { - this->squelch = squelch; - this->m_squelch_isSet = true; -} - -qint32 -SWGVORDemodMCReport::getAudioSampleRate() { - return audio_sample_rate; -} -void -SWGVORDemodMCReport::setAudioSampleRate(qint32 audio_sample_rate) { - this->audio_sample_rate = audio_sample_rate; - this->m_audio_sample_rate_isSet = true; -} - - -bool -SWGVORDemodMCReport::isSet(){ - bool isObjectUpdated = false; - do{ - if(m_channel_power_db_isSet){ - isObjectUpdated = true; break; - } - if(m_squelch_isSet){ - isObjectUpdated = true; break; - } - if(m_audio_sample_rate_isSet){ - isObjectUpdated = true; break; - } - }while(false); - return isObjectUpdated; -} -} - diff --git a/swagger/sdrangel/code/qt5/client/SWGVORDemodMCReport.h b/swagger/sdrangel/code/qt5/client/SWGVORDemodMCReport.h deleted file mode 100644 index 37927bd75..000000000 --- a/swagger/sdrangel/code/qt5/client/SWGVORDemodMCReport.h +++ /dev/null @@ -1,70 +0,0 @@ -/** - * SDRangel - * This is the web REST/JSON API of SDRangel SDR software. SDRangel is an Open Source Qt5/OpenGL 3.0+ (4.3+ in Windows) GUI and server Software Defined Radio and signal analyzer in software. It supports Airspy, BladeRF, HackRF, LimeSDR, PlutoSDR, RTL-SDR, SDRplay RSP1 and FunCube --- Limitations and specifcities: * In SDRangel GUI the first Rx device set cannot be deleted. Conversely the server starts with no device sets and its number of device sets can be reduced to zero by as many calls as necessary to /sdrangel/deviceset with DELETE method. * Preset import and export from/to file is a server only feature. * Device set focus is a GUI only feature. * The following channels are not implemented (status 501 is returned): ATV and DATV demodulators, Channel Analyzer NG, LoRa demodulator * The device settings and report structures contains only the sub-structure corresponding to the device type. The DeviceSettings and DeviceReport structures documented here shows all of them but only one will be or should be present at a time * The channel settings and report structures contains only the sub-structure corresponding to the channel type. The ChannelSettings and ChannelReport structures documented here shows all of them but only one will be or should be present at a time --- - * - * OpenAPI spec version: 6.0.0 - * Contact: f4exb06@gmail.com - * - * NOTE: This class is auto generated by the swagger code generator program. - * https://github.com/swagger-api/swagger-codegen.git - * Do not edit the class manually. - */ - -/* - * SWGVORDemodMCReport.h - * - * VORDemodMC - */ - -#ifndef SWGVORDemodMCReport_H_ -#define SWGVORDemodMCReport_H_ - -#include - - - -#include "SWGObject.h" -#include "export.h" - -namespace SWGSDRangel { - -class SWG_API SWGVORDemodMCReport: public SWGObject { -public: - SWGVORDemodMCReport(); - SWGVORDemodMCReport(QString* json); - virtual ~SWGVORDemodMCReport(); - void init(); - void cleanup(); - - virtual QString asJson () override; - virtual QJsonObject* asJsonObject() override; - virtual void fromJsonObject(QJsonObject &json) override; - virtual SWGVORDemodMCReport* fromJson(QString &jsonString) override; - - float getChannelPowerDb(); - void setChannelPowerDb(float channel_power_db); - - qint32 getSquelch(); - void setSquelch(qint32 squelch); - - qint32 getAudioSampleRate(); - void setAudioSampleRate(qint32 audio_sample_rate); - - - virtual bool isSet() override; - -private: - float channel_power_db; - bool m_channel_power_db_isSet; - - qint32 squelch; - bool m_squelch_isSet; - - qint32 audio_sample_rate; - bool m_audio_sample_rate_isSet; - -}; - -} - -#endif /* SWGVORDemodMCReport_H_ */ diff --git a/swagger/sdrangel/code/qt5/client/SWGVORDemodMCSettings.cpp b/swagger/sdrangel/code/qt5/client/SWGVORDemodMCSettings.cpp deleted file mode 100644 index 1e7cc0201..000000000 --- a/swagger/sdrangel/code/qt5/client/SWGVORDemodMCSettings.cpp +++ /dev/null @@ -1,463 +0,0 @@ -/** - * SDRangel - * This is the web REST/JSON API of SDRangel SDR software. SDRangel is an Open Source Qt5/OpenGL 3.0+ (4.3+ in Windows) GUI and server Software Defined Radio and signal analyzer in software. It supports Airspy, BladeRF, HackRF, LimeSDR, PlutoSDR, RTL-SDR, SDRplay RSP1 and FunCube --- Limitations and specifcities: * In SDRangel GUI the first Rx device set cannot be deleted. Conversely the server starts with no device sets and its number of device sets can be reduced to zero by as many calls as necessary to /sdrangel/deviceset with DELETE method. * Preset import and export from/to file is a server only feature. * Device set focus is a GUI only feature. * The following channels are not implemented (status 501 is returned): ATV and DATV demodulators, Channel Analyzer NG, LoRa demodulator * The device settings and report structures contains only the sub-structure corresponding to the device type. The DeviceSettings and DeviceReport structures documented here shows all of them but only one will be or should be present at a time * The channel settings and report structures contains only the sub-structure corresponding to the channel type. The ChannelSettings and ChannelReport structures documented here shows all of them but only one will be or should be present at a time --- - * - * OpenAPI spec version: 6.0.0 - * Contact: f4exb06@gmail.com - * - * NOTE: This class is auto generated by the swagger code generator program. - * https://github.com/swagger-api/swagger-codegen.git - * Do not edit the class manually. - */ - - -#include "SWGVORDemodMCSettings.h" - -#include "SWGHelpers.h" - -#include -#include -#include -#include - -namespace SWGSDRangel { - -SWGVORDemodMCSettings::SWGVORDemodMCSettings(QString* json) { - init(); - this->fromJson(*json); -} - -SWGVORDemodMCSettings::SWGVORDemodMCSettings() { - squelch = 0.0f; - m_squelch_isSet = false; - volume = 0.0f; - m_volume_isSet = false; - audio_mute = 0; - m_audio_mute_isSet = false; - rgb_color = 0; - m_rgb_color_isSet = false; - title = nullptr; - m_title_isSet = false; - audio_device_name = nullptr; - m_audio_device_name_isSet = false; - stream_index = 0; - m_stream_index_isSet = false; - use_reverse_api = 0; - m_use_reverse_api_isSet = false; - reverse_api_address = nullptr; - m_reverse_api_address_isSet = false; - reverse_api_port = 0; - m_reverse_api_port_isSet = false; - reverse_api_device_index = 0; - m_reverse_api_device_index_isSet = false; - reverse_api_channel_index = 0; - m_reverse_api_channel_index_isSet = false; - ident_threshold = 0; - m_ident_threshold_isSet = false; - mag_dec_adjust = 0; - m_mag_dec_adjust_isSet = false; - channel_marker = nullptr; - m_channel_marker_isSet = false; - rollup_state = nullptr; - m_rollup_state_isSet = false; -} - -SWGVORDemodMCSettings::~SWGVORDemodMCSettings() { - this->cleanup(); -} - -void -SWGVORDemodMCSettings::init() { - squelch = 0.0f; - m_squelch_isSet = false; - volume = 0.0f; - m_volume_isSet = false; - audio_mute = 0; - m_audio_mute_isSet = false; - rgb_color = 0; - m_rgb_color_isSet = false; - title = new QString(""); - m_title_isSet = false; - audio_device_name = new QString(""); - m_audio_device_name_isSet = false; - stream_index = 0; - m_stream_index_isSet = false; - use_reverse_api = 0; - m_use_reverse_api_isSet = false; - reverse_api_address = new QString(""); - m_reverse_api_address_isSet = false; - reverse_api_port = 0; - m_reverse_api_port_isSet = false; - reverse_api_device_index = 0; - m_reverse_api_device_index_isSet = false; - reverse_api_channel_index = 0; - m_reverse_api_channel_index_isSet = false; - ident_threshold = 0; - m_ident_threshold_isSet = false; - mag_dec_adjust = 0; - m_mag_dec_adjust_isSet = false; - channel_marker = new SWGChannelMarker(); - m_channel_marker_isSet = false; - rollup_state = new SWGRollupState(); - m_rollup_state_isSet = false; -} - -void -SWGVORDemodMCSettings::cleanup() { - - - - - if(title != nullptr) { - delete title; - } - if(audio_device_name != nullptr) { - delete audio_device_name; - } - - - if(reverse_api_address != nullptr) { - delete reverse_api_address; - } - - - - - - if(channel_marker != nullptr) { - delete channel_marker; - } - if(rollup_state != nullptr) { - delete rollup_state; - } -} - -SWGVORDemodMCSettings* -SWGVORDemodMCSettings::fromJson(QString &json) { - QByteArray array (json.toStdString().c_str()); - QJsonDocument doc = QJsonDocument::fromJson(array); - QJsonObject jsonObject = doc.object(); - this->fromJsonObject(jsonObject); - return this; -} - -void -SWGVORDemodMCSettings::fromJsonObject(QJsonObject &pJson) { - ::SWGSDRangel::setValue(&squelch, pJson["squelch"], "float", ""); - - ::SWGSDRangel::setValue(&volume, pJson["volume"], "float", ""); - - ::SWGSDRangel::setValue(&audio_mute, pJson["audioMute"], "qint32", ""); - - ::SWGSDRangel::setValue(&rgb_color, pJson["rgbColor"], "qint32", ""); - - ::SWGSDRangel::setValue(&title, pJson["title"], "QString", "QString"); - - ::SWGSDRangel::setValue(&audio_device_name, pJson["audioDeviceName"], "QString", "QString"); - - ::SWGSDRangel::setValue(&stream_index, pJson["streamIndex"], "qint32", ""); - - ::SWGSDRangel::setValue(&use_reverse_api, pJson["useReverseAPI"], "qint32", ""); - - ::SWGSDRangel::setValue(&reverse_api_address, pJson["reverseAPIAddress"], "QString", "QString"); - - ::SWGSDRangel::setValue(&reverse_api_port, pJson["reverseAPIPort"], "qint32", ""); - - ::SWGSDRangel::setValue(&reverse_api_device_index, pJson["reverseAPIDeviceIndex"], "qint32", ""); - - ::SWGSDRangel::setValue(&reverse_api_channel_index, pJson["reverseAPIChannelIndex"], "qint32", ""); - - ::SWGSDRangel::setValue(&ident_threshold, pJson["identThreshold"], "qint32", ""); - - ::SWGSDRangel::setValue(&mag_dec_adjust, pJson["magDecAdjust"], "qint32", ""); - - ::SWGSDRangel::setValue(&channel_marker, pJson["channelMarker"], "SWGChannelMarker", "SWGChannelMarker"); - - ::SWGSDRangel::setValue(&rollup_state, pJson["rollupState"], "SWGRollupState", "SWGRollupState"); - -} - -QString -SWGVORDemodMCSettings::asJson () -{ - QJsonObject* obj = this->asJsonObject(); - - QJsonDocument doc(*obj); - QByteArray bytes = doc.toJson(); - delete obj; - return QString(bytes); -} - -QJsonObject* -SWGVORDemodMCSettings::asJsonObject() { - QJsonObject* obj = new QJsonObject(); - if(m_squelch_isSet){ - obj->insert("squelch", QJsonValue(squelch)); - } - if(m_volume_isSet){ - obj->insert("volume", QJsonValue(volume)); - } - if(m_audio_mute_isSet){ - obj->insert("audioMute", QJsonValue(audio_mute)); - } - if(m_rgb_color_isSet){ - obj->insert("rgbColor", QJsonValue(rgb_color)); - } - if(title != nullptr && *title != QString("")){ - toJsonValue(QString("title"), title, obj, QString("QString")); - } - if(audio_device_name != nullptr && *audio_device_name != QString("")){ - toJsonValue(QString("audioDeviceName"), audio_device_name, obj, QString("QString")); - } - if(m_stream_index_isSet){ - obj->insert("streamIndex", QJsonValue(stream_index)); - } - if(m_use_reverse_api_isSet){ - obj->insert("useReverseAPI", QJsonValue(use_reverse_api)); - } - if(reverse_api_address != nullptr && *reverse_api_address != QString("")){ - toJsonValue(QString("reverseAPIAddress"), reverse_api_address, obj, QString("QString")); - } - if(m_reverse_api_port_isSet){ - obj->insert("reverseAPIPort", QJsonValue(reverse_api_port)); - } - if(m_reverse_api_device_index_isSet){ - obj->insert("reverseAPIDeviceIndex", QJsonValue(reverse_api_device_index)); - } - if(m_reverse_api_channel_index_isSet){ - obj->insert("reverseAPIChannelIndex", QJsonValue(reverse_api_channel_index)); - } - if(m_ident_threshold_isSet){ - obj->insert("identThreshold", QJsonValue(ident_threshold)); - } - if(m_mag_dec_adjust_isSet){ - obj->insert("magDecAdjust", QJsonValue(mag_dec_adjust)); - } - if((channel_marker != nullptr) && (channel_marker->isSet())){ - toJsonValue(QString("channelMarker"), channel_marker, obj, QString("SWGChannelMarker")); - } - if((rollup_state != nullptr) && (rollup_state->isSet())){ - toJsonValue(QString("rollupState"), rollup_state, obj, QString("SWGRollupState")); - } - - return obj; -} - -float -SWGVORDemodMCSettings::getSquelch() { - return squelch; -} -void -SWGVORDemodMCSettings::setSquelch(float squelch) { - this->squelch = squelch; - this->m_squelch_isSet = true; -} - -float -SWGVORDemodMCSettings::getVolume() { - return volume; -} -void -SWGVORDemodMCSettings::setVolume(float volume) { - this->volume = volume; - this->m_volume_isSet = true; -} - -qint32 -SWGVORDemodMCSettings::getAudioMute() { - return audio_mute; -} -void -SWGVORDemodMCSettings::setAudioMute(qint32 audio_mute) { - this->audio_mute = audio_mute; - this->m_audio_mute_isSet = true; -} - -qint32 -SWGVORDemodMCSettings::getRgbColor() { - return rgb_color; -} -void -SWGVORDemodMCSettings::setRgbColor(qint32 rgb_color) { - this->rgb_color = rgb_color; - this->m_rgb_color_isSet = true; -} - -QString* -SWGVORDemodMCSettings::getTitle() { - return title; -} -void -SWGVORDemodMCSettings::setTitle(QString* title) { - this->title = title; - this->m_title_isSet = true; -} - -QString* -SWGVORDemodMCSettings::getAudioDeviceName() { - return audio_device_name; -} -void -SWGVORDemodMCSettings::setAudioDeviceName(QString* audio_device_name) { - this->audio_device_name = audio_device_name; - this->m_audio_device_name_isSet = true; -} - -qint32 -SWGVORDemodMCSettings::getStreamIndex() { - return stream_index; -} -void -SWGVORDemodMCSettings::setStreamIndex(qint32 stream_index) { - this->stream_index = stream_index; - this->m_stream_index_isSet = true; -} - -qint32 -SWGVORDemodMCSettings::getUseReverseApi() { - return use_reverse_api; -} -void -SWGVORDemodMCSettings::setUseReverseApi(qint32 use_reverse_api) { - this->use_reverse_api = use_reverse_api; - this->m_use_reverse_api_isSet = true; -} - -QString* -SWGVORDemodMCSettings::getReverseApiAddress() { - return reverse_api_address; -} -void -SWGVORDemodMCSettings::setReverseApiAddress(QString* reverse_api_address) { - this->reverse_api_address = reverse_api_address; - this->m_reverse_api_address_isSet = true; -} - -qint32 -SWGVORDemodMCSettings::getReverseApiPort() { - return reverse_api_port; -} -void -SWGVORDemodMCSettings::setReverseApiPort(qint32 reverse_api_port) { - this->reverse_api_port = reverse_api_port; - this->m_reverse_api_port_isSet = true; -} - -qint32 -SWGVORDemodMCSettings::getReverseApiDeviceIndex() { - return reverse_api_device_index; -} -void -SWGVORDemodMCSettings::setReverseApiDeviceIndex(qint32 reverse_api_device_index) { - this->reverse_api_device_index = reverse_api_device_index; - this->m_reverse_api_device_index_isSet = true; -} - -qint32 -SWGVORDemodMCSettings::getReverseApiChannelIndex() { - return reverse_api_channel_index; -} -void -SWGVORDemodMCSettings::setReverseApiChannelIndex(qint32 reverse_api_channel_index) { - this->reverse_api_channel_index = reverse_api_channel_index; - this->m_reverse_api_channel_index_isSet = true; -} - -qint32 -SWGVORDemodMCSettings::getIdentThreshold() { - return ident_threshold; -} -void -SWGVORDemodMCSettings::setIdentThreshold(qint32 ident_threshold) { - this->ident_threshold = ident_threshold; - this->m_ident_threshold_isSet = true; -} - -qint32 -SWGVORDemodMCSettings::getMagDecAdjust() { - return mag_dec_adjust; -} -void -SWGVORDemodMCSettings::setMagDecAdjust(qint32 mag_dec_adjust) { - this->mag_dec_adjust = mag_dec_adjust; - this->m_mag_dec_adjust_isSet = true; -} - -SWGChannelMarker* -SWGVORDemodMCSettings::getChannelMarker() { - return channel_marker; -} -void -SWGVORDemodMCSettings::setChannelMarker(SWGChannelMarker* channel_marker) { - this->channel_marker = channel_marker; - this->m_channel_marker_isSet = true; -} - -SWGRollupState* -SWGVORDemodMCSettings::getRollupState() { - return rollup_state; -} -void -SWGVORDemodMCSettings::setRollupState(SWGRollupState* rollup_state) { - this->rollup_state = rollup_state; - this->m_rollup_state_isSet = true; -} - - -bool -SWGVORDemodMCSettings::isSet(){ - bool isObjectUpdated = false; - do{ - if(m_squelch_isSet){ - isObjectUpdated = true; break; - } - if(m_volume_isSet){ - isObjectUpdated = true; break; - } - if(m_audio_mute_isSet){ - isObjectUpdated = true; break; - } - if(m_rgb_color_isSet){ - isObjectUpdated = true; break; - } - if(title && *title != QString("")){ - isObjectUpdated = true; break; - } - if(audio_device_name && *audio_device_name != QString("")){ - isObjectUpdated = true; break; - } - if(m_stream_index_isSet){ - isObjectUpdated = true; break; - } - if(m_use_reverse_api_isSet){ - isObjectUpdated = true; break; - } - if(reverse_api_address && *reverse_api_address != QString("")){ - isObjectUpdated = true; break; - } - if(m_reverse_api_port_isSet){ - isObjectUpdated = true; break; - } - if(m_reverse_api_device_index_isSet){ - isObjectUpdated = true; break; - } - if(m_reverse_api_channel_index_isSet){ - isObjectUpdated = true; break; - } - if(m_ident_threshold_isSet){ - isObjectUpdated = true; break; - } - if(m_mag_dec_adjust_isSet){ - isObjectUpdated = true; break; - } - if(channel_marker && channel_marker->isSet()){ - isObjectUpdated = true; break; - } - if(rollup_state && rollup_state->isSet()){ - isObjectUpdated = true; break; - } - }while(false); - return isObjectUpdated; -} -} - diff --git a/swagger/sdrangel/code/qt5/client/SWGVORDemodMCSettings.h b/swagger/sdrangel/code/qt5/client/SWGVORDemodMCSettings.h deleted file mode 100644 index 3bd0f407f..000000000 --- a/swagger/sdrangel/code/qt5/client/SWGVORDemodMCSettings.h +++ /dev/null @@ -1,151 +0,0 @@ -/** - * SDRangel - * This is the web REST/JSON API of SDRangel SDR software. SDRangel is an Open Source Qt5/OpenGL 3.0+ (4.3+ in Windows) GUI and server Software Defined Radio and signal analyzer in software. It supports Airspy, BladeRF, HackRF, LimeSDR, PlutoSDR, RTL-SDR, SDRplay RSP1 and FunCube --- Limitations and specifcities: * In SDRangel GUI the first Rx device set cannot be deleted. Conversely the server starts with no device sets and its number of device sets can be reduced to zero by as many calls as necessary to /sdrangel/deviceset with DELETE method. * Preset import and export from/to file is a server only feature. * Device set focus is a GUI only feature. * The following channels are not implemented (status 501 is returned): ATV and DATV demodulators, Channel Analyzer NG, LoRa demodulator * The device settings and report structures contains only the sub-structure corresponding to the device type. The DeviceSettings and DeviceReport structures documented here shows all of them but only one will be or should be present at a time * The channel settings and report structures contains only the sub-structure corresponding to the channel type. The ChannelSettings and ChannelReport structures documented here shows all of them but only one will be or should be present at a time --- - * - * OpenAPI spec version: 6.0.0 - * Contact: f4exb06@gmail.com - * - * NOTE: This class is auto generated by the swagger code generator program. - * https://github.com/swagger-api/swagger-codegen.git - * Do not edit the class manually. - */ - -/* - * SWGVORDemodMCSettings.h - * - * VORDemodMC - */ - -#ifndef SWGVORDemodMCSettings_H_ -#define SWGVORDemodMCSettings_H_ - -#include - - -#include "SWGChannelMarker.h" -#include "SWGRollupState.h" -#include - -#include "SWGObject.h" -#include "export.h" - -namespace SWGSDRangel { - -class SWG_API SWGVORDemodMCSettings: public SWGObject { -public: - SWGVORDemodMCSettings(); - SWGVORDemodMCSettings(QString* json); - virtual ~SWGVORDemodMCSettings(); - void init(); - void cleanup(); - - virtual QString asJson () override; - virtual QJsonObject* asJsonObject() override; - virtual void fromJsonObject(QJsonObject &json) override; - virtual SWGVORDemodMCSettings* fromJson(QString &jsonString) override; - - float getSquelch(); - void setSquelch(float squelch); - - float getVolume(); - void setVolume(float volume); - - qint32 getAudioMute(); - void setAudioMute(qint32 audio_mute); - - qint32 getRgbColor(); - void setRgbColor(qint32 rgb_color); - - QString* getTitle(); - void setTitle(QString* title); - - QString* getAudioDeviceName(); - void setAudioDeviceName(QString* audio_device_name); - - qint32 getStreamIndex(); - void setStreamIndex(qint32 stream_index); - - qint32 getUseReverseApi(); - void setUseReverseApi(qint32 use_reverse_api); - - QString* getReverseApiAddress(); - void setReverseApiAddress(QString* reverse_api_address); - - qint32 getReverseApiPort(); - void setReverseApiPort(qint32 reverse_api_port); - - qint32 getReverseApiDeviceIndex(); - void setReverseApiDeviceIndex(qint32 reverse_api_device_index); - - qint32 getReverseApiChannelIndex(); - void setReverseApiChannelIndex(qint32 reverse_api_channel_index); - - qint32 getIdentThreshold(); - void setIdentThreshold(qint32 ident_threshold); - - qint32 getMagDecAdjust(); - void setMagDecAdjust(qint32 mag_dec_adjust); - - SWGChannelMarker* getChannelMarker(); - void setChannelMarker(SWGChannelMarker* channel_marker); - - SWGRollupState* getRollupState(); - void setRollupState(SWGRollupState* rollup_state); - - - virtual bool isSet() override; - -private: - float squelch; - bool m_squelch_isSet; - - float volume; - bool m_volume_isSet; - - qint32 audio_mute; - bool m_audio_mute_isSet; - - qint32 rgb_color; - bool m_rgb_color_isSet; - - QString* title; - bool m_title_isSet; - - QString* audio_device_name; - bool m_audio_device_name_isSet; - - qint32 stream_index; - bool m_stream_index_isSet; - - qint32 use_reverse_api; - bool m_use_reverse_api_isSet; - - QString* reverse_api_address; - bool m_reverse_api_address_isSet; - - qint32 reverse_api_port; - bool m_reverse_api_port_isSet; - - qint32 reverse_api_device_index; - bool m_reverse_api_device_index_isSet; - - qint32 reverse_api_channel_index; - bool m_reverse_api_channel_index_isSet; - - qint32 ident_threshold; - bool m_ident_threshold_isSet; - - qint32 mag_dec_adjust; - bool m_mag_dec_adjust_isSet; - - SWGChannelMarker* channel_marker; - bool m_channel_marker_isSet; - - SWGRollupState* rollup_state; - bool m_rollup_state_isSet; - -}; - -} - -#endif /* SWGVORDemodMCSettings_H_ */ diff --git a/swagger/sdrangel/code/qt5/client/SWGVORDemodSCReport.cpp b/swagger/sdrangel/code/qt5/client/SWGVORDemodSCReport.cpp deleted file mode 100644 index b59d20e7b..000000000 --- a/swagger/sdrangel/code/qt5/client/SWGVORDemodSCReport.cpp +++ /dev/null @@ -1,363 +0,0 @@ -/** - * SDRangel - * This is the web REST/JSON API of SDRangel SDR software. SDRangel is an Open Source Qt5/OpenGL 3.0+ (4.3+ in Windows) GUI and server Software Defined Radio and signal analyzer in software. It supports Airspy, BladeRF, HackRF, LimeSDR, PlutoSDR, RTL-SDR, SDRplay RSP1 and FunCube --- Limitations and specifcities: * In SDRangel GUI the first Rx device set cannot be deleted. Conversely the server starts with no device sets and its number of device sets can be reduced to zero by as many calls as necessary to /sdrangel/deviceset with DELETE method. * Preset import and export from/to file is a server only feature. * Device set focus is a GUI only feature. * The following channels are not implemented (status 501 is returned): ATV and DATV demodulators, Channel Analyzer NG, LoRa demodulator * The device settings and report structures contains only the sub-structure corresponding to the device type. The DeviceSettings and DeviceReport structures documented here shows all of them but only one will be or should be present at a time * The channel settings and report structures contains only the sub-structure corresponding to the channel type. The ChannelSettings and ChannelReport structures documented here shows all of them but only one will be or should be present at a time --- - * - * OpenAPI spec version: 6.0.0 - * Contact: f4exb06@gmail.com - * - * NOTE: This class is auto generated by the swagger code generator program. - * https://github.com/swagger-api/swagger-codegen.git - * Do not edit the class manually. - */ - - -#include "SWGVORDemodSCReport.h" - -#include "SWGHelpers.h" - -#include -#include -#include -#include - -namespace SWGSDRangel { - -SWGVORDemodSCReport::SWGVORDemodSCReport(QString* json) { - init(); - this->fromJson(*json); -} - -SWGVORDemodSCReport::SWGVORDemodSCReport() { - channel_power_db = 0.0f; - m_channel_power_db_isSet = false; - squelch = 0; - m_squelch_isSet = false; - audio_sample_rate = 0; - m_audio_sample_rate_isSet = false; - volume = 0.0f; - m_volume_isSet = false; - nav_id = 0; - m_nav_id_isSet = false; - radial = 0.0f; - m_radial_isSet = false; - ref_mag = 0.0f; - m_ref_mag_isSet = false; - var_mag = 0.0f; - m_var_mag_isSet = false; - valid_radial = 0; - m_valid_radial_isSet = false; - valid_ref_mag = 0; - m_valid_ref_mag_isSet = false; - valid_var_mag = 0; - m_valid_var_mag_isSet = false; - morse_ident = nullptr; - m_morse_ident_isSet = false; -} - -SWGVORDemodSCReport::~SWGVORDemodSCReport() { - this->cleanup(); -} - -void -SWGVORDemodSCReport::init() { - channel_power_db = 0.0f; - m_channel_power_db_isSet = false; - squelch = 0; - m_squelch_isSet = false; - audio_sample_rate = 0; - m_audio_sample_rate_isSet = false; - volume = 0.0f; - m_volume_isSet = false; - nav_id = 0; - m_nav_id_isSet = false; - radial = 0.0f; - m_radial_isSet = false; - ref_mag = 0.0f; - m_ref_mag_isSet = false; - var_mag = 0.0f; - m_var_mag_isSet = false; - valid_radial = 0; - m_valid_radial_isSet = false; - valid_ref_mag = 0; - m_valid_ref_mag_isSet = false; - valid_var_mag = 0; - m_valid_var_mag_isSet = false; - morse_ident = new QString(""); - m_morse_ident_isSet = false; -} - -void -SWGVORDemodSCReport::cleanup() { - - - - - - - - - - - - if(morse_ident != nullptr) { - delete morse_ident; - } -} - -SWGVORDemodSCReport* -SWGVORDemodSCReport::fromJson(QString &json) { - QByteArray array (json.toStdString().c_str()); - QJsonDocument doc = QJsonDocument::fromJson(array); - QJsonObject jsonObject = doc.object(); - this->fromJsonObject(jsonObject); - return this; -} - -void -SWGVORDemodSCReport::fromJsonObject(QJsonObject &pJson) { - ::SWGSDRangel::setValue(&channel_power_db, pJson["channelPowerDB"], "float", ""); - - ::SWGSDRangel::setValue(&squelch, pJson["squelch"], "qint32", ""); - - ::SWGSDRangel::setValue(&audio_sample_rate, pJson["audioSampleRate"], "qint32", ""); - - ::SWGSDRangel::setValue(&volume, pJson["volume"], "float", ""); - - ::SWGSDRangel::setValue(&nav_id, pJson["navId"], "qint32", ""); - - ::SWGSDRangel::setValue(&radial, pJson["radial"], "float", ""); - - ::SWGSDRangel::setValue(&ref_mag, pJson["refMag"], "float", ""); - - ::SWGSDRangel::setValue(&var_mag, pJson["varMag"], "float", ""); - - ::SWGSDRangel::setValue(&valid_radial, pJson["validRadial"], "qint32", ""); - - ::SWGSDRangel::setValue(&valid_ref_mag, pJson["validRefMag"], "qint32", ""); - - ::SWGSDRangel::setValue(&valid_var_mag, pJson["validVarMag"], "qint32", ""); - - ::SWGSDRangel::setValue(&morse_ident, pJson["morseIdent"], "QString", "QString"); - -} - -QString -SWGVORDemodSCReport::asJson () -{ - QJsonObject* obj = this->asJsonObject(); - - QJsonDocument doc(*obj); - QByteArray bytes = doc.toJson(); - delete obj; - return QString(bytes); -} - -QJsonObject* -SWGVORDemodSCReport::asJsonObject() { - QJsonObject* obj = new QJsonObject(); - if(m_channel_power_db_isSet){ - obj->insert("channelPowerDB", QJsonValue(channel_power_db)); - } - if(m_squelch_isSet){ - obj->insert("squelch", QJsonValue(squelch)); - } - if(m_audio_sample_rate_isSet){ - obj->insert("audioSampleRate", QJsonValue(audio_sample_rate)); - } - if(m_volume_isSet){ - obj->insert("volume", QJsonValue(volume)); - } - if(m_nav_id_isSet){ - obj->insert("navId", QJsonValue(nav_id)); - } - if(m_radial_isSet){ - obj->insert("radial", QJsonValue(radial)); - } - if(m_ref_mag_isSet){ - obj->insert("refMag", QJsonValue(ref_mag)); - } - if(m_var_mag_isSet){ - obj->insert("varMag", QJsonValue(var_mag)); - } - if(m_valid_radial_isSet){ - obj->insert("validRadial", QJsonValue(valid_radial)); - } - if(m_valid_ref_mag_isSet){ - obj->insert("validRefMag", QJsonValue(valid_ref_mag)); - } - if(m_valid_var_mag_isSet){ - obj->insert("validVarMag", QJsonValue(valid_var_mag)); - } - if(morse_ident != nullptr && *morse_ident != QString("")){ - toJsonValue(QString("morseIdent"), morse_ident, obj, QString("QString")); - } - - return obj; -} - -float -SWGVORDemodSCReport::getChannelPowerDb() { - return channel_power_db; -} -void -SWGVORDemodSCReport::setChannelPowerDb(float channel_power_db) { - this->channel_power_db = channel_power_db; - this->m_channel_power_db_isSet = true; -} - -qint32 -SWGVORDemodSCReport::getSquelch() { - return squelch; -} -void -SWGVORDemodSCReport::setSquelch(qint32 squelch) { - this->squelch = squelch; - this->m_squelch_isSet = true; -} - -qint32 -SWGVORDemodSCReport::getAudioSampleRate() { - return audio_sample_rate; -} -void -SWGVORDemodSCReport::setAudioSampleRate(qint32 audio_sample_rate) { - this->audio_sample_rate = audio_sample_rate; - this->m_audio_sample_rate_isSet = true; -} - -float -SWGVORDemodSCReport::getVolume() { - return volume; -} -void -SWGVORDemodSCReport::setVolume(float volume) { - this->volume = volume; - this->m_volume_isSet = true; -} - -qint32 -SWGVORDemodSCReport::getNavId() { - return nav_id; -} -void -SWGVORDemodSCReport::setNavId(qint32 nav_id) { - this->nav_id = nav_id; - this->m_nav_id_isSet = true; -} - -float -SWGVORDemodSCReport::getRadial() { - return radial; -} -void -SWGVORDemodSCReport::setRadial(float radial) { - this->radial = radial; - this->m_radial_isSet = true; -} - -float -SWGVORDemodSCReport::getRefMag() { - return ref_mag; -} -void -SWGVORDemodSCReport::setRefMag(float ref_mag) { - this->ref_mag = ref_mag; - this->m_ref_mag_isSet = true; -} - -float -SWGVORDemodSCReport::getVarMag() { - return var_mag; -} -void -SWGVORDemodSCReport::setVarMag(float var_mag) { - this->var_mag = var_mag; - this->m_var_mag_isSet = true; -} - -qint32 -SWGVORDemodSCReport::getValidRadial() { - return valid_radial; -} -void -SWGVORDemodSCReport::setValidRadial(qint32 valid_radial) { - this->valid_radial = valid_radial; - this->m_valid_radial_isSet = true; -} - -qint32 -SWGVORDemodSCReport::getValidRefMag() { - return valid_ref_mag; -} -void -SWGVORDemodSCReport::setValidRefMag(qint32 valid_ref_mag) { - this->valid_ref_mag = valid_ref_mag; - this->m_valid_ref_mag_isSet = true; -} - -qint32 -SWGVORDemodSCReport::getValidVarMag() { - return valid_var_mag; -} -void -SWGVORDemodSCReport::setValidVarMag(qint32 valid_var_mag) { - this->valid_var_mag = valid_var_mag; - this->m_valid_var_mag_isSet = true; -} - -QString* -SWGVORDemodSCReport::getMorseIdent() { - return morse_ident; -} -void -SWGVORDemodSCReport::setMorseIdent(QString* morse_ident) { - this->morse_ident = morse_ident; - this->m_morse_ident_isSet = true; -} - - -bool -SWGVORDemodSCReport::isSet(){ - bool isObjectUpdated = false; - do{ - if(m_channel_power_db_isSet){ - isObjectUpdated = true; break; - } - if(m_squelch_isSet){ - isObjectUpdated = true; break; - } - if(m_audio_sample_rate_isSet){ - isObjectUpdated = true; break; - } - if(m_volume_isSet){ - isObjectUpdated = true; break; - } - if(m_nav_id_isSet){ - isObjectUpdated = true; break; - } - if(m_radial_isSet){ - isObjectUpdated = true; break; - } - if(m_ref_mag_isSet){ - isObjectUpdated = true; break; - } - if(m_var_mag_isSet){ - isObjectUpdated = true; break; - } - if(m_valid_radial_isSet){ - isObjectUpdated = true; break; - } - if(m_valid_ref_mag_isSet){ - isObjectUpdated = true; break; - } - if(m_valid_var_mag_isSet){ - isObjectUpdated = true; break; - } - if(morse_ident && *morse_ident != QString("")){ - isObjectUpdated = true; break; - } - }while(false); - return isObjectUpdated; -} -} - diff --git a/swagger/sdrangel/code/qt5/client/SWGVORDemodSCReport.h b/swagger/sdrangel/code/qt5/client/SWGVORDemodSCReport.h deleted file mode 100644 index fbb7e9497..000000000 --- a/swagger/sdrangel/code/qt5/client/SWGVORDemodSCReport.h +++ /dev/null @@ -1,125 +0,0 @@ -/** - * SDRangel - * This is the web REST/JSON API of SDRangel SDR software. SDRangel is an Open Source Qt5/OpenGL 3.0+ (4.3+ in Windows) GUI and server Software Defined Radio and signal analyzer in software. It supports Airspy, BladeRF, HackRF, LimeSDR, PlutoSDR, RTL-SDR, SDRplay RSP1 and FunCube --- Limitations and specifcities: * In SDRangel GUI the first Rx device set cannot be deleted. Conversely the server starts with no device sets and its number of device sets can be reduced to zero by as many calls as necessary to /sdrangel/deviceset with DELETE method. * Preset import and export from/to file is a server only feature. * Device set focus is a GUI only feature. * The following channels are not implemented (status 501 is returned): ATV and DATV demodulators, Channel Analyzer NG, LoRa demodulator * The device settings and report structures contains only the sub-structure corresponding to the device type. The DeviceSettings and DeviceReport structures documented here shows all of them but only one will be or should be present at a time * The channel settings and report structures contains only the sub-structure corresponding to the channel type. The ChannelSettings and ChannelReport structures documented here shows all of them but only one will be or should be present at a time --- - * - * OpenAPI spec version: 6.0.0 - * Contact: f4exb06@gmail.com - * - * NOTE: This class is auto generated by the swagger code generator program. - * https://github.com/swagger-api/swagger-codegen.git - * Do not edit the class manually. - */ - -/* - * SWGVORDemodSCReport.h - * - * VORDemod - */ - -#ifndef SWGVORDemodSCReport_H_ -#define SWGVORDemodSCReport_H_ - -#include - - -#include - -#include "SWGObject.h" -#include "export.h" - -namespace SWGSDRangel { - -class SWG_API SWGVORDemodSCReport: public SWGObject { -public: - SWGVORDemodSCReport(); - SWGVORDemodSCReport(QString* json); - virtual ~SWGVORDemodSCReport(); - void init(); - void cleanup(); - - virtual QString asJson () override; - virtual QJsonObject* asJsonObject() override; - virtual void fromJsonObject(QJsonObject &json) override; - virtual SWGVORDemodSCReport* fromJson(QString &jsonString) override; - - float getChannelPowerDb(); - void setChannelPowerDb(float channel_power_db); - - qint32 getSquelch(); - void setSquelch(qint32 squelch); - - qint32 getAudioSampleRate(); - void setAudioSampleRate(qint32 audio_sample_rate); - - float getVolume(); - void setVolume(float volume); - - qint32 getNavId(); - void setNavId(qint32 nav_id); - - float getRadial(); - void setRadial(float radial); - - float getRefMag(); - void setRefMag(float ref_mag); - - float getVarMag(); - void setVarMag(float var_mag); - - qint32 getValidRadial(); - void setValidRadial(qint32 valid_radial); - - qint32 getValidRefMag(); - void setValidRefMag(qint32 valid_ref_mag); - - qint32 getValidVarMag(); - void setValidVarMag(qint32 valid_var_mag); - - QString* getMorseIdent(); - void setMorseIdent(QString* morse_ident); - - - virtual bool isSet() override; - -private: - float channel_power_db; - bool m_channel_power_db_isSet; - - qint32 squelch; - bool m_squelch_isSet; - - qint32 audio_sample_rate; - bool m_audio_sample_rate_isSet; - - float volume; - bool m_volume_isSet; - - qint32 nav_id; - bool m_nav_id_isSet; - - float radial; - bool m_radial_isSet; - - float ref_mag; - bool m_ref_mag_isSet; - - float var_mag; - bool m_var_mag_isSet; - - qint32 valid_radial; - bool m_valid_radial_isSet; - - qint32 valid_ref_mag; - bool m_valid_ref_mag_isSet; - - qint32 valid_var_mag; - bool m_valid_var_mag_isSet; - - QString* morse_ident; - bool m_morse_ident_isSet; - -}; - -} - -#endif /* SWGVORDemodSCReport_H_ */ diff --git a/swagger/sdrangel/code/qt5/client/SWGVORDemodSCSettings.cpp b/swagger/sdrangel/code/qt5/client/SWGVORDemodSCSettings.cpp deleted file mode 100644 index 090389f0b..000000000 --- a/swagger/sdrangel/code/qt5/client/SWGVORDemodSCSettings.cpp +++ /dev/null @@ -1,486 +0,0 @@ -/** - * SDRangel - * This is the web REST/JSON API of SDRangel SDR software. SDRangel is an Open Source Qt5/OpenGL 3.0+ (4.3+ in Windows) GUI and server Software Defined Radio and signal analyzer in software. It supports Airspy, BladeRF, HackRF, LimeSDR, PlutoSDR, RTL-SDR, SDRplay RSP1 and FunCube --- Limitations and specifcities: * In SDRangel GUI the first Rx device set cannot be deleted. Conversely the server starts with no device sets and its number of device sets can be reduced to zero by as many calls as necessary to /sdrangel/deviceset with DELETE method. * Preset import and export from/to file is a server only feature. * Device set focus is a GUI only feature. * The following channels are not implemented (status 501 is returned): ATV and DATV demodulators, Channel Analyzer NG, LoRa demodulator * The device settings and report structures contains only the sub-structure corresponding to the device type. The DeviceSettings and DeviceReport structures documented here shows all of them but only one will be or should be present at a time * The channel settings and report structures contains only the sub-structure corresponding to the channel type. The ChannelSettings and ChannelReport structures documented here shows all of them but only one will be or should be present at a time --- - * - * OpenAPI spec version: 6.0.0 - * Contact: f4exb06@gmail.com - * - * NOTE: This class is auto generated by the swagger code generator program. - * https://github.com/swagger-api/swagger-codegen.git - * Do not edit the class manually. - */ - - -#include "SWGVORDemodSCSettings.h" - -#include "SWGHelpers.h" - -#include -#include -#include -#include - -namespace SWGSDRangel { - -SWGVORDemodSCSettings::SWGVORDemodSCSettings(QString* json) { - init(); - this->fromJson(*json); -} - -SWGVORDemodSCSettings::SWGVORDemodSCSettings() { - input_frequency_offset = 0L; - m_input_frequency_offset_isSet = false; - nav_id = 0; - m_nav_id_isSet = false; - squelch = 0.0f; - m_squelch_isSet = false; - volume = 0.0f; - m_volume_isSet = false; - audio_mute = 0; - m_audio_mute_isSet = false; - rgb_color = 0; - m_rgb_color_isSet = false; - title = nullptr; - m_title_isSet = false; - audio_device_name = nullptr; - m_audio_device_name_isSet = false; - stream_index = 0; - m_stream_index_isSet = false; - use_reverse_api = 0; - m_use_reverse_api_isSet = false; - reverse_api_address = nullptr; - m_reverse_api_address_isSet = false; - reverse_api_port = 0; - m_reverse_api_port_isSet = false; - reverse_api_device_index = 0; - m_reverse_api_device_index_isSet = false; - reverse_api_channel_index = 0; - m_reverse_api_channel_index_isSet = false; - ident_threshold = 0; - m_ident_threshold_isSet = false; - channel_marker = nullptr; - m_channel_marker_isSet = false; - rollup_state = nullptr; - m_rollup_state_isSet = false; -} - -SWGVORDemodSCSettings::~SWGVORDemodSCSettings() { - this->cleanup(); -} - -void -SWGVORDemodSCSettings::init() { - input_frequency_offset = 0L; - m_input_frequency_offset_isSet = false; - nav_id = 0; - m_nav_id_isSet = false; - squelch = 0.0f; - m_squelch_isSet = false; - volume = 0.0f; - m_volume_isSet = false; - audio_mute = 0; - m_audio_mute_isSet = false; - rgb_color = 0; - m_rgb_color_isSet = false; - title = new QString(""); - m_title_isSet = false; - audio_device_name = new QString(""); - m_audio_device_name_isSet = false; - stream_index = 0; - m_stream_index_isSet = false; - use_reverse_api = 0; - m_use_reverse_api_isSet = false; - reverse_api_address = new QString(""); - m_reverse_api_address_isSet = false; - reverse_api_port = 0; - m_reverse_api_port_isSet = false; - reverse_api_device_index = 0; - m_reverse_api_device_index_isSet = false; - reverse_api_channel_index = 0; - m_reverse_api_channel_index_isSet = false; - ident_threshold = 0; - m_ident_threshold_isSet = false; - channel_marker = new SWGChannelMarker(); - m_channel_marker_isSet = false; - rollup_state = new SWGRollupState(); - m_rollup_state_isSet = false; -} - -void -SWGVORDemodSCSettings::cleanup() { - - - - - - - if(title != nullptr) { - delete title; - } - if(audio_device_name != nullptr) { - delete audio_device_name; - } - - - if(reverse_api_address != nullptr) { - delete reverse_api_address; - } - - - - - if(channel_marker != nullptr) { - delete channel_marker; - } - if(rollup_state != nullptr) { - delete rollup_state; - } -} - -SWGVORDemodSCSettings* -SWGVORDemodSCSettings::fromJson(QString &json) { - QByteArray array (json.toStdString().c_str()); - QJsonDocument doc = QJsonDocument::fromJson(array); - QJsonObject jsonObject = doc.object(); - this->fromJsonObject(jsonObject); - return this; -} - -void -SWGVORDemodSCSettings::fromJsonObject(QJsonObject &pJson) { - ::SWGSDRangel::setValue(&input_frequency_offset, pJson["inputFrequencyOffset"], "qint64", ""); - - ::SWGSDRangel::setValue(&nav_id, pJson["navId"], "qint32", ""); - - ::SWGSDRangel::setValue(&squelch, pJson["squelch"], "float", ""); - - ::SWGSDRangel::setValue(&volume, pJson["volume"], "float", ""); - - ::SWGSDRangel::setValue(&audio_mute, pJson["audioMute"], "qint32", ""); - - ::SWGSDRangel::setValue(&rgb_color, pJson["rgbColor"], "qint32", ""); - - ::SWGSDRangel::setValue(&title, pJson["title"], "QString", "QString"); - - ::SWGSDRangel::setValue(&audio_device_name, pJson["audioDeviceName"], "QString", "QString"); - - ::SWGSDRangel::setValue(&stream_index, pJson["streamIndex"], "qint32", ""); - - ::SWGSDRangel::setValue(&use_reverse_api, pJson["useReverseAPI"], "qint32", ""); - - ::SWGSDRangel::setValue(&reverse_api_address, pJson["reverseAPIAddress"], "QString", "QString"); - - ::SWGSDRangel::setValue(&reverse_api_port, pJson["reverseAPIPort"], "qint32", ""); - - ::SWGSDRangel::setValue(&reverse_api_device_index, pJson["reverseAPIDeviceIndex"], "qint32", ""); - - ::SWGSDRangel::setValue(&reverse_api_channel_index, pJson["reverseAPIChannelIndex"], "qint32", ""); - - ::SWGSDRangel::setValue(&ident_threshold, pJson["identThreshold"], "qint32", ""); - - ::SWGSDRangel::setValue(&channel_marker, pJson["channelMarker"], "SWGChannelMarker", "SWGChannelMarker"); - - ::SWGSDRangel::setValue(&rollup_state, pJson["rollupState"], "SWGRollupState", "SWGRollupState"); - -} - -QString -SWGVORDemodSCSettings::asJson () -{ - QJsonObject* obj = this->asJsonObject(); - - QJsonDocument doc(*obj); - QByteArray bytes = doc.toJson(); - delete obj; - return QString(bytes); -} - -QJsonObject* -SWGVORDemodSCSettings::asJsonObject() { - QJsonObject* obj = new QJsonObject(); - if(m_input_frequency_offset_isSet){ - obj->insert("inputFrequencyOffset", QJsonValue(input_frequency_offset)); - } - if(m_nav_id_isSet){ - obj->insert("navId", QJsonValue(nav_id)); - } - if(m_squelch_isSet){ - obj->insert("squelch", QJsonValue(squelch)); - } - if(m_volume_isSet){ - obj->insert("volume", QJsonValue(volume)); - } - if(m_audio_mute_isSet){ - obj->insert("audioMute", QJsonValue(audio_mute)); - } - if(m_rgb_color_isSet){ - obj->insert("rgbColor", QJsonValue(rgb_color)); - } - if(title != nullptr && *title != QString("")){ - toJsonValue(QString("title"), title, obj, QString("QString")); - } - if(audio_device_name != nullptr && *audio_device_name != QString("")){ - toJsonValue(QString("audioDeviceName"), audio_device_name, obj, QString("QString")); - } - if(m_stream_index_isSet){ - obj->insert("streamIndex", QJsonValue(stream_index)); - } - if(m_use_reverse_api_isSet){ - obj->insert("useReverseAPI", QJsonValue(use_reverse_api)); - } - if(reverse_api_address != nullptr && *reverse_api_address != QString("")){ - toJsonValue(QString("reverseAPIAddress"), reverse_api_address, obj, QString("QString")); - } - if(m_reverse_api_port_isSet){ - obj->insert("reverseAPIPort", QJsonValue(reverse_api_port)); - } - if(m_reverse_api_device_index_isSet){ - obj->insert("reverseAPIDeviceIndex", QJsonValue(reverse_api_device_index)); - } - if(m_reverse_api_channel_index_isSet){ - obj->insert("reverseAPIChannelIndex", QJsonValue(reverse_api_channel_index)); - } - if(m_ident_threshold_isSet){ - obj->insert("identThreshold", QJsonValue(ident_threshold)); - } - if((channel_marker != nullptr) && (channel_marker->isSet())){ - toJsonValue(QString("channelMarker"), channel_marker, obj, QString("SWGChannelMarker")); - } - if((rollup_state != nullptr) && (rollup_state->isSet())){ - toJsonValue(QString("rollupState"), rollup_state, obj, QString("SWGRollupState")); - } - - return obj; -} - -qint64 -SWGVORDemodSCSettings::getInputFrequencyOffset() { - return input_frequency_offset; -} -void -SWGVORDemodSCSettings::setInputFrequencyOffset(qint64 input_frequency_offset) { - this->input_frequency_offset = input_frequency_offset; - this->m_input_frequency_offset_isSet = true; -} - -qint32 -SWGVORDemodSCSettings::getNavId() { - return nav_id; -} -void -SWGVORDemodSCSettings::setNavId(qint32 nav_id) { - this->nav_id = nav_id; - this->m_nav_id_isSet = true; -} - -float -SWGVORDemodSCSettings::getSquelch() { - return squelch; -} -void -SWGVORDemodSCSettings::setSquelch(float squelch) { - this->squelch = squelch; - this->m_squelch_isSet = true; -} - -float -SWGVORDemodSCSettings::getVolume() { - return volume; -} -void -SWGVORDemodSCSettings::setVolume(float volume) { - this->volume = volume; - this->m_volume_isSet = true; -} - -qint32 -SWGVORDemodSCSettings::getAudioMute() { - return audio_mute; -} -void -SWGVORDemodSCSettings::setAudioMute(qint32 audio_mute) { - this->audio_mute = audio_mute; - this->m_audio_mute_isSet = true; -} - -qint32 -SWGVORDemodSCSettings::getRgbColor() { - return rgb_color; -} -void -SWGVORDemodSCSettings::setRgbColor(qint32 rgb_color) { - this->rgb_color = rgb_color; - this->m_rgb_color_isSet = true; -} - -QString* -SWGVORDemodSCSettings::getTitle() { - return title; -} -void -SWGVORDemodSCSettings::setTitle(QString* title) { - this->title = title; - this->m_title_isSet = true; -} - -QString* -SWGVORDemodSCSettings::getAudioDeviceName() { - return audio_device_name; -} -void -SWGVORDemodSCSettings::setAudioDeviceName(QString* audio_device_name) { - this->audio_device_name = audio_device_name; - this->m_audio_device_name_isSet = true; -} - -qint32 -SWGVORDemodSCSettings::getStreamIndex() { - return stream_index; -} -void -SWGVORDemodSCSettings::setStreamIndex(qint32 stream_index) { - this->stream_index = stream_index; - this->m_stream_index_isSet = true; -} - -qint32 -SWGVORDemodSCSettings::getUseReverseApi() { - return use_reverse_api; -} -void -SWGVORDemodSCSettings::setUseReverseApi(qint32 use_reverse_api) { - this->use_reverse_api = use_reverse_api; - this->m_use_reverse_api_isSet = true; -} - -QString* -SWGVORDemodSCSettings::getReverseApiAddress() { - return reverse_api_address; -} -void -SWGVORDemodSCSettings::setReverseApiAddress(QString* reverse_api_address) { - this->reverse_api_address = reverse_api_address; - this->m_reverse_api_address_isSet = true; -} - -qint32 -SWGVORDemodSCSettings::getReverseApiPort() { - return reverse_api_port; -} -void -SWGVORDemodSCSettings::setReverseApiPort(qint32 reverse_api_port) { - this->reverse_api_port = reverse_api_port; - this->m_reverse_api_port_isSet = true; -} - -qint32 -SWGVORDemodSCSettings::getReverseApiDeviceIndex() { - return reverse_api_device_index; -} -void -SWGVORDemodSCSettings::setReverseApiDeviceIndex(qint32 reverse_api_device_index) { - this->reverse_api_device_index = reverse_api_device_index; - this->m_reverse_api_device_index_isSet = true; -} - -qint32 -SWGVORDemodSCSettings::getReverseApiChannelIndex() { - return reverse_api_channel_index; -} -void -SWGVORDemodSCSettings::setReverseApiChannelIndex(qint32 reverse_api_channel_index) { - this->reverse_api_channel_index = reverse_api_channel_index; - this->m_reverse_api_channel_index_isSet = true; -} - -qint32 -SWGVORDemodSCSettings::getIdentThreshold() { - return ident_threshold; -} -void -SWGVORDemodSCSettings::setIdentThreshold(qint32 ident_threshold) { - this->ident_threshold = ident_threshold; - this->m_ident_threshold_isSet = true; -} - -SWGChannelMarker* -SWGVORDemodSCSettings::getChannelMarker() { - return channel_marker; -} -void -SWGVORDemodSCSettings::setChannelMarker(SWGChannelMarker* channel_marker) { - this->channel_marker = channel_marker; - this->m_channel_marker_isSet = true; -} - -SWGRollupState* -SWGVORDemodSCSettings::getRollupState() { - return rollup_state; -} -void -SWGVORDemodSCSettings::setRollupState(SWGRollupState* rollup_state) { - this->rollup_state = rollup_state; - this->m_rollup_state_isSet = true; -} - - -bool -SWGVORDemodSCSettings::isSet(){ - bool isObjectUpdated = false; - do{ - if(m_input_frequency_offset_isSet){ - isObjectUpdated = true; break; - } - if(m_nav_id_isSet){ - isObjectUpdated = true; break; - } - if(m_squelch_isSet){ - isObjectUpdated = true; break; - } - if(m_volume_isSet){ - isObjectUpdated = true; break; - } - if(m_audio_mute_isSet){ - isObjectUpdated = true; break; - } - if(m_rgb_color_isSet){ - isObjectUpdated = true; break; - } - if(title && *title != QString("")){ - isObjectUpdated = true; break; - } - if(audio_device_name && *audio_device_name != QString("")){ - isObjectUpdated = true; break; - } - if(m_stream_index_isSet){ - isObjectUpdated = true; break; - } - if(m_use_reverse_api_isSet){ - isObjectUpdated = true; break; - } - if(reverse_api_address && *reverse_api_address != QString("")){ - isObjectUpdated = true; break; - } - if(m_reverse_api_port_isSet){ - isObjectUpdated = true; break; - } - if(m_reverse_api_device_index_isSet){ - isObjectUpdated = true; break; - } - if(m_reverse_api_channel_index_isSet){ - isObjectUpdated = true; break; - } - if(m_ident_threshold_isSet){ - isObjectUpdated = true; break; - } - if(channel_marker && channel_marker->isSet()){ - isObjectUpdated = true; break; - } - if(rollup_state && rollup_state->isSet()){ - isObjectUpdated = true; break; - } - }while(false); - return isObjectUpdated; -} -} - diff --git a/swagger/sdrangel/code/qt5/client/SWGVORDemodSCSettings.h b/swagger/sdrangel/code/qt5/client/SWGVORDemodSCSettings.h deleted file mode 100644 index c7acf6446..000000000 --- a/swagger/sdrangel/code/qt5/client/SWGVORDemodSCSettings.h +++ /dev/null @@ -1,157 +0,0 @@ -/** - * SDRangel - * This is the web REST/JSON API of SDRangel SDR software. SDRangel is an Open Source Qt5/OpenGL 3.0+ (4.3+ in Windows) GUI and server Software Defined Radio and signal analyzer in software. It supports Airspy, BladeRF, HackRF, LimeSDR, PlutoSDR, RTL-SDR, SDRplay RSP1 and FunCube --- Limitations and specifcities: * In SDRangel GUI the first Rx device set cannot be deleted. Conversely the server starts with no device sets and its number of device sets can be reduced to zero by as many calls as necessary to /sdrangel/deviceset with DELETE method. * Preset import and export from/to file is a server only feature. * Device set focus is a GUI only feature. * The following channels are not implemented (status 501 is returned): ATV and DATV demodulators, Channel Analyzer NG, LoRa demodulator * The device settings and report structures contains only the sub-structure corresponding to the device type. The DeviceSettings and DeviceReport structures documented here shows all of them but only one will be or should be present at a time * The channel settings and report structures contains only the sub-structure corresponding to the channel type. The ChannelSettings and ChannelReport structures documented here shows all of them but only one will be or should be present at a time --- - * - * OpenAPI spec version: 6.0.0 - * Contact: f4exb06@gmail.com - * - * NOTE: This class is auto generated by the swagger code generator program. - * https://github.com/swagger-api/swagger-codegen.git - * Do not edit the class manually. - */ - -/* - * SWGVORDemodSCSettings.h - * - * VORDemod - */ - -#ifndef SWGVORDemodSCSettings_H_ -#define SWGVORDemodSCSettings_H_ - -#include - - -#include "SWGChannelMarker.h" -#include "SWGRollupState.h" -#include - -#include "SWGObject.h" -#include "export.h" - -namespace SWGSDRangel { - -class SWG_API SWGVORDemodSCSettings: public SWGObject { -public: - SWGVORDemodSCSettings(); - SWGVORDemodSCSettings(QString* json); - virtual ~SWGVORDemodSCSettings(); - void init(); - void cleanup(); - - virtual QString asJson () override; - virtual QJsonObject* asJsonObject() override; - virtual void fromJsonObject(QJsonObject &json) override; - virtual SWGVORDemodSCSettings* fromJson(QString &jsonString) override; - - qint64 getInputFrequencyOffset(); - void setInputFrequencyOffset(qint64 input_frequency_offset); - - qint32 getNavId(); - void setNavId(qint32 nav_id); - - float getSquelch(); - void setSquelch(float squelch); - - float getVolume(); - void setVolume(float volume); - - qint32 getAudioMute(); - void setAudioMute(qint32 audio_mute); - - qint32 getRgbColor(); - void setRgbColor(qint32 rgb_color); - - QString* getTitle(); - void setTitle(QString* title); - - QString* getAudioDeviceName(); - void setAudioDeviceName(QString* audio_device_name); - - qint32 getStreamIndex(); - void setStreamIndex(qint32 stream_index); - - qint32 getUseReverseApi(); - void setUseReverseApi(qint32 use_reverse_api); - - QString* getReverseApiAddress(); - void setReverseApiAddress(QString* reverse_api_address); - - qint32 getReverseApiPort(); - void setReverseApiPort(qint32 reverse_api_port); - - qint32 getReverseApiDeviceIndex(); - void setReverseApiDeviceIndex(qint32 reverse_api_device_index); - - qint32 getReverseApiChannelIndex(); - void setReverseApiChannelIndex(qint32 reverse_api_channel_index); - - qint32 getIdentThreshold(); - void setIdentThreshold(qint32 ident_threshold); - - SWGChannelMarker* getChannelMarker(); - void setChannelMarker(SWGChannelMarker* channel_marker); - - SWGRollupState* getRollupState(); - void setRollupState(SWGRollupState* rollup_state); - - - virtual bool isSet() override; - -private: - qint64 input_frequency_offset; - bool m_input_frequency_offset_isSet; - - qint32 nav_id; - bool m_nav_id_isSet; - - float squelch; - bool m_squelch_isSet; - - float volume; - bool m_volume_isSet; - - qint32 audio_mute; - bool m_audio_mute_isSet; - - qint32 rgb_color; - bool m_rgb_color_isSet; - - QString* title; - bool m_title_isSet; - - QString* audio_device_name; - bool m_audio_device_name_isSet; - - qint32 stream_index; - bool m_stream_index_isSet; - - qint32 use_reverse_api; - bool m_use_reverse_api_isSet; - - QString* reverse_api_address; - bool m_reverse_api_address_isSet; - - qint32 reverse_api_port; - bool m_reverse_api_port_isSet; - - qint32 reverse_api_device_index; - bool m_reverse_api_device_index_isSet; - - qint32 reverse_api_channel_index; - bool m_reverse_api_channel_index_isSet; - - qint32 ident_threshold; - bool m_ident_threshold_isSet; - - SWGChannelMarker* channel_marker; - bool m_channel_marker_isSet; - - SWGRollupState* rollup_state; - bool m_rollup_state_isSet; - -}; - -} - -#endif /* SWGVORDemodSCSettings_H_ */ From c892ed558d089fba55804decef7615d6c929a8b9 Mon Sep 17 00:00:00 2001 From: f4exb Date: Mon, 2 May 2022 01:10:14 +0200 Subject: [PATCH 090/115] VOR localizer feature: fixed segfault on exit --- plugins/feature/vorlocalizer/vorlocalizerworker.cpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/plugins/feature/vorlocalizer/vorlocalizerworker.cpp b/plugins/feature/vorlocalizer/vorlocalizerworker.cpp index ebe1cd3e4..5b64def9a 100644 --- a/plugins/feature/vorlocalizer/vorlocalizerworker.cpp +++ b/plugins/feature/vorlocalizer/vorlocalizerworker.cpp @@ -395,11 +395,8 @@ void VorLocalizerWorker::updateChannels() for (auto rrChannel : rrPlan.m_channels) { - qDebug() << "VorLocalizerWorker::updateChannels: RR channel: " - << "channel:" << rrChannel.m_channelAPI - << "index:" << rrChannel.m_channelIndex - << "shift:" << rrChannel.m_frequencyShift - << "navId:" << rrChannel.m_navId; + qDebug("VorLocalizerWorker::updateChannels: RR channel: %p index: %d shift: %d navId: %d", + rrChannel.m_channelAPI, rrChannel.m_channelIndex, rrChannel.m_frequencyShift, rrChannel.m_navId); } } } From 32820e25b4afd306d50d154b64ff451fbc8e7961 Mon Sep 17 00:00:00 2001 From: f4exb Date: Mon, 2 May 2022 02:01:10 +0200 Subject: [PATCH 091/115] Massive UI revamping (v7): Fixed Tx and MIMO channel device label when retrieved from configuration. Fixes #1227 --- sdrgui/device/deviceuiset.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sdrgui/device/deviceuiset.cpp b/sdrgui/device/deviceuiset.cpp index 7ca335251..084c54e2c 100644 --- a/sdrgui/device/deviceuiset.cpp +++ b/sdrgui/device/deviceuiset.cpp @@ -463,7 +463,7 @@ void DeviceUISet::loadTxChannelSettings(const Preset *preset, PluginAPI *pluginA } txChannelGUI->restoreGeometry(txChannelGUI->getGeometryBytes()); - txChannelGUI->setDeviceType(ChannelGUI::DeviceRx); + txChannelGUI->setDeviceType(ChannelGUI::DeviceTx); txChannelGUI->setDeviceSetIndex(m_deviceSetIndex); txChannelGUI->setIndex(channelAPI->getIndexInDeviceSet()); txChannelGUI->setIndexToolTip(m_deviceAPI->getSamplingDeviceDisplayName()); @@ -636,7 +636,7 @@ void DeviceUISet::loadMIMOChannelSettings(const Preset *preset, PluginAPI *plugi } channelGUI->restoreGeometry(channelGUI->getGeometryBytes()); - channelGUI->setDeviceType(ChannelGUI::DeviceRx); + channelGUI->setDeviceType(ChannelGUI::DeviceMIMO); channelGUI->setDeviceSetIndex(m_deviceSetIndex); channelGUI->setIndex(channelAPI->getIndexInDeviceSet()); channelGUI->setIndexToolTip(m_deviceAPI->getSamplingDeviceDisplayName()); From dba9a74acb81d89833e37af0b6f7ab5055cf650b Mon Sep 17 00:00:00 2001 From: f4exb Date: Mon, 2 May 2022 19:45:18 +0200 Subject: [PATCH 092/115] Massive UI revamping (v7): alpha.3 release --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c5a320578..78b3cfd94 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,7 +17,7 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON) set(sdrangel_VERSION_MAJOR "7") set(sdrangel_VERSION_MINOR "0") set(sdrangel_VERSION_PATCH "0") -set(sdrangel_VERSION_SUFFIX "alpha.2") +set(sdrangel_VERSION_SUFFIX "alpha.3") # SDRAngel cmake options option(DEBUG_OUTPUT "Print debug messages" OFF) From 14701da1f5c62da764fcb6e5cb90a62774d0e740 Mon Sep 17 00:00:00 2001 From: Jon Beniston Date: Tue, 3 May 2022 13:44:09 +0100 Subject: [PATCH 093/115] VOR Localizer: Use shared OpenAIP code to get latest URL --- plugins/feature/vorlocalizer/CMakeLists.txt | 1 - plugins/feature/vorlocalizer/navaid.h | 368 ---------- .../feature/vorlocalizer/vorlocalizergui.cpp | 627 ++++-------------- .../feature/vorlocalizer/vorlocalizergui.h | 18 +- sdrbase/util/openaip.h | 11 +- 5 files changed, 127 insertions(+), 898 deletions(-) delete mode 100644 plugins/feature/vorlocalizer/navaid.h diff --git a/plugins/feature/vorlocalizer/CMakeLists.txt b/plugins/feature/vorlocalizer/CMakeLists.txt index ddf07bb7a..aaf5267bd 100644 --- a/plugins/feature/vorlocalizer/CMakeLists.txt +++ b/plugins/feature/vorlocalizer/CMakeLists.txt @@ -34,7 +34,6 @@ if(NOT SERVER_MODE) set(vor_HEADERS ${vor_HEADERS} vorlocalizergui.h - navaid.h ) set(TARGET_NAME vorlocalizer) diff --git a/plugins/feature/vorlocalizer/navaid.h b/plugins/feature/vorlocalizer/navaid.h deleted file mode 100644 index 1ac7419f6..000000000 --- a/plugins/feature/vorlocalizer/navaid.h +++ /dev/null @@ -1,368 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////////// -// Copyright (C) 2020 Jon Beniston, M7RCE // -// // -// This program 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 as version 3 of the License, or // -// (at your option) any later version. // -// // -// This program 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 V3 for more details. // -// // -// You should have received a copy of the GNU General Public License // -// along with this program. If not, see . // -/////////////////////////////////////////////////////////////////////////////////// - -#ifndef INCLUDE_NAVAID_H -#define INCLUDE_NAVAID_H - -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "util/units.h" -#include "util/csv.h" - -#define OURAIRPORTS_NAVAIDS_URL "https://ourairports.com/data/navaids.csv" -#define OPENAIP_NAVAIDS_URL "https://www.openaip.net/customer_export_akfshb9237tgwiuvb4tgiwbf/%1_nav.aip" - -struct NavAid { - - int m_id; - QString m_ident; // 2 or 3 character ident - QString m_type; // VOR, VOR-DME or VORTAC - QString m_name; - float m_latitude; - float m_longitude; - float m_elevation; - int m_frequencykHz; - QString m_channel; - int m_range; // Nautical miles - float m_magneticDeclination; - bool m_alignedTrueNorth; // Is the VOR aligned to true North, rather than magnetic (may be the case at high latitudes) - - static QString trimQuotes(const QString s) - { - if (s.startsWith('\"') && s.endsWith('\"')) - return s.mid(1, s.size() - 2); - else - return s; - } - - int getRangeMetres() - { - return Units::nauticalMilesToIntegerMetres((float)m_range); - } - - // OpenAIP XML file - static void readNavAidsXML(QHash *navAidInfo, const QString &filename) - { - QFile file(filename); - if (file.open(QIODevice::ReadOnly | QIODevice::Text)) - { - QXmlStreamReader xmlReader(&file); - - while(!xmlReader.atEnd() && !xmlReader.hasError()) - { - if (xmlReader.readNextStartElement()) - { - if (xmlReader.name() == "NAVAID") - { - QStringRef typeRef = xmlReader.attributes().value("TYPE"); - if ((typeRef == QLatin1String("VOR")) - || (typeRef== QLatin1String("VOR-DME")) - || (typeRef == QLatin1String("VORTAC"))) - { - QString type = typeRef.toString(); - int identifier = 0; - QString name; - QString id; - float lat = 0.0f; - float lon = 0.0f; - float elevation = 0.0f; - int frequency = 0; - QString channel; - int range = 25; - float declination = 0.0f; - bool alignedTrueNorth = false; - while(xmlReader.readNextStartElement()) - { - if (xmlReader.name() == QLatin1String("IDENTIFIER")) - identifier = xmlReader.readElementText().toInt(); - else if (xmlReader.name() == QLatin1String("NAME")) - name = xmlReader.readElementText(); - else if (xmlReader.name() == QLatin1String("ID")) - id = xmlReader.readElementText(); - else if (xmlReader.name() == QLatin1String("GEOLOCATION")) - { - while(xmlReader.readNextStartElement()) - { - if (xmlReader.name() == QLatin1String("LAT")) - lat = xmlReader.readElementText().toFloat(); - else if (xmlReader.name() == QLatin1String("LON")) - lon = xmlReader.readElementText().toFloat(); - else if (xmlReader.name() == QLatin1String("ELEV")) - elevation = xmlReader.readElementText().toFloat(); - else - xmlReader.skipCurrentElement(); - } - } - else if (xmlReader.name() == QLatin1String("RADIO")) - { - while(xmlReader.readNextStartElement()) - { - if (xmlReader.name() == QLatin1String("FREQUENCY")) - frequency = (int)(xmlReader.readElementText().toFloat() * 1000); - else if (xmlReader.name() == QLatin1String("CHANNEL")) - channel = xmlReader.readElementText(); - else - xmlReader.skipCurrentElement(); - } - } - else if (xmlReader.name() == QLatin1String("PARAMS")) - { - while(xmlReader.readNextStartElement()) - { - if (xmlReader.name() == QLatin1String("RANGE")) - range = xmlReader.readElementText().toInt(); - else if (xmlReader.name() == QLatin1String("DECLINATION")) - declination = xmlReader.readElementText().toFloat(); - else if (xmlReader.name() == QLatin1String("ALIGNEDTOTRUENORTH")) - alignedTrueNorth = xmlReader.readElementText() == "TRUE"; - else - xmlReader.skipCurrentElement(); - } - } - else - xmlReader.skipCurrentElement(); - } - NavAid *vor = new NavAid(); - vor->m_id = identifier; - vor->m_ident = id; - // Check idents conform to our filtering rules - if (vor->m_ident.size() < 2) - qDebug() << "Warning: VOR Ident less than 2 characters: " << vor->m_ident; - else if (vor->m_ident.size() > 3) - qDebug() << "Warning: VOR Ident greater than 3 characters: " << vor->m_ident; - vor->m_type = type; - vor->m_name = name; - vor->m_frequencykHz = frequency; - vor->m_channel = channel; - vor->m_latitude = lat; - vor->m_longitude = lon; - vor->m_elevation = elevation; - vor->m_range = range; - vor->m_magneticDeclination = declination; - vor->m_alignedTrueNorth = alignedTrueNorth; - navAidInfo->insert(identifier, vor); - } - } - } - } - - file.close(); - } - else - qDebug() << "NavAid::readNavAidsXML: Could not open " << filename << " for reading."; - } - - // Read OurAirport's NavAids CSV file - // See comments for readOSNDB - static QHash *readNavAidsDB(const QString &filename) - { - int cnt = 0; - QHash *navAidInfo = nullptr; - - // Column numbers used for the data as of 2020/10/28 - int idCol = 0; - int identCol = 2; - int typeCol = 4; - int nameCol = 3; - int frequencyCol = 5; - int latitudeCol = 6; - int longitudeCol = 7; - int elevationCol = 8; - int powerCol = 18; - - qDebug() << "NavAid::readNavAidsDB: " << filename; - - FILE *file; - QByteArray utfFilename = filename.toUtf8(); - QLocale cLocale(QLocale::C); - if ((file = fopen(utfFilename.constData(), "r")) != NULL) - { - char row[2048]; - - if (fgets(row, sizeof(row), file)) - { - navAidInfo = new QHash(); - navAidInfo->reserve(15000); - - // Read header - int idx = 0; - char *p = strtok(row, ","); - while (p != NULL) - { - if (!strcmp(p, "id")) - idCol = idx; - else if (!strcmp(p, "ident")) - identCol = idx; - else if (!strcmp(p, "type")) - typeCol = idx; - else if (!strcmp(p, "name")) - nameCol = idx; - else if (!strcmp(p, "frequency_khz")) - frequencyCol = idx; - else if (!strcmp(p, "latitude_deg")) - latitudeCol = idx; - else if (!strcmp(p, "longitude_deg")) - longitudeCol = idx; - else if (!strcmp(p, "elevation_ft")) - elevationCol = idx; - else if (!strcmp(p, "power")) - powerCol = idx; - p = strtok(NULL, ","); - idx++; - } - // Read data - while (fgets(row, sizeof(row), file)) - { - int id = 0; - char *idString = NULL; - char *ident = NULL; - size_t identLen = 0; - char *type = NULL; - size_t typeLen = 0; - char *name = NULL; - size_t nameLen = 0; - char *frequencyString = NULL; - int frequency; - float latitude = 0.0f; - char *latitudeString = NULL; - size_t latitudeLen = 0; - float longitude = 0.0f; - char *longitudeString = NULL; - size_t longitudeLen = 0; - float elevation = 0.0f; - char *elevationString = NULL; - size_t elevationLen = 0; - char *power = NULL; - size_t powerLen = 0; - - char *q = row; - idx = 0; - while ((p = csvNext(&q)) != nullptr) - { - // Read strings, stripping quotes - if (idx == idCol) - { - idString = p; - idString[strlen(idString)] = '\0'; - id = strtol(idString, NULL, 10); - } - else if ((idx == identCol) && (p[0] == '\"')) - { - ident = p+1; - identLen = strlen(ident)-1; - ident[identLen] = '\0'; - } - else if ((idx == typeCol) && (p[0] == '\"')) - { - type = p+1; - typeLen = strlen(type)-1; - type[typeLen] = '\0'; - } - else if ((idx == nameCol) && (p[0] == '\"')) - { - name = p+1; - nameLen = strlen(name)-1; - name[nameLen] = '\0'; - } - if (idx == frequencyCol) - { - frequencyString = p; - frequencyString[strlen(frequencyString)] = '\0'; - frequency = strtol(frequencyString, NULL, 10); - } - else if (idx == latitudeCol) - { - latitudeString = p; - latitudeLen = strlen(latitudeString)-1; - latitudeString[latitudeLen] = '\0'; - latitude = cLocale.toFloat(latitudeString); - } - else if (idx == longitudeCol) - { - longitudeString = p; - longitudeLen = strlen(longitudeString)-1; - longitudeString[longitudeLen] = '\0'; - longitude = cLocale.toFloat(longitudeString); - } - else if (idx == elevationCol) - { - elevationString = p; - elevationLen = strlen(elevationString)-1; - elevationString[elevationLen] = '\0'; - elevation = cLocale.toFloat(elevationString); - } - else if ((idx == powerCol) && (p[0] == '\"')) - { - power = p+1; - powerLen = strlen(power)-1; - power[powerLen] = '\0'; - } - idx++; - } - - // For now, we only want VORs - if (type && !strncmp(type, "VOR", 3)) - { - NavAid *vor = new NavAid(); - vor->m_id = id; - vor->m_ident = QString(ident); - // Check idents conform to our filtering rules - if (vor->m_ident.size() < 2) - qDebug() << "Warning: VOR Ident less than 2 characters: " << vor->m_ident; - else if (vor->m_ident.size() > 3) - qDebug() << "Warning: VOR Ident greater than 3 characters: " << vor->m_ident; - vor->m_type = QString(type); - vor->m_name = QString(name); - vor->m_frequencykHz = frequency; - vor->m_latitude = latitude; - vor->m_longitude = longitude; - vor->m_elevation = elevation; - if (power && !strcmp(power, "HIGH")) - vor->m_range = 100; - else if (power && !strcmp(power, "MEDIUM")) - vor->m_range = 40; - else - vor->m_range = 25; - vor->m_magneticDeclination = 0.0f; - vor->m_alignedTrueNorth = false; - navAidInfo->insert(id, vor); - cnt++; - } - } - } - fclose(file); - } - else - qDebug() << "NavAid::readNavAidsDB: Failed to open " << filename; - - qDebug() << "NavAid::readNavAidsDB: Read " << cnt << " VORs"; - - return navAidInfo; - } - -}; - -#endif // INCLUDE_NAVAID_H diff --git a/plugins/feature/vorlocalizer/vorlocalizergui.cpp b/plugins/feature/vorlocalizer/vorlocalizergui.cpp index 2e4c0b965..a00085a19 100644 --- a/plugins/feature/vorlocalizer/vorlocalizergui.cpp +++ b/plugins/feature/vorlocalizer/vorlocalizergui.cpp @@ -47,258 +47,6 @@ #include "vorlocalizersettings.h" #include "vorlocalizergui.h" -static const char *countryCodes[] = { - "ad", - "ae", - "af", - "ag", - "ai", - "al", - "am", - "an", - "ao", - "aq", - "ar", - "as", - "at", - "au", - "aw", - "ax", - "az", - "ba", - "bb", - "bd", - "be", - "bf", - "bg", - "bh", - "bi", - "bj", - "bl", - "bm", - "bn", - "bo", - "bq", - "br", - "bs", - "bt", - "bv", - "bw", - "by", - "bz", - "ca", - "cc", - "cd", - "cf", - "cg", - "ch", - "ci", - "ck", - "cl", - "cm", - "cn", - "co", - "cr", - "cu", - "cv", - "cw", - "cx", - "cy", - "cz", - "de", - "dj", - "dk", - "dm", - "do", - "dz", - "ec", - "ee", - "eg", - "eh", - "er", - "es", - "et", - "fi", - "fj", - "fk", - "fm", - "fo", - "fr", - "ga", - "gb", - "ge", - "gf", - "gg", - "gh", - "gi", - "gl", - "gm", - "gn", - "gp", - "gq", - "gr", - "gs", - "gt", - "gu", - "gw", - "gy", - "hk", - "hm", - "hn", - "hr", - "hu", - "id", - "ie", - "il", - "im", - "in", - "io", - "iq", - "ir", - "is", - "it", - "je", - "jm", - "jo", - "jp", - "ke", - "kg", - "kh", - "ki", - "km", - "kn", - "kp", - "kr", - "kw", - "ky", - "kz", - "la", - "lb", - "lc", - "li", - "lk", - "lr", - "ls", - "lt", - "lu", - "lv", - "ly", - "ma", - "mc", - "md", - "me", - "mf", - "mg", - "mh", - "mk", - "ml", - "mm", - "mn", - "mo", - "mp", - "mq", - "mr", - "ms", - "mt", - "mu", - "mv", - "mw", - "mx", - "my", - "mz", - "na", - "nc", - "ne", - "nf", - "ng", - "ni", - "nl", - "no", - "np", - "nr", - "nu", - "nz", - "om", - "pa", - "pe", - "pf", - "pg", - "ph", - "pk", - "pl", - "pm", - "pn", - "pr", - "ps", - "pt", - "pw", - "py", - "qa", - "re", - "ro", - "rs", - "ru", - "rw", - "sa", - "sb", - "sc", - "sd", - "se", - "sg", - "sh", - "si", - "sj", - "sk", - "sl", - "sm", - "sn", - "so", - "sr", - "ss", - "st", - "sv", - "sx", - "sy", - "sz", - "tc", - "td", - "tf", - "tg", - "th", - "tj", - "tk", - "tl", - "tm", - "tn", - "to", - "tr", - "tt", - "tv", - "tw", - "tz", - "ua", - "ug", - "um", - "us", - "uy", - "uz", - "va", - "vc", - "ve", - "vg", - "vi", - "vn", - "vu", - "wf", - "ws", - "ye", - "yt", - "za", - "zm", - "zw", - nullptr -}; - // Lats and longs in decimal degrees. Distance in metres. Bearing in degrees. // https://www.movable-type.co.uk/scripts/latlong.html static void calcRadialEndPoint(float startLatitude, float startLongitude, float distance, float bearing, float &endLatitude, float &endLongitude) @@ -702,7 +450,7 @@ void VORLocalizerGUI::selectVOR(VORGUI *vorGUI, bool selected) // Add to settings to create corresponding demodulator m_settings.m_subChannelSettings.insert(navId, VORLocalizerSubChannelSettings{ navId, - vorGUI->m_navAid->m_frequencykHz * 1000, + (int)(vorGUI->m_navAid->m_frequencykHz * 1000), false }); @@ -725,23 +473,21 @@ void VORLocalizerGUI::selectVOR(VORGUI *vorGUI, bool selected) void VORLocalizerGUI::updateVORs() { m_vorModel.removeAllVORs(); - QHash::iterator i = m_vors->begin(); AzEl azEl = m_azEl; - while (i != m_vors->end()) + for (auto vor : m_vors) { - NavAid *vor = i.value(); + if (vor->m_type.contains("VOR")) // Exclude DMEs + { + // Calculate distance to VOR from My Position + azEl.setTarget(vor->m_latitude, vor->m_longitude, Units::feetToMetres(vor->m_elevation)); + azEl.calculate(); - // Calculate distance to VOR from My Position - azEl.setTarget(vor->m_latitude, vor->m_longitude, Units::feetToMetres(vor->m_elevation)); - azEl.calculate(); - - // Only display VOR if in range - if (azEl.getDistance() <= 200000) { - m_vorModel.addVOR(vor); + // Only display VOR if in range + if (azEl.getDistance() <= 200000) { + m_vorModel.addVOR(vor); + } } - - ++i; } } @@ -807,41 +553,47 @@ bool VORLocalizerGUI::handleMessage(const Message& message) int subChannelId = report.getSubChannelId(); VORGUI *vorGUI = m_selectedVORs.value(subChannelId); + if (vorGUI) + { + // Display radial and signal magnitudes in table - // Display radial and signal magnitudes in table + Real varMagDB = std::round(20.0*std::log10(report.getVarMag())); + Real refMagDB = std::round(20.0*std::log10(report.getRefMag())); - Real varMagDB = std::round(20.0*std::log10(report.getVarMag())); - Real refMagDB = std::round(20.0*std::log10(report.getRefMag())); + bool validRadial = report.getValidRadial(); + vorGUI->m_radialItem->setData(Qt::DisplayRole, std::round(report.getRadial())); + vorGUI->m_navIdItem->setData(Qt::DisplayRole, subChannelId); - bool validRadial = report.getValidRadial(); - vorGUI->m_radialItem->setData(Qt::DisplayRole, std::round(report.getRadial())); - vorGUI->m_navIdItem->setData(Qt::DisplayRole, subChannelId); + if (validRadial) { + vorGUI->m_radialItem->setForeground(QBrush(Qt::white)); + } else { + vorGUI->m_radialItem->setForeground(QBrush(Qt::red)); + } - if (validRadial) { - vorGUI->m_radialItem->setForeground(QBrush(Qt::white)); - } else { - vorGUI->m_radialItem->setForeground(QBrush(Qt::red)); + vorGUI->m_refMagItem->setData(Qt::DisplayRole, refMagDB); + + if (report.getValidRefMag()) { + vorGUI->m_refMagItem->setForeground(QBrush(Qt::white)); + } else { + vorGUI->m_refMagItem->setForeground(QBrush(Qt::red)); + } + + vorGUI->m_varMagItem->setData(Qt::DisplayRole, varMagDB); + + if (report.getValidVarMag()) { + vorGUI->m_varMagItem->setForeground(QBrush(Qt::white)); + } else { + vorGUI->m_varMagItem->setForeground(QBrush(Qt::red)); + } + + // Update radial on map + m_vorModel.setRadial(subChannelId, validRadial, report.getRadial()); } - - vorGUI->m_refMagItem->setData(Qt::DisplayRole, refMagDB); - - if (report.getValidRefMag()) { - vorGUI->m_refMagItem->setForeground(QBrush(Qt::white)); - } else { - vorGUI->m_refMagItem->setForeground(QBrush(Qt::red)); + else + { + qDebug() << "VORLocalizerGUI::handleMessage: Got MsgReportRadial for non-existant subChannelId " << subChannelId; } - vorGUI->m_varMagItem->setData(Qt::DisplayRole, varMagDB); - - if (report.getValidVarMag()) { - vorGUI->m_varMagItem->setForeground(QBrush(Qt::white)); - } else { - vorGUI->m_varMagItem->setForeground(QBrush(Qt::red)); - } - - // Update radial on map - m_vorModel.setRadial(subChannelId, validRadial, report.getRadial()); - return true; } else if (VORLocalizerReport::MsgReportIdent::match(message)) @@ -850,38 +602,45 @@ bool VORLocalizerGUI::handleMessage(const Message& message) int subChannelId = report.getSubChannelId(); VORGUI *vorGUI = m_selectedVORs.value(subChannelId); - - QString ident = report.getIdent(); - // Convert Morse to a string - QString identString = Morse::toString(ident); - // Idents should only be two or three characters, so filter anything else - // other than TEST which indicates a VOR is under maintainance (may also be TST) - if (((identString.size() >= 2) && (identString.size() <= 3)) || (identString == "TEST")) + if (vorGUI) { - vorGUI->m_rxIdentItem->setText(identString); - vorGUI->m_rxMorseItem->setText(Morse::toSpacedUnicode(ident)); - if (vorGUI->m_navAid->m_ident == identString) + QString ident = report.getIdent(); + // Convert Morse to a string + QString identString = Morse::toString(ident); + // Idents should only be two or three characters, so filter anything else + // other than TEST which indicates a VOR is under maintainance (may also be TST) + if (((identString.size() >= 2) && (identString.size() <= 3)) || (identString == "TEST")) { - // Set colour to green if matching expected ident - vorGUI->m_rxIdentItem->setForeground(QBrush(Qt::green)); - vorGUI->m_rxMorseItem->setForeground(QBrush(Qt::green)); + vorGUI->m_rxIdentItem->setText(identString); + vorGUI->m_rxMorseItem->setText(Morse::toSpacedUnicode(ident)); + + if (vorGUI->m_navAid->m_ident == identString) + { + // Set colour to green if matching expected ident + vorGUI->m_rxIdentItem->setForeground(QBrush(Qt::green)); + vorGUI->m_rxMorseItem->setForeground(QBrush(Qt::green)); + } + else + { + // Set colour to green if not matching expected ident + vorGUI->m_rxIdentItem->setForeground(QBrush(Qt::red)); + vorGUI->m_rxMorseItem->setForeground(QBrush(Qt::red)); + } } else { - // Set colour to green if not matching expected ident - vorGUI->m_rxIdentItem->setForeground(QBrush(Qt::red)); - vorGUI->m_rxMorseItem->setForeground(QBrush(Qt::red)); + // Set yellow to indicate we've filtered something (unless red) + if (vorGUI->m_rxIdentItem->foreground().color() != Qt::red) + { + vorGUI->m_rxIdentItem->setForeground(QBrush(Qt::yellow)); + vorGUI->m_rxMorseItem->setForeground(QBrush(Qt::yellow)); + } } } else { - // Set yellow to indicate we've filtered something (unless red) - if (vorGUI->m_rxIdentItem->foreground().color() != Qt::red) - { - vorGUI->m_rxIdentItem->setForeground(QBrush(Qt::yellow)); - vorGUI->m_rxMorseItem->setForeground(QBrush(Qt::yellow)); - } + qDebug() << "VORLocalizerGUI::handleMessage: Got MsgReportIdent for non-existant subChannelId " << subChannelId; } return true; @@ -938,142 +697,6 @@ void VORLocalizerGUI::handleInputMessages() } } -qint64 VORLocalizerGUI::fileAgeInDays(QString filename) -{ - QFile file(filename); - - if (file.exists()) - { - QDateTime modified = file.fileTime(QFileDevice::FileModificationTime); - - if (modified.isValid()) { - return modified.daysTo(QDateTime::currentDateTime()); - } else { - return -1; - } - } - - return -1; -} - -bool VORLocalizerGUI::confirmDownload(QString filename) -{ - qint64 age = fileAgeInDays(filename); - - if ((age == -1) || (age > 100)) - { - return true; - } - else - { - QMessageBox::StandardButton reply; - - if (age == 0) { - reply = QMessageBox::question(this, "Confirm download", "This file was last downloaded today. Are you sure you wish to redownload it?", QMessageBox::Yes|QMessageBox::No); - } else if (age == 1) { - reply = QMessageBox::question(this, "Confirm download", "This file was last downloaded yesterday. Are you sure you wish to redownload it?", QMessageBox::Yes|QMessageBox::No); - } else { - reply = QMessageBox::question(this, "Confirm download", QString("This file was last downloaded %1 days ago. Are you sure you wish to redownload this file?").arg(age), QMessageBox::Yes|QMessageBox::No); - } - - return reply == QMessageBox::Yes; - } -} - -QString VORLocalizerGUI::getDataDir() -{ - // Get directory to store app data in - QStringList locations = QStandardPaths::standardLocations(QStandardPaths::AppDataLocation); - // First dir is writable - return locations[0]; -} - -QString VORLocalizerGUI::getOpenAIPVORDBFilename(int i) -{ - if (countryCodes[i] != nullptr) { - return getDataDir() + "/" + countryCodes[i] + "_nav.aip"; - } else { - return ""; - } -} - -QString VORLocalizerGUI::getOpenAIPVORDBURL(int i) -{ - if (countryCodes[i] != nullptr) { - return QString(OPENAIP_NAVAIDS_URL).arg(countryCodes[i]); - } else { - return ""; - } -} - -QString VORLocalizerGUI::getVORDBFilename() -{ - return getDataDir() + "/vorDatabase.csv"; -} - -void VORLocalizerGUI::updateDownloadProgress(qint64 bytesRead, qint64 totalBytes) -{ - if (m_progressDialog) - { - m_progressDialog->setMaximum(totalBytes); - m_progressDialog->setValue(bytesRead); - } -} - -void VORLocalizerGUI::downloadFinished(const QString& filename, bool success) -{ - bool closeDialog = true; - if (success) - { - if (filename == getVORDBFilename()) - { - m_vors = NavAid::readNavAidsDB(filename); - - if (m_vors != nullptr) { - updateVORs(); - } - } - else if (filename == getOpenAIPVORDBFilename(m_countryIndex)) - { - m_countryIndex++; - - if (countryCodes[m_countryIndex] != nullptr) - { - QString vorDBFile = getOpenAIPVORDBFilename(m_countryIndex); - QString urlString = getOpenAIPVORDBURL(m_countryIndex); - QUrl dbURL(urlString); - m_progressDialog->setLabelText(QString("Downloading %1.").arg(urlString)); - m_progressDialog->setValue(m_countryIndex); - m_dlm.download(dbURL, vorDBFile); - closeDialog = false; - } - else - { - readNavAids(); - - if (m_vors) { - updateVORs(); - } - } - } - else - { - qDebug() << "VORLocalizerGUI::downloadFinished: Unexpected filename: " << filename; - } - } - else - { - qDebug() << "VORLocalizerGUI::downloadFinished: Failed: " << filename; - QMessageBox::warning(this, "Download failed", QString("Failed to download %1").arg(filename)); - } - if (closeDialog && m_progressDialog) - { - m_progressDialog->close(); - delete m_progressDialog; - m_progressDialog = nullptr; - } -} - void VORLocalizerGUI::on_startStop_toggled(bool checked) { if (m_doApplySettings) @@ -1083,59 +706,56 @@ void VORLocalizerGUI::on_startStop_toggled(bool checked) } } -void VORLocalizerGUI::on_getOurAirportsVORDB_clicked() -{ - // Don't try to download while already in progress - if (m_progressDialog == nullptr) - { - QString vorDBFile = getVORDBFilename(); - - if (confirmDownload(vorDBFile)) - { - // Download OurAirports navaid database to disk - QUrl dbURL(QString(OURAIRPORTS_NAVAIDS_URL)); - m_progressDialog = new QProgressDialog(this); - m_progressDialog->setCancelButton(nullptr); - m_progressDialog->setMinimumDuration(500); - m_progressDialog->setLabelText(QString("Downloading %1.").arg(OURAIRPORTS_NAVAIDS_URL)); - QNetworkReply *reply = m_dlm.download(dbURL, vorDBFile); - connect(reply, SIGNAL(downloadProgress(qint64,qint64)), this, SLOT(updateDownloadProgress(qint64,qint64))); - } - } -} - void VORLocalizerGUI::on_getOpenAIPVORDB_clicked() { // Don't try to download while already in progress if (!m_progressDialog) { - m_countryIndex = 0; - QString vorDBFile = getOpenAIPVORDBFilename(m_countryIndex); + m_progressDialog = new QProgressDialog(this); + m_progressDialog->setMaximum(OpenAIP::m_countryCodes.size()); + m_progressDialog->setCancelButton(nullptr); - if (confirmDownload(vorDBFile)) - { - // Download OpenAIP XML to disk - QString urlString = getOpenAIPVORDBURL(m_countryIndex); - QUrl dbURL(urlString); - m_progressDialog = new QProgressDialog(this); - m_progressDialog->setCancelButton(nullptr); - m_progressDialog->setMinimumDuration(500); - m_progressDialog->setMaximum(sizeof(countryCodes)/sizeof(countryCodes[0])); - m_progressDialog->setValue(0); - m_progressDialog->setLabelText(QString("Downloading %1.").arg(urlString)); - m_dlm.download(dbURL, vorDBFile); - } + m_openAIP.downloadNavAids(); } } void VORLocalizerGUI::readNavAids() { - m_vors = new QHash(); + m_vors = OpenAIP::readNavAids(); + updateVORs(); +} - for (int countryIndex = 0; countryCodes[countryIndex] != nullptr; countryIndex++) +void VORLocalizerGUI::downloadingURL(const QString& url) +{ + if (m_progressDialog) { - QString vorDBFile = getOpenAIPVORDBFilename(countryIndex); - NavAid::readNavAidsXML(m_vors, vorDBFile); + m_progressDialog->setLabelText(QString("Downloading %1.").arg(url)); + m_progressDialog->setValue(m_progressDialog->value() + 1); + } +} + +void VORLocalizerGUI::downloadError(const QString& error) +{ + QMessageBox::critical(this, "VOR Localizer", error); + if (m_progressDialog) + { + m_progressDialog->close(); + delete m_progressDialog; + m_progressDialog = nullptr; + } +} + +void VORLocalizerGUI::downloadNavAidsFinished() +{ + if (m_progressDialog) { + m_progressDialog->setLabelText("Reading NAVAIDs."); + } + readNavAids(); + if (m_progressDialog) + { + m_progressDialog->close(); + delete m_progressDialog; + m_progressDialog = nullptr; } } @@ -1231,7 +851,6 @@ VORLocalizerGUI::VORLocalizerGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISe m_tickCount(0), m_progressDialog(nullptr), m_vorModel(this), - m_vors(nullptr), m_lastFeatureState(0), m_rrSecondsCount(0) { @@ -1250,7 +869,9 @@ VORLocalizerGUI::VORLocalizerGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISe m_muteIcon.addPixmap(QPixmap("://sound_on.png"), QIcon::Normal, QIcon::Off); connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &))); - connect(&m_dlm, &HttpDownloadManager::downloadComplete, this, &VORLocalizerGUI::downloadFinished); + connect(&m_openAIP, &OpenAIP::downloadingURL, this, &VORLocalizerGUI::downloadingURL); + connect(&m_openAIP, &OpenAIP::downloadError, this, &VORLocalizerGUI::downloadError); + connect(&m_openAIP, &OpenAIP::downloadNavAidsFinished, this, &VORLocalizerGUI::downloadNavAidsFinished); m_vorLocalizer = reinterpret_cast(feature); m_vorLocalizer->setMessageQueueToGUI(getInputMessageQueue()); @@ -1293,22 +914,8 @@ VORLocalizerGUI::VORLocalizerGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISe } // Read in VOR information if it exists - bool useOurAirports = false; - - if (useOurAirports) - { - m_vors = NavAid::readNavAidsDB(getVORDBFilename()); - ui->getOpenAIPVORDB->setVisible(false); - } - else - { - readNavAids(); - ui->getOurAirportsVORDB->setVisible(false); - } - - if (m_vors) { - updateVORs(); - } + readNavAids(); + ui->getOurAirportsVORDB->setVisible(false); // Resize the table using dummy data resizeTable(); @@ -1346,6 +953,7 @@ VORLocalizerGUI::VORLocalizerGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISe VORLocalizerGUI::~VORLocalizerGUI() { delete ui; + qDeleteAll(m_vors); } void VORLocalizerGUI::blockApplySettings(bool block) @@ -1459,7 +1067,6 @@ void VORLocalizerGUI::tick() void VORLocalizerGUI::makeUIConnections() { QObject::connect(ui->startStop, &ButtonSwitch::toggled, this, &VORLocalizerGUI::on_startStop_toggled); - QObject::connect(ui->getOurAirportsVORDB, &QPushButton::clicked, this, &VORLocalizerGUI::on_getOurAirportsVORDB_clicked); QObject::connect(ui->getOpenAIPVORDB, &QPushButton::clicked, this, &VORLocalizerGUI::on_getOpenAIPVORDB_clicked); QObject::connect(ui->magDecAdjust, &ButtonSwitch::toggled, this, &VORLocalizerGUI::on_magDecAdjust_toggled); QObject::connect(ui->rrTime, &QDial::valueChanged, this, &VORLocalizerGUI::on_rrTime_valueChanged); diff --git a/plugins/feature/vorlocalizer/vorlocalizergui.h b/plugins/feature/vorlocalizer/vorlocalizergui.h index 0f741c952..51639ad92 100644 --- a/plugins/feature/vorlocalizer/vorlocalizergui.h +++ b/plugins/feature/vorlocalizer/vorlocalizergui.h @@ -37,10 +37,10 @@ #include "util/messagequeue.h" #include "util/httpdownloadmanager.h" #include "util/azel.h" +#include "util/openaip.h" #include "settings/rollupstate.h" #include "vorlocalizersettings.h" -#include "navaid.h" class PluginAPI; class FeatureUISet; @@ -239,9 +239,10 @@ private: QMenu *menu; // Column select context menu HttpDownloadManager m_dlm; QProgressDialog *m_progressDialog; + OpenAIP m_openAIP; int m_countryIndex; VORModel m_vorModel; - QHash *m_vors; + QList m_vors; QHash m_selectedVORs; AzEl m_azEl; // Position of station QIcon m_muteIcon; @@ -264,19 +265,11 @@ private: void calculateFreqOffset(VORGUI *vorGUI); void calculateFreqOffsets(); void updateVORs(); - QString getOpenAIPVORDBURL(int i); - QString getOpenAIPVORDBFilename(int i); - QString getVORDBFilename(); void readNavAids(); - // Move to util - QString getDataDir(); - qint64 fileAgeInDays(QString filename); - bool confirmDownload(QString filename); void updateChannelList(); private slots: void on_startStop_toggled(bool checked); - void on_getOurAirportsVORDB_clicked(); void on_getOpenAIPVORDB_clicked(); void on_magDecAdjust_toggled(bool checked); void on_rrTime_valueChanged(int value); @@ -288,11 +281,12 @@ private slots: void columnSelectMenuChecked(bool checked = false); void onWidgetRolled(QWidget* widget, bool rollDown); void onMenuDialogCalled(const QPoint& p); - void updateDownloadProgress(qint64 bytesRead, qint64 totalBytes); - void downloadFinished(const QString& filename, bool success); void handleInputMessages(); void updateStatus(); void tick(); + void downloadingURL(const QString& url); + void downloadError(const QString& error); + void downloadNavAidsFinished(); }; #endif // INCLUDE_VORLOCALIZERGUI_H diff --git a/sdrbase/util/openaip.h b/sdrbase/util/openaip.h index 12434ba82..2c358a22c 100644 --- a/sdrbase/util/openaip.h +++ b/sdrbase/util/openaip.h @@ -50,7 +50,6 @@ struct SDRBASE_API Airspace { }; QString m_category; // A-G, GLIDING, DANGER, PROHIBITED, TMZ - int m_id; QString m_country; // GB QString m_name; // BIGGIN HILL ATZ 129.405 - TODO: Extract frequency so we can tune to it AltLimit m_top; // Top of airspace @@ -139,11 +138,7 @@ struct SDRBASE_API Airspace { while(xmlReader.readNextStartElement()) { - if (xmlReader.name() == QLatin1String("ID")) - { - airspace->m_id = xmlReader.readElementText().toInt(); - } - else if (xmlReader.name() == QLatin1String("COUNTRY")) + if (xmlReader.name() == QLatin1String("COUNTRY")) { airspace->m_country = xmlReader.readElementText(); } @@ -237,7 +232,7 @@ struct SDRBASE_API Airspace { }; struct SDRBASE_API NavAid { - + int m_id; // Unique ID needed by VOR feature - Don't use value from database as that's 96-bit QString m_ident; // 2 or 3 character ident QString m_type; // NDB, VOR, VOR-DME or VORTAC QString m_name; @@ -258,6 +253,7 @@ struct SDRBASE_API NavAid { // OpenAIP XML file static QList readXML(const QString &filename) { + int uniqueId = 1; QList navAidInfo; QFile file(filename); if (file.open(QIODevice::ReadOnly | QIODevice::Text)) @@ -352,6 +348,7 @@ struct SDRBASE_API NavAid { } } NavAid *navAid = new NavAid(); + navAid->m_id = uniqueId++; navAid->m_ident = id; // Check idents conform to our filtering rules if (navAid->m_ident.size() < 2) { From 44f0746cbe0c345f202217f29569c261cddcab82 Mon Sep 17 00:00:00 2001 From: Jon Beniston Date: Tue, 3 May 2022 13:51:39 +0100 Subject: [PATCH 094/115] VOR Localizer: Update station icon on map when My Position preference is changed --- .../feature/vorlocalizer/vorlocalizergui.cpp | 53 +++++++++++++++++++ .../feature/vorlocalizer/vorlocalizergui.h | 1 + 2 files changed, 54 insertions(+) diff --git a/plugins/feature/vorlocalizer/vorlocalizergui.cpp b/plugins/feature/vorlocalizer/vorlocalizergui.cpp index a00085a19..7ea8815b9 100644 --- a/plugins/feature/vorlocalizer/vorlocalizergui.cpp +++ b/plugins/feature/vorlocalizer/vorlocalizergui.cpp @@ -945,6 +945,9 @@ VORLocalizerGUI::VORLocalizerGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISe ui->rrTurnTimeProgress->setValue(0); ui->rrTurnTimeProgress->setToolTip(tr("Round robin turn time %1s").arg(0)); + // Get updated when position changes + connect(&MainCore::instance()->getSettings(), &MainSettings::preferenceChanged, this, &VORLocalizerGUI::preferenceChanged); + displaySettings(); applySettings(true); makeUIConnections(); @@ -1064,6 +1067,56 @@ void VORLocalizerGUI::tick() } } +void VORLocalizerGUI::preferenceChanged(int elementType) +{ + Preferences::ElementType pref = (Preferences::ElementType)elementType; + if ((pref == Preferences::Latitude) || (pref == Preferences::Longitude) || (pref == Preferences::Altitude)) + { + Real stationLatitude = MainCore::instance()->getSettings().getLatitude(); + Real stationLongitude = MainCore::instance()->getSettings().getLongitude(); + Real stationAltitude = MainCore::instance()->getSettings().getAltitude(); + + if ( (stationLatitude != m_azEl.getLocationSpherical().m_latitude) + || (stationLongitude != m_azEl.getLocationSpherical().m_longitude) + || (stationAltitude != m_azEl.getLocationSpherical().m_altitude)) + { + m_azEl.setLocation(stationLatitude, stationLongitude, stationAltitude); + + // Update distances and what is visible + updateVORs(); + + // Update icon position on Map + QQuickItem *item = ui->map->rootObject(); + QObject *map = item->findChild("map"); + if (map != nullptr) + { + QObject *stationObject = map->findChild("station"); + if(stationObject != NULL) + { + QGeoCoordinate coords = stationObject->property("coordinate").value(); + coords.setLatitude(stationLatitude); + coords.setLongitude(stationLongitude); + coords.setAltitude(stationAltitude); + stationObject->setProperty("coordinate", QVariant::fromValue(coords)); + } + } + } + } + if (pref == Preferences::StationName) + { + // Update icon label on Map + QQuickItem *item = ui->map->rootObject(); + QObject *map = item->findChild("map"); + if (map != nullptr) + { + QObject *stationObject = map->findChild("station"); + if(stationObject != NULL) { + stationObject->setProperty("stationName", QVariant::fromValue(MainCore::instance()->getSettings().getStationName())); + } + } + } +} + void VORLocalizerGUI::makeUIConnections() { QObject::connect(ui->startStop, &ButtonSwitch::toggled, this, &VORLocalizerGUI::on_startStop_toggled); diff --git a/plugins/feature/vorlocalizer/vorlocalizergui.h b/plugins/feature/vorlocalizer/vorlocalizergui.h index 51639ad92..a6169e53b 100644 --- a/plugins/feature/vorlocalizer/vorlocalizergui.h +++ b/plugins/feature/vorlocalizer/vorlocalizergui.h @@ -287,6 +287,7 @@ private slots: void downloadingURL(const QString& url); void downloadError(const QString& error); void downloadNavAidsFinished(); + void preferenceChanged(int elementType); }; #endif // INCLUDE_VORLOCALIZERGUI_H From ebca05a8a12665be514cb10416dab64842b47018 Mon Sep 17 00:00:00 2001 From: Jon Beniston Date: Tue, 3 May 2022 14:53:56 +0100 Subject: [PATCH 095/115] localizer GUI updates. Add QSplitter to allow area for table & map to be adjusted. Remove obsolete OurAirports button. Add workaround for QT Map redraw bug. Automatically update VOR channels when channels are added or removed. Remove Nav Id from table as it doesn't mean anything from a user's perspective. --- plugins/feature/vorlocalizer/readme.md | 5 - .../feature/vorlocalizer/vorlocalizergui.cpp | 68 +++- .../feature/vorlocalizer/vorlocalizergui.h | 7 +- .../feature/vorlocalizer/vorlocalizergui.ui | 365 +++++++----------- .../vorlocalizer/vorlocalizersettings.h | 19 +- 5 files changed, 223 insertions(+), 241 deletions(-) diff --git a/plugins/feature/vorlocalizer/readme.md b/plugins/feature/vorlocalizer/readme.md index 057424524..d26641daa 100644 --- a/plugins/feature/vorlocalizer/readme.md +++ b/plugins/feature/vorlocalizer/readme.md @@ -55,10 +55,6 @@ Channels may be used in round robin turns if their number is not enough to cover When there is more than one turn for a device valid radial directions are averaged and the resulting average is used during the round robin loop. Averaging also takes place for reference and variable signal levels. -

    9: Refresh VOR demodulators list and allocation

    - -Use this button to (re)scan the available VOR demodulators in the SDRangel instance and (re)run the round robin allocation. -

    B: VOR Table

    The VOR table displays information about selected VORs. To select or deselect a VOR, double click it on the map. The information displayed includes: @@ -67,7 +63,6 @@ The VOR table displays information about selected VORs. To select or deselect a * Name - The name of the VOR. For example: 'LONDON'. * Freq (MHz) - The center frequency the VOR transmits on in MHz. The frequency is highlighted in green when the VOR is serviced by a demodulator. -* Nav Id - This is the VOR unique identifier from the VOR database. * Ident - A 2 or 3 character identifier for the VOR. For example: 'LON'. * Morse - The Morse code identifier for the VOR. For example: '.-.. --- -.' * RX Ident - This contains the demodulated ident. If it matches the expected ident, it will be displayed in green, if not, it will be displayed in red. If an ident is received that is not 2 or 3 characters, it will not be displayed, but the last received ident will be displayed in yellow. diff --git a/plugins/feature/vorlocalizer/vorlocalizergui.cpp b/plugins/feature/vorlocalizer/vorlocalizergui.cpp index 7ea8815b9..fb8d1356a 100644 --- a/plugins/feature/vorlocalizer/vorlocalizergui.cpp +++ b/plugins/feature/vorlocalizer/vorlocalizergui.cpp @@ -137,7 +137,6 @@ VORGUI::VORGUI(NavAid *navAid, VORLocalizerGUI *gui) : // These are deleted by QTableWidget m_nameItem = new QTableWidgetItem(); m_frequencyItem = new QTableWidgetItem(); - m_navIdItem = new QTableWidgetItem(); m_radialItem = new QTableWidgetItem(); m_identItem = new QTableWidgetItem(); m_morseItem = new QTableWidgetItem(); @@ -358,7 +357,6 @@ void VORLocalizerGUI::resizeTable() ui->vorData->setRowCount(row + 1); ui->vorData->setItem(row, VORLocalizerSettings::VOR_COL_NAME, new QTableWidgetItem("White Sulphur Springs")); ui->vorData->setItem(row, VORLocalizerSettings::VOR_COL_FREQUENCY, new QTableWidgetItem("Freq (MHz) ")); - ui->vorData->setItem(row, VORLocalizerSettings::VOR_COL_NAVID, new QTableWidgetItem("99999999")); ui->vorData->setItem(row, VORLocalizerSettings::VOR_COL_IDENT, new QTableWidgetItem("Ident ")); ui->vorData->setItem(row, VORLocalizerSettings::VOR_COL_MORSE, new QTableWidgetItem(Morse::toSpacedUnicode(morse))); ui->vorData->setItem(row, VORLocalizerSettings::VOR_COL_RADIAL, new QTableWidgetItem("Radial (o) ")); @@ -432,8 +430,6 @@ void VORLocalizerGUI::selectVOR(VORGUI *vorGUI, bool selected) ui->vorData->setRowCount(row + 1); ui->vorData->setItem(row, VORLocalizerSettings::VOR_COL_NAME, vorGUI->m_nameItem); ui->vorData->setItem(row, VORLocalizerSettings::VOR_COL_FREQUENCY, vorGUI->m_frequencyItem); - ui->vorData->setItem(row, VORLocalizerSettings::VOR_COL_NAVID, vorGUI->m_navIdItem); - ui->vorData->setItem(row, VORLocalizerSettings::VOR_COL_IDENT, vorGUI->m_identItem); ui->vorData->setItem(row, VORLocalizerSettings::VOR_COL_MORSE, vorGUI->m_morseItem); ui->vorData->setItem(row, VORLocalizerSettings::VOR_COL_RADIAL, vorGUI->m_radialItem); ui->vorData->setItem(row, VORLocalizerSettings::VOR_COL_RX_IDENT, vorGUI->m_rxIdentItem); @@ -562,7 +558,6 @@ bool VORLocalizerGUI::handleMessage(const Message& message) bool validRadial = report.getValidRadial(); vorGUI->m_radialItem->setData(Qt::DisplayRole, std::round(report.getRadial())); - vorGUI->m_navIdItem->setData(Qt::DisplayRole, subChannelId); if (validRadial) { vorGUI->m_radialItem->setForeground(QBrush(Qt::white)); @@ -780,7 +775,7 @@ void VORLocalizerGUI::on_centerShift_valueChanged(int value) applySettings(); } -void VORLocalizerGUI::on_channelsRefresh_clicked() +void VORLocalizerGUI::channelsRefresh() { if (m_doApplySettings) { @@ -915,7 +910,6 @@ VORLocalizerGUI::VORLocalizerGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISe // Read in VOR information if it exists readNavAids(); - ui->getOurAirportsVORDB->setVisible(false); // Resize the table using dummy data resizeTable(); @@ -950,11 +944,24 @@ VORLocalizerGUI::VORLocalizerGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISe displaySettings(); applySettings(true); + + connect(&m_redrawMapTimer, &QTimer::timeout, this, &VORLocalizerGUI::redrawMap); + m_redrawMapTimer.setSingleShot(true); + ui->map->installEventFilter(this); + makeUIConnections(); + + // Update channel list when added/removed + connect(MainCore::instance(), &MainCore::channelAdded, this, &VORLocalizerGUI::channelsRefresh); + connect(MainCore::instance(), &MainCore::channelRemoved, this, &VORLocalizerGUI::channelsRefresh); + // List already opened channels + channelsRefresh(); } VORLocalizerGUI::~VORLocalizerGUI() { + disconnect(&m_redrawMapTimer, &QTimer::timeout, this, &VORLocalizerGUI::redrawMap); + m_redrawMapTimer.stop(); delete ui; qDeleteAll(m_vors); } @@ -1117,6 +1124,52 @@ void VORLocalizerGUI::preferenceChanged(int elementType) } } +void VORLocalizerGUI::redrawMap() +{ + // An awful workaround for https://bugreports.qt.io/browse/QTBUG-100333 + // Also used in ADS-B demod + QQuickItem *item = ui->map->rootObject(); + if (item) + { + QObject *object = item->findChild("map"); + if (object) + { + double zoom = object->property("zoomLevel").value(); + object->setProperty("zoomLevel", QVariant::fromValue(zoom+1)); + object->setProperty("zoomLevel", QVariant::fromValue(zoom)); + } + } +} + +void VORLocalizerGUI::showEvent(QShowEvent *event) +{ + if (!event->spontaneous()) + { + // Workaround for https://bugreports.qt.io/browse/QTBUG-100333 + // MapQuickItems can be in wrong position when window is first displayed + m_redrawMapTimer.start(500); + } +} + +bool VORLocalizerGUI::eventFilter(QObject *obj, QEvent *event) +{ + if (obj == ui->map) + { + if (event->type() == QEvent::Resize) + { + // Workaround for https://bugreports.qt.io/browse/QTBUG-100333 + // MapQuickItems can be in wrong position after vertical resize + QResizeEvent *resizeEvent = static_cast(event); + QSize oldSize = resizeEvent->oldSize(); + QSize size = resizeEvent->size(); + if (oldSize.height() != size.height()) { + redrawMap(); + } + } + } + return false; +} + void VORLocalizerGUI::makeUIConnections() { QObject::connect(ui->startStop, &ButtonSwitch::toggled, this, &VORLocalizerGUI::on_startStop_toggled); @@ -1124,5 +1177,4 @@ void VORLocalizerGUI::makeUIConnections() QObject::connect(ui->magDecAdjust, &ButtonSwitch::toggled, this, &VORLocalizerGUI::on_magDecAdjust_toggled); QObject::connect(ui->rrTime, &QDial::valueChanged, this, &VORLocalizerGUI::on_rrTime_valueChanged); QObject::connect(ui->centerShift, &QDial::valueChanged, this, &VORLocalizerGUI::on_centerShift_valueChanged); - QObject::connect(ui->channelsRefresh, &QPushButton::clicked, this, &VORLocalizerGUI::on_channelsRefresh_clicked); } diff --git a/plugins/feature/vorlocalizer/vorlocalizergui.h b/plugins/feature/vorlocalizer/vorlocalizergui.h index a6169e53b..20c953f31 100644 --- a/plugins/feature/vorlocalizer/vorlocalizergui.h +++ b/plugins/feature/vorlocalizer/vorlocalizergui.h @@ -64,7 +64,6 @@ public: QTableWidgetItem *m_nameItem; QTableWidgetItem *m_frequencyItem; - QTableWidgetItem *m_navIdItem; QTableWidgetItem *m_identItem; QTableWidgetItem *m_morseItem; QTableWidgetItem *m_radialItem; @@ -249,6 +248,7 @@ private: QTimer m_statusTimer; int m_lastFeatureState; int m_rrSecondsCount; + QTimer m_redrawMapTimer; explicit VORLocalizerGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISet, Feature *feature, QWidget* parent = nullptr); virtual ~VORLocalizerGUI(); @@ -257,6 +257,7 @@ private: void applySettings(bool force = false); void displaySettings(); bool handleMessage(const Message& message); + void redrawMap(); void makeUIConnections(); void resizeTable(); @@ -274,7 +275,7 @@ private slots: void on_magDecAdjust_toggled(bool checked); void on_rrTime_valueChanged(int value); void on_centerShift_valueChanged(int value); - void on_channelsRefresh_clicked(); + void channelsRefresh(); void vorData_sectionMoved(int logicalIndex, int oldVisualIndex, int newVisualIndex); void vorData_sectionResized(int logicalIndex, int oldSize, int newSize); void columnSelectMenu(QPoint pos); @@ -288,6 +289,8 @@ private slots: void downloadError(const QString& error); void downloadNavAidsFinished(); void preferenceChanged(int elementType); + virtual void showEvent(QShowEvent *event); + virtual bool eventFilter(QObject *obj, QEvent *event); }; #endif // INCLUDE_VORLOCALIZERGUI_H diff --git a/plugins/feature/vorlocalizer/vorlocalizergui.ui b/plugins/feature/vorlocalizer/vorlocalizergui.ui index 4efe999be..62180fc6b 100644 --- a/plugins/feature/vorlocalizer/vorlocalizergui.ui +++ b/plugins/feature/vorlocalizer/vorlocalizergui.ui @@ -6,7 +6,7 @@ 0 0 - 462 + 470 850
    @@ -40,7 +40,7 @@ 0 0 461 - 61 + 31
    @@ -85,23 +85,6 @@
    - - - - true - - - Download OurAirports VOR database - - - - - - - :/demodvor/icons/vor.png:/demodvor/icons/vor.png - - - @@ -136,6 +119,13 @@ + + + + Qt::Vertical + + + @@ -223,6 +213,13 @@ QToolTip{background-color: white; color: black;} + + + + Qt::Vertical + + + @@ -275,22 +272,12 @@ QToolTip{background-color: white; color: black;} - + - Qt::Horizontal + Qt::Vertical - - - 40 - 20 - - - +
    - - - - @@ -306,27 +293,7 @@ QToolTip{background-color: white; color: black;} - - - - 24 - 16777215 - - - - Refresh VOR channels available - - - - - - - :/recycle.png:/recycle.png - - - - - + Qt::Horizontal @@ -345,14 +312,14 @@ QToolTip{background-color: white; color: black;} - 0 - 110 - 461 - 145 + 10 + 50 + 441 + 710 - + 0 0 @@ -366,178 +333,146 @@ QToolTip{background-color: white; color: black;} VORs - - - 2 - + - 3 + 0 - 3 + 0 - 3 + 0 - 3 + 0 - - - QAbstractItemView::NoEditTriggers - - - - Name - - - Name of the VOR - - - - - Freq (MHz) - - - Frequency of the VOR in MHz - - - - - Nav Id - - - Offset of the VOR's frequency from the current center frequency. Red indicates out of range. - - - - - Ident - - - Ident for the VOR - - - - - Morse - - - Morse code ident for the VOR - - - - - RX Ident - - - Received ident - - - - - RX Morse - - - Received Morse code ident - - - - - Radial (°) - - - Calculated radial from the VOR - - - - - Ref (dB) - - - Magnitude of received reference signal in dB - - - - - Var (dB) - - - Magnitude of received variable signal in dB - - - - - Mute - - - Mute/unmute audio from selected VORs - - - - - - - - - - 0 - 258 - 461 - 581 - - - - - 0 - 0 - - - - Map - - - - 2 - - - 3 - - - 3 - - - 3 - - - 3 - - - + 0 0 - - - 100 - 500 - - - - VOR map - - - QQuickWidget::SizeRootObjectToView - - - - - + + Qt::Vertical + + + + 0 + 1 + + + + QAbstractItemView::NoEditTriggers + + + + Name + + + Name of the VOR + + + + + Freq (MHz) + + + Frequency of the VOR in MHz + + + + + Ident + + + Ident for the VOR + + + + + Morse + + + Morse code ident for the VOR + + + + + RX Ident + + + Received ident + + + + + RX Morse + + + Received Morse code ident + + + + + Radial (°) + + + Calculated radial from the VOR + + + + + Ref (dB) + + + Magnitude of received reference signal in dB + + + + + Var (dB) + + + Magnitude of received variable signal in dB + + + + + Mute + + + Mute/unmute audio from selected VORs + + + + + + + 0 + 4 + + + + + 100 + 500 + + + + VOR map + + + QQuickWidget::SizeRootObjectToView + + + + + + + @@ -562,9 +497,7 @@ QToolTip{background-color: white; color: black;} - getOurAirportsVORDB vorData - map diff --git a/plugins/feature/vorlocalizer/vorlocalizersettings.h b/plugins/feature/vorlocalizer/vorlocalizersettings.h index c34a0f790..0c50e3beb 100644 --- a/plugins/feature/vorlocalizer/vorlocalizersettings.h +++ b/plugins/feature/vorlocalizer/vorlocalizersettings.h @@ -77,18 +77,17 @@ struct VORLocalizerSettings int m_workspaceIndex; QByteArray m_geometryBytes; - static const int VORDEMOD_COLUMNS = 11; + static const int VORDEMOD_COLUMNS = 10; static const int VOR_COL_NAME = 0; static const int VOR_COL_FREQUENCY = 1; - static const int VOR_COL_NAVID = 2; - static const int VOR_COL_IDENT = 3; - static const int VOR_COL_MORSE = 4; - static const int VOR_COL_RX_IDENT = 5; - static const int VOR_COL_RX_MORSE = 6; - static const int VOR_COL_RADIAL = 7; - static const int VOR_COL_REF_MAG = 8; - static const int VOR_COL_VAR_MAG = 9; - static const int VOR_COL_MUTE = 10; + static const int VOR_COL_IDENT = 2; + static const int VOR_COL_MORSE = 3; + static const int VOR_COL_RX_IDENT = 4; + static const int VOR_COL_RX_MORSE = 5; + static const int VOR_COL_RADIAL = 6; + static const int VOR_COL_REF_MAG = 7; + static const int VOR_COL_VAR_MAG = 8; + static const int VOR_COL_MUTE = 9; int m_columnIndexes[VORDEMOD_COLUMNS];//!< How the columns are ordered in the table int m_columnSizes[VORDEMOD_COLUMNS]; //!< Size of the coumns in the table From 6a6ccbeecd9adc28b35fff8ac6cf3a7c4145ad0b Mon Sep 17 00:00:00 2001 From: Jon Beniston Date: Wed, 4 May 2022 08:20:51 +0100 Subject: [PATCH 096/115] VOR Localizer: Fix tooltop --- plugins/feature/vorlocalizer/vorlocalizergui.ui | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/feature/vorlocalizer/vorlocalizergui.ui b/plugins/feature/vorlocalizer/vorlocalizergui.ui index 62180fc6b..95611c090 100644 --- a/plugins/feature/vorlocalizer/vorlocalizergui.ui +++ b/plugins/feature/vorlocalizer/vorlocalizergui.ui @@ -170,7 +170,7 @@
    - Sound volume (%) + Round robin turn time (s) 20s From 2f1fe7ecb3573584d08ee29129f1fa15f4c8112a Mon Sep 17 00:00:00 2001 From: Jon Beniston Date: Wed, 4 May 2022 08:21:50 +0100 Subject: [PATCH 097/115] VOR Localizer: Update channels when device updated or when feature started, as device b/w or whether fixed center freq may have changed --- plugins/feature/vorlocalizer/vorlocalizergui.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/plugins/feature/vorlocalizer/vorlocalizergui.cpp b/plugins/feature/vorlocalizer/vorlocalizergui.cpp index fb8d1356a..dfb398814 100644 --- a/plugins/feature/vorlocalizer/vorlocalizergui.cpp +++ b/plugins/feature/vorlocalizer/vorlocalizergui.cpp @@ -698,6 +698,12 @@ void VORLocalizerGUI::on_startStop_toggled(bool checked) { VORLocalizer::MsgStartStop *message = VORLocalizer::MsgStartStop::create(checked); m_vorLocalizer->getInputMessageQueue()->push(message); + + if (checked) + { + // Refresh channels in case device b/w has changed + channelsRefresh(); + } } } @@ -954,6 +960,8 @@ VORLocalizerGUI::VORLocalizerGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISe // Update channel list when added/removed connect(MainCore::instance(), &MainCore::channelAdded, this, &VORLocalizerGUI::channelsRefresh); connect(MainCore::instance(), &MainCore::channelRemoved, this, &VORLocalizerGUI::channelsRefresh); + // Also replan when device changed (as bandwidth may change or may becomed fixed center freq) + connect(MainCore::instance(), &MainCore::deviceChanged, this, &VORLocalizerGUI::channelsRefresh); // List already opened channels channelsRefresh(); } From cfa50510e8fbafda5599bbc51eaaad8a8b638423 Mon Sep 17 00:00:00 2001 From: Jon Beniston Date: Wed, 4 May 2022 08:27:25 +0100 Subject: [PATCH 098/115] VOR Localizer: Add support for devices such as File Input for which the center frequency can't be changed. Use device b/w instead of channel b/w, as the latter may be decimated. --- .../vorlocalizer/vorlocalizerworker.cpp | 153 +++++++++--------- .../feature/vorlocalizer/vorlocalizerworker.h | 6 +- 2 files changed, 85 insertions(+), 74 deletions(-) diff --git a/plugins/feature/vorlocalizer/vorlocalizerworker.cpp b/plugins/feature/vorlocalizer/vorlocalizerworker.cpp index 5b64def9a..0dc5f5621 100644 --- a/plugins/feature/vorlocalizer/vorlocalizerworker.cpp +++ b/plugins/feature/vorlocalizer/vorlocalizerworker.cpp @@ -24,7 +24,11 @@ #include "SWGErrorResponse.h" #include "device/deviceset.h" +#include "device/deviceapi.h" +#include "dsp/devicesamplesource.h" +#include "dsp/devicesamplesink.h" #include "channel/channelapi.h" +#include "channel/channelwebapiutils.h" #include "webapi/webapiadapterinterface.h" #include "webapi/webapiutils.h" #include "maincore.h" @@ -78,6 +82,7 @@ void VorLocalizerWorker::started() m_rrTimer.start(m_settings.m_rrTime * 1000); disconnect(thread(), SIGNAL(started()), this, SLOT(started())); } + void VorLocalizerWorker::stopWork() { QMutexLocker mutexLocker(&m_mutex); @@ -205,6 +210,53 @@ void VorLocalizerWorker::updateHardware() m_mutex.unlock(); } +quint64 VorLocalizerWorker::getDeviceCenterFrequency(int deviceIndex) +{ + std::vector deviceSets = MainCore::instance()->getDeviceSets(); + if (deviceIndex < deviceSets.size()) + { + DeviceSet *deviceSet = deviceSets[deviceIndex]; + if (deviceSet->m_deviceSourceEngine) + { + DeviceSampleSource *source = deviceSet->m_deviceAPI->getSampleSource(); + return source->getCenterFrequency(); + } + else if (deviceSet->m_deviceSinkEngine) + { + DeviceSampleSink *sink = deviceSet->m_deviceAPI->getSampleSink(); + return sink->getCenterFrequency(); + } + } + return 0; +} + +int VorLocalizerWorker::getDeviceSampleRate(int deviceIndex) +{ + std::vector deviceSets = MainCore::instance()->getDeviceSets(); + if (deviceIndex < deviceSets.size()) + { + DeviceSet *deviceSet = deviceSets[deviceIndex]; + if (deviceSet->m_deviceSourceEngine) + { + DeviceSampleSource *source = deviceSet->m_deviceAPI->getSampleSource(); + return source->getSampleRate(); + } + else if (deviceSet->m_deviceSinkEngine) + { + DeviceSampleSink *sink = deviceSet->m_deviceAPI->getSampleSink(); + return sink->getSampleRate(); + } + } + return 0; +} + +// Does this device have a center frequency setting (FileInput doesn't) +bool VorLocalizerWorker::hasCenterFrequencySetting(int deviceIndex) +{ + double deviceFrequency; + return !ChannelWebAPIUtils::getCenterFrequency(deviceIndex, deviceFrequency); +} + void VorLocalizerWorker::removeVORChannel(int navId) { qDebug("VorLocalizerWorker::removeVORChannel: %d", navId); @@ -294,7 +346,12 @@ void VorLocalizerWorker::updateChannels() RRTurnPlan turnPlan(deviceChannel); int fMin = vorList.front().m_frequency; int fMax = vorList.back().m_frequency; - int devFreq = (fMin + fMax) / 2; + int devFreq; + if (turnPlan.m_fixedCenterFrequency) { + devFreq = getDeviceCenterFrequency(turnPlan.m_device.m_deviceIndex); + } else { + devFreq = (fMin + fMax) / 2; + } turnPlan.m_device.m_frequency = devFreq; int iCh = 0; @@ -321,7 +378,6 @@ void VorLocalizerWorker::updateChannels() ++it; } } - iCh++; } @@ -359,7 +415,12 @@ void VorLocalizerWorker::updateChannels() RRTurnPlan turnPlan(deviceChannel); int fMin = vorList.front().m_frequency; int fMax = vorList.back().m_frequency; - int devFreq = (fMin + fMax) / 2; + int devFreq; + if (turnPlan.m_fixedCenterFrequency) { + devFreq = getDeviceCenterFrequency(turnPlan.m_device.m_deviceIndex); + } else { + devFreq = (fMin + fMax) / 2; + } turnPlan.m_device.m_frequency = devFreq; int iCh = 0; @@ -406,69 +467,6 @@ void VorLocalizerWorker::updateChannels() rrNextTurn(); } -void VorLocalizerWorker::allocateChannel(ChannelAPI *channel, int vorFrequency, int vorNavId, int channelShift) -{ - VORLocalizerSettings::AvailableChannel& availableChannel = m_availableChannels->operator[](channel); - qDebug() << "VorLocalizerWorker::allocateChannel:" - << " vorNavId:" << vorNavId - << " vorFrequency:" << vorFrequency - << " channelShift:" << channelShift - << " deviceIndex:" << availableChannel.m_deviceSetIndex - << " channelIndex:" << availableChannel.m_channelIndex; - double deviceFrequency = vorFrequency - channelShift; - setDeviceFrequency(availableChannel.m_deviceSetIndex, deviceFrequency); - setChannelShift(availableChannel.m_deviceSetIndex, availableChannel.m_channelIndex, channelShift, vorNavId); - availableChannel.m_navId = vorNavId; -} - -void VorLocalizerWorker::setDeviceFrequency(int deviceIndex, double targetFrequency) -{ - SWGSDRangel::SWGDeviceSettings deviceSettingsResponse; - SWGSDRangel::SWGErrorResponse errorResponse; - int httpRC; - - // Get current device center frequency - httpRC = m_webAPIAdapterInterface->devicesetDeviceSettingsGet( - deviceIndex, - deviceSettingsResponse, - errorResponse - ); - - if (httpRC/100 != 2) - { - qWarning("VorLocalizerWorker::setDeviceFrequency: get device frequency error %d: %s", - httpRC, qPrintable(*errorResponse.getMessage())); - } - - QJsonObject *jsonObj = deviceSettingsResponse.asJsonObject(); - - // Update centerFrequency - WebAPIUtils::setSubObjectDouble(*jsonObj, "centerFrequency", targetFrequency); - QStringList deviceSettingsKeys; - deviceSettingsKeys.append("centerFrequency"); - deviceSettingsResponse.init(); - deviceSettingsResponse.fromJsonObject(*jsonObj); - SWGSDRangel::SWGErrorResponse errorResponse2; - - httpRC = m_webAPIAdapterInterface->devicesetDeviceSettingsPutPatch( - deviceIndex, - false, // PATCH - deviceSettingsKeys, - deviceSettingsResponse, - errorResponse2 - ); - - if (httpRC/100 == 2) - { - qDebug("VorLocalizerWorker::setDeviceFrequency: set device frequency %f OK", targetFrequency); - } - else - { - qWarning("VorLocalizerWorker::setDeviceFrequency: set device frequency error %d: %s", - httpRC, qPrintable(*errorResponse2.getMessage())); - } -} - void VorLocalizerWorker::setChannelShift(int deviceIndex, int channelIndex, double targetOffset, int vorNavId) { SWGSDRangel::SWGChannelSettings channelSettingsResponse; @@ -674,14 +672,16 @@ void VorLocalizerWorker::getChannelsByDevice( for (; itr != availableChannels->end(); ++itr) { devicesChannelsMap[itr->m_deviceSetIndex].m_device.m_deviceIndex = itr->m_deviceSetIndex; - devicesChannelsMap[itr->m_deviceSetIndex].m_bandwidth = itr->m_basebandSampleRate; + devicesChannelsMap[itr->m_deviceSetIndex].m_bandwidth = getDeviceSampleRate(itr->m_deviceSetIndex); // Get b/w of device, not channel, as the latter may be decimated devicesChannelsMap[itr->m_deviceSetIndex].m_channels.push_back(RRChannel{itr->m_channelAPI, itr->m_channelIndex, 0, -1}); } - QMap::const_iterator itm = devicesChannelsMap.begin(); + QMap::iterator itm = devicesChannelsMap.begin(); devicesChannels.clear(); - for (; itm != devicesChannelsMap.end(); ++itm) { + for (; itm != devicesChannelsMap.end(); ++itm) + { + itm->m_fixedCenterFrequency = hasCenterFrequencySetting(itm->m_device.m_deviceIndex); devicesChannels.push_back(*itm); } @@ -700,23 +700,32 @@ void VorLocalizerWorker::rrNextTurn() unsigned int turnCount = m_rrTurnCounters[iDevPlan]; int deviceIndex = rrPlan[turnCount].m_device.m_deviceIndex; int deviceFrequency = rrPlan[turnCount].m_device.m_frequency - m_settings.m_centerShift; + qDebug() << "VorLocalizerWorker::rrNextTurn: " << "turn:" << turnCount << "device:" << deviceIndex << "frequency:" << deviceFrequency - m_settings.m_centerShift; - setDeviceFrequency(deviceIndex, deviceFrequency); + + if (!rrPlan[turnCount].m_fixedCenterFrequency) { + ChannelWebAPIUtils::setCenterFrequency(deviceIndex, deviceFrequency); + } for (auto channel : rrPlan[turnCount].m_channels) { + int shift = channel.m_frequencyShift; + if (!rrPlan[turnCount].m_fixedCenterFrequency) { + shift += m_settings.m_centerShift; + } + qDebug() << "VorLocalizerWorker::rrNextTurn: " << "device:" << deviceIndex << "channel:" << channel.m_channelIndex - << "shift:" << channel.m_frequencyShift + m_settings.m_centerShift + << "shift:" << shift << "navId:" << channel.m_navId; setChannelShift( deviceIndex, channel.m_channelIndex, - channel.m_frequencyShift + m_settings.m_centerShift, + shift, channel.m_navId ); m_channelAllocations[channel.m_navId] = ChannelAllocation{ diff --git a/plugins/feature/vorlocalizer/vorlocalizerworker.h b/plugins/feature/vorlocalizer/vorlocalizerworker.h index c6d3c724a..daf693c44 100644 --- a/plugins/feature/vorlocalizer/vorlocalizerworker.h +++ b/plugins/feature/vorlocalizer/vorlocalizerworker.h @@ -121,6 +121,7 @@ private: RRDevice m_device; int m_bandwidth; std::vector m_channels; + bool m_fixedCenterFrequency; // Devices such as FileInput that can't have center freq changed RRTurnPlan() = default; RRTurnPlan(const RRTurnPlan&) = default; @@ -157,10 +158,11 @@ private: void removeVORChannel(int navId); void addVORChannel(const VORLocalizerSubChannelSettings& subChannelSettings); void updateChannels(); //!< (re)allocate channels to service VORs - void allocateChannel(ChannelAPI *channel, int vorFrequency, int vorNavId, int channelShift); - void setDeviceFrequency(int deviceIndex, double targetFrequency); void setChannelShift(int deviceIndex, int channelIndex, double targetOffset, int vorNavId); void setAudioMute(int vorNavId, bool audioMute); + static quint64 getDeviceCenterFrequency(int deviceIndex); + static int getDeviceSampleRate(int deviceIndex); + static bool hasCenterFrequencySetting(int deviceIndex); static void generateIndexCombinations(int length, int subLength, std::vector>& indexCombinations); static void getVORRanges(const QList& vors, int subLength, std::vector& vorRanges); static void filterVORRanges(std::vector& vorRanges, int thresholdBW); From 59f1f6099825490e49ad40efec711f85be079cdf Mon Sep 17 00:00:00 2001 From: Jon Beniston Date: Wed, 4 May 2022 08:30:40 +0100 Subject: [PATCH 099/115] VOR Localizer: Fix link to demod plugin --- plugins/feature/vorlocalizer/readme.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/feature/vorlocalizer/readme.md b/plugins/feature/vorlocalizer/readme.md index d26641daa..2f4050fb9 100644 --- a/plugins/feature/vorlocalizer/readme.md +++ b/plugins/feature/vorlocalizer/readme.md @@ -2,7 +2,7 @@

    Introduction

    -This plugin can control and receive information from single channel VOR demodulators (see [VOR single channel demodulator](../../channelrx/demodvorsc/readme.md) for details) and collate information from multiple VOR demodulators in order to show your position on a map. +This plugin can control and receive information from VOR demodulators (see [VOR demodulator](../../channelrx/demodvor/readme.md) for details) and collate information from multiple VOR demodulators in order to show your position on a map.

    Interface

    @@ -19,7 +19,7 @@ There are 3 sections in this interface:

    1: Start/Stop plugin

    -This button starts or stops the plugin +This button starts or stops the plugin.

    2: Download VOR Database

    @@ -35,7 +35,7 @@ Available VOR demodulator channels are allocated to service the selected VORs on

    5: Round robin turn time progress

    -Shows the round robin turn time progress +Shows the round robin turn time progress.

    6: Force averaging over round robin turn time

    From 52564bee36943f2127e8479b0db31719e7eaf8b0 Mon Sep 17 00:00:00 2001 From: Jon Beniston Date: Wed, 4 May 2022 08:39:10 +0100 Subject: [PATCH 100/115] VOR Localizer: Change GUI label from Sh to delta cf --- plugins/feature/vorlocalizer/vorlocalizergui.ui | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/feature/vorlocalizer/vorlocalizergui.ui b/plugins/feature/vorlocalizer/vorlocalizergui.ui index 95611c090..1653842f4 100644 --- a/plugins/feature/vorlocalizer/vorlocalizergui.ui +++ b/plugins/feature/vorlocalizer/vorlocalizergui.ui @@ -223,7 +223,7 @@ QToolTip{background-color: white; color: black;} - Sh + Δcf From 099e111d53a3bb2d466cd6ade627ea34aa5b429f Mon Sep 17 00:00:00 2001 From: Jon Beniston Date: Wed, 4 May 2022 09:08:31 +0100 Subject: [PATCH 101/115] VOR Localizer: Add Ident column data to table --- plugins/feature/vorlocalizer/vorlocalizergui.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/feature/vorlocalizer/vorlocalizergui.cpp b/plugins/feature/vorlocalizer/vorlocalizergui.cpp index dfb398814..79a2895e0 100644 --- a/plugins/feature/vorlocalizer/vorlocalizergui.cpp +++ b/plugins/feature/vorlocalizer/vorlocalizergui.cpp @@ -430,6 +430,7 @@ void VORLocalizerGUI::selectVOR(VORGUI *vorGUI, bool selected) ui->vorData->setRowCount(row + 1); ui->vorData->setItem(row, VORLocalizerSettings::VOR_COL_NAME, vorGUI->m_nameItem); ui->vorData->setItem(row, VORLocalizerSettings::VOR_COL_FREQUENCY, vorGUI->m_frequencyItem); + ui->vorData->setItem(row, VORLocalizerSettings::VOR_COL_IDENT, vorGUI->m_identItem); ui->vorData->setItem(row, VORLocalizerSettings::VOR_COL_MORSE, vorGUI->m_morseItem); ui->vorData->setItem(row, VORLocalizerSettings::VOR_COL_RADIAL, vorGUI->m_radialItem); ui->vorData->setItem(row, VORLocalizerSettings::VOR_COL_RX_IDENT, vorGUI->m_rxIdentItem); From 2fafb2afe744529ed70df1514a2c0d6595bad166 Mon Sep 17 00:00:00 2001 From: Jon Beniston Date: Wed, 4 May 2022 09:08:46 +0100 Subject: [PATCH 102/115] VOR Localizer: Update doc images --- doc/img/VORLocalizer_plugin.png | Bin 47300 -> 27788 bytes doc/img/VORLocalizer_plugin.xcf | Bin 140185 -> 98125 bytes doc/img/VORLocalizer_settings.png | Bin 12613 -> 10290 bytes doc/img/VORLocalizer_settings.xcf | Bin 44049 -> 40263 bytes 4 files changed, 0 insertions(+), 0 deletions(-) diff --git a/doc/img/VORLocalizer_plugin.png b/doc/img/VORLocalizer_plugin.png index 319c42aab191c837d128b146f5815c13d39230f6..e80750b7dda235c0c4f8d7e5a4168608092aa2a1 100644 GIT binary patch literal 27788 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 diff --git a/doc/img/VORLocalizer_plugin.xcf b/doc/img/VORLocalizer_plugin.xcf index 930e0cdc90abc0d3c0d2d028d91bbdc5799e90ac..a0927b554299980b47ad8fae0bec45467a7e3683 100644 GIT binary patch literal 98125 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 Rfp&U`jD+I*GBJa|{{!7DwPpYS diff --git a/doc/img/VORLocalizer_settings.xcf b/doc/img/VORLocalizer_settings.xcf index b62d8525def4bcf22a1572f923d7ec8714b10703..ce1bd733f507176ab46530730a021b17ee64149f 100644 GIT binary patch literal 40263 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 OijwI#IHlA}ivAzE#MSfw From 7d2fc09a65d3e50577e1c17216bdcce1541afa31 Mon Sep 17 00:00:00 2001 From: Jon Beniston Date: Wed, 4 May 2022 09:51:28 +0100 Subject: [PATCH 103/115] VOR Demod: Reset state when navId changes, so we don't report old ident for new navId --- plugins/channelrx/demodvor/vordemod.cpp | 7 ++++++- plugins/channelrx/demodvor/vordemodsink.cpp | 13 +++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/plugins/channelrx/demodvor/vordemod.cpp b/plugins/channelrx/demodvor/vordemod.cpp index 6ada23dbc..ed3d12f94 100644 --- a/plugins/channelrx/demodvor/vordemod.cpp +++ b/plugins/channelrx/demodvor/vordemod.cpp @@ -257,6 +257,12 @@ void VORDemod::applySettings(const VORDemodSettings& settings, bool force) } if ((m_settings.m_navId != settings.m_navId) || force) { reverseAPIKeys.append("navId"); + + // Reset state so we don't report old data for new NavId + m_radial = 0.0f; + m_refMag = 0.0f; + m_varMag = 0.0f; + m_morseIdent = ""; } if ((m_settings.m_squelch != settings.m_squelch) || force) { reverseAPIKeys.append("squelch"); @@ -509,7 +515,6 @@ void VORDemod::webapiFormatChannelReport(SWGSDRangel::SWGChannelReport& response double magsqAvg, magsqPeak; int nbMagsqSamples; getMagSqLevels(magsqAvg, magsqPeak, nbMagsqSamples); - response.getVorDemodReport()->setChannelPowerDb(CalcDb::dbPower(magsqAvg)); response.getVorDemodReport()->setSquelch(m_basebandSink->getSquelchOpen() ? 1 : 0); response.getVorDemodReport()->setAudioSampleRate(m_basebandSink->getAudioSampleRate()); diff --git a/plugins/channelrx/demodvor/vordemodsink.cpp b/plugins/channelrx/demodvor/vordemodsink.cpp index 2fc56080b..89567f524 100644 --- a/plugins/channelrx/demodvor/vordemodsink.cpp +++ b/plugins/channelrx/demodvor/vordemodsink.cpp @@ -401,6 +401,19 @@ void VORDemodSCSink::applySettings(const VORDemodSettings& settings, bool force) m_squelchLevel = CalcDb::powerFromdB(settings.m_squelch); } + if (m_settings.m_navId != settings.m_navId) + { + // Reset state when navId changes, so we don't report old ident for new navId + m_binSampleCnt = 0; + m_binCnt = 0; + m_identNoise = 0.0001f; + for (int i = 0; i < m_identBins; i++) + { + m_identMaxs[i] = 0.0f; + } + m_ident = ""; + } + m_settings = settings; } From 600699a1a8d07e0f6ef3b3f1d5b6023e5ac7cca0 Mon Sep 17 00:00:00 2001 From: Jon Beniston Date: Wed, 4 May 2022 10:05:03 +0100 Subject: [PATCH 104/115] VOR Demodulator: Reset filters when navId changes, to avoid glitches in calculated radial --- plugins/channelrx/demodvor/vordemod.cpp | 4 ++-- plugins/channelrx/demodvor/vordemodsink.cpp | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/plugins/channelrx/demodvor/vordemod.cpp b/plugins/channelrx/demodvor/vordemod.cpp index ed3d12f94..2397ac790 100644 --- a/plugins/channelrx/demodvor/vordemod.cpp +++ b/plugins/channelrx/demodvor/vordemod.cpp @@ -260,8 +260,8 @@ void VORDemod::applySettings(const VORDemodSettings& settings, bool force) // Reset state so we don't report old data for new NavId m_radial = 0.0f; - m_refMag = 0.0f; - m_varMag = 0.0f; + m_refMag = -200.0f; + m_varMag = -200.0f; m_morseIdent = ""; } if ((m_settings.m_squelch != settings.m_squelch) || force) { diff --git a/plugins/channelrx/demodvor/vordemodsink.cpp b/plugins/channelrx/demodvor/vordemodsink.cpp index 89567f524..5c05b51a8 100644 --- a/plugins/channelrx/demodvor/vordemodsink.cpp +++ b/plugins/channelrx/demodvor/vordemodsink.cpp @@ -412,6 +412,8 @@ void VORDemodSCSink::applySettings(const VORDemodSettings& settings, bool force) m_identMaxs[i] = 0.0f; } m_ident = ""; + m_refGoertzel.reset(); + m_varGoertzel.reset(); } m_settings = settings; From 233c3f961d4a24924349722a0c82d053dda5c4cb Mon Sep 17 00:00:00 2001 From: f4exb Date: Tue, 3 May 2022 04:55:35 +0200 Subject: [PATCH 105/115] v7 API: removed instance feature set end points and some swagger generated code cleanup --- .../mod802.15.4/ieee_802_15_4_mod.cpp | 1 - plugins/channeltx/modpacket/packetmod.cpp | 1 - sdrbase/resources/webapi/doc/html2/index.html | 1079 +---------------- .../resources/webapi/doc/swagger/swagger.yaml | 68 +- sdrbase/webapi/webapiadapter.cpp | 69 +- sdrbase/webapi/webapiadapter.h | 13 - sdrbase/webapi/webapiadapterinterface.cpp | 2 - sdrbase/webapi/webapiadapterinterface.h | 2 - sdrbase/webapi/webapirequestmapper.cpp | 84 -- sdrbase/webapi/webapirequestmapper.h | 3 - swagger/sdrangel/api/swagger/swagger.yaml | 68 +- swagger/sdrangel/code/html2/index.html | 1079 +---------------- .../code/qt5/client/SWGAISModActions_tx.cpp | 110 -- .../code/qt5/client/SWGAISModActions_tx.h | 59 - .../qt5/client/SWGFeaturePresetTransfer.cpp | 133 -- .../qt5/client/SWGFeaturePresetTransfer.h | 65 - .../code/qt5/client/SWGFeatureSetApi.cpp | 104 -- .../code/qt5/client/SWGFeatureSetApi.h | 10 - .../code/qt5/client/SWGFeatureSetList.cpp | 137 --- .../code/qt5/client/SWGFeatureSetList.h | 66 - .../client/SWGIEEE_802_15_4_ModActions_tx.cpp | 110 -- .../client/SWGIEEE_802_15_4_ModActions_tx.h | 59 - .../code/qt5/client/SWGInstanceApi.cpp | 52 - .../sdrangel/code/qt5/client/SWGInstanceApi.h | 6 - .../qt5/client/SWGInstanceSummaryResponse.cpp | 32 +- .../qt5/client/SWGInstanceSummaryResponse.h | 10 +- .../code/qt5/client/SWGLRPTDemodSettings.cpp | 597 --------- .../code/qt5/client/SWGLRPTDemodSettings.h | 185 --- .../code/qt5/client/SWGMapActions_find.cpp | 110 -- .../code/qt5/client/SWGMapActions_find.h | 59 - .../code/qt5/client/SWGMapItem_track.cpp | 154 --- .../code/qt5/client/SWGMapItem_track.h | 70 -- .../code/qt5/client/SWGModelFactory.h | 6 - .../qt5/client/SWGPacketModActions_tx.cpp | 185 --- .../code/qt5/client/SWGPacketModActions_tx.h | 77 -- ...GVORLocalizerSettings_vorDemodChannels.cpp | 131 -- ...SWGVORLocalizerSettings_vorDemodChannels.h | 64 - 37 files changed, 33 insertions(+), 5027 deletions(-) delete mode 100644 swagger/sdrangel/code/qt5/client/SWGAISModActions_tx.cpp delete mode 100644 swagger/sdrangel/code/qt5/client/SWGAISModActions_tx.h delete mode 100644 swagger/sdrangel/code/qt5/client/SWGFeaturePresetTransfer.cpp delete mode 100644 swagger/sdrangel/code/qt5/client/SWGFeaturePresetTransfer.h delete mode 100644 swagger/sdrangel/code/qt5/client/SWGFeatureSetList.cpp delete mode 100644 swagger/sdrangel/code/qt5/client/SWGFeatureSetList.h delete mode 100644 swagger/sdrangel/code/qt5/client/SWGIEEE_802_15_4_ModActions_tx.cpp delete mode 100644 swagger/sdrangel/code/qt5/client/SWGIEEE_802_15_4_ModActions_tx.h delete mode 100644 swagger/sdrangel/code/qt5/client/SWGLRPTDemodSettings.cpp delete mode 100644 swagger/sdrangel/code/qt5/client/SWGLRPTDemodSettings.h delete mode 100644 swagger/sdrangel/code/qt5/client/SWGMapActions_find.cpp delete mode 100644 swagger/sdrangel/code/qt5/client/SWGMapActions_find.h delete mode 100644 swagger/sdrangel/code/qt5/client/SWGMapItem_track.cpp delete mode 100644 swagger/sdrangel/code/qt5/client/SWGMapItem_track.h delete mode 100644 swagger/sdrangel/code/qt5/client/SWGPacketModActions_tx.cpp delete mode 100644 swagger/sdrangel/code/qt5/client/SWGPacketModActions_tx.h delete mode 100644 swagger/sdrangel/code/qt5/client/SWGVORLocalizerSettings_vorDemodChannels.cpp delete mode 100644 swagger/sdrangel/code/qt5/client/SWGVORLocalizerSettings_vorDemodChannels.h diff --git a/plugins/channeltx/mod802.15.4/ieee_802_15_4_mod.cpp b/plugins/channeltx/mod802.15.4/ieee_802_15_4_mod.cpp index 592e7f94e..eb18df7ef 100644 --- a/plugins/channeltx/mod802.15.4/ieee_802_15_4_mod.cpp +++ b/plugins/channeltx/mod802.15.4/ieee_802_15_4_mod.cpp @@ -31,7 +31,6 @@ #include "SWGChannelActions.h" #include "SWGIEEE_802_15_4_ModReport.h" #include "SWGIEEE_802_15_4_ModActions.h" -#include "SWGIEEE_802_15_4_ModActions_tx.h" #include #include diff --git a/plugins/channeltx/modpacket/packetmod.cpp b/plugins/channeltx/modpacket/packetmod.cpp index d3f086bba..c11add032 100644 --- a/plugins/channeltx/modpacket/packetmod.cpp +++ b/plugins/channeltx/modpacket/packetmod.cpp @@ -31,7 +31,6 @@ #include "SWGChannelActions.h" #include "SWGPacketModReport.h" #include "SWGPacketModActions.h" -#include "SWGPacketModActions_tx.h" #include #include diff --git a/sdrbase/resources/webapi/doc/html2/index.html b/sdrbase/resources/webapi/doc/html2/index.html index 51fc25d47..b58b5a96e 100644 --- a/sdrbase/resources/webapi/doc/html2/index.html +++ b/sdrbase/resources/webapi/doc/html2/index.html @@ -5370,22 +5370,6 @@ margin-bottom: 20px; } }, "description" : "Grouping of features" -}; - defs.FeatureSetList = { - "required" : [ "featuresetcount" ], - "properties" : { - "featuresetcount" : { - "type" : "integer", - "description" : "Number of feature sets opened in this instance" - }, - "featureSets" : { - "type" : "array", - "items" : { - "$ref" : "#/definitions/FeatureSet" - } - } - }, - "description" : "List of feature sets opened in this instance" }; defs.FeatureSetPreset = { "properties" : { @@ -6867,8 +6851,8 @@ margin-bottom: 20px; "devicesetlist" : { "$ref" : "#/definitions/DeviceSetList" }, - "featuresetlist" : { - "$ref" : "#/definitions/FeatureSetList" + "featureset" : { + "$ref" : "#/definitions/FeatureSet" } }, "description" : "Summarized information about this SDRangel instance" @@ -14533,12 +14517,6 @@ margin-bottom: 20px;
  • featuresetPresetPut
  • -
  • - instanceFeatureSetDelete -
  • -
  • - instanceFeatureSetPost -
  • instanceAMBEDevicesDelete @@ -14603,9 +14581,6 @@ margin-bottom: 20px;
  • instanceFeaturePresetGet
  • -
  • - instanceFeatureSetsGet -
  • instanceFeatures
  • @@ -36155,719 +36130,6 @@ $(document).ready(function() {
    -
    -
    -
    -

    instanceFeatureSetDelete

    -

    -
    -
    -
    -

    -

    Remove last feature set. The GUI version does not remove the first feature set.

    -

    -
    -
    /sdrangel/featureset
    -

    -

    Usage and SDK Samples

    -

    - - -
    -
    -
    curl -X DELETE "http://localhost/sdrangel/featureset"
    -
    -
    -
    import SWGSDRangel.*;
    -import SWGSDRangel.auth.*;
    -import SWGSDRangel.model.*;
    -import SWGSDRangel.api.FeatureSetApi;
    -
    -import java.io.File;
    -import java.util.*;
    -
    -public class FeatureSetApiExample {
    -
    -    public static void main(String[] args) {
    -        
    -        FeatureSetApi apiInstance = new FeatureSetApi();
    -        try {
    -            SuccessResponse result = apiInstance.instanceFeatureSetDelete();
    -            System.out.println(result);
    -        } catch (ApiException e) {
    -            System.err.println("Exception when calling FeatureSetApi#instanceFeatureSetDelete");
    -            e.printStackTrace();
    -        }
    -    }
    -}
    -
    - -
    -
    import SWGSDRangel.api.FeatureSetApi;
    -
    -public class FeatureSetApiExample {
    -
    -    public static void main(String[] args) {
    -        FeatureSetApi apiInstance = new FeatureSetApi();
    -        try {
    -            SuccessResponse result = apiInstance.instanceFeatureSetDelete();
    -            System.out.println(result);
    -        } catch (ApiException e) {
    -            System.err.println("Exception when calling FeatureSetApi#instanceFeatureSetDelete");
    -            e.printStackTrace();
    -        }
    -    }
    -}
    -
    - -
    -
    
    -FeatureSetApi *apiInstance = [[FeatureSetApi alloc] init];
    -
    -[apiInstance instanceFeatureSetDeleteWithCompletionHandler: 
    -              ^(SuccessResponse output, NSError* error) {
    -                            if (output) {
    -                                NSLog(@"%@", output);
    -                            }
    -                            if (error) {
    -                                NSLog(@"Error: %@", error);
    -                            }
    -                        }];
    -
    -
    - -
    -
    var SdRangel = require('sd_rangel');
    -
    -var api = new SdRangel.FeatureSetApi()
    -
    -var callback = function(error, data, response) {
    -  if (error) {
    -    console.error(error);
    -  } else {
    -    console.log('API called successfully. Returned data: ' + data);
    -  }
    -};
    -api.instanceFeatureSetDelete(callback);
    -
    -
    - - -
    -
    using System;
    -using System.Diagnostics;
    -using SWGSDRangel.Api;
    -using SWGSDRangel.Client;
    -using SWGSDRangel.Model;
    -
    -namespace Example
    -{
    -    public class instanceFeatureSetDeleteExample
    -    {
    -        public void main()
    -        {
    -            
    -            var apiInstance = new FeatureSetApi();
    -
    -            try
    -            {
    -                SuccessResponse result = apiInstance.instanceFeatureSetDelete();
    -                Debug.WriteLine(result);
    -            }
    -            catch (Exception e)
    -            {
    -                Debug.Print("Exception when calling FeatureSetApi.instanceFeatureSetDelete: " + e.Message );
    -            }
    -        }
    -    }
    -}
    -
    -
    - -
    -
    <?php
    -require_once(__DIR__ . '/vendor/autoload.php');
    -
    -$api_instance = new Swagger\Client\Api\FeatureSetApi();
    -
    -try {
    -    $result = $api_instance->instanceFeatureSetDelete();
    -    print_r($result);
    -} catch (Exception $e) {
    -    echo 'Exception when calling FeatureSetApi->instanceFeatureSetDelete: ', $e->getMessage(), PHP_EOL;
    -}
    -?>
    -
    - -
    -
    use Data::Dumper;
    -use SWGSDRangel::Configuration;
    -use SWGSDRangel::FeatureSetApi;
    -
    -my $api_instance = SWGSDRangel::FeatureSetApi->new();
    -
    -eval { 
    -    my $result = $api_instance->instanceFeatureSetDelete();
    -    print Dumper($result);
    -};
    -if ($@) {
    -    warn "Exception when calling FeatureSetApi->instanceFeatureSetDelete: $@\n";
    -}
    -
    - -
    -
    from __future__ import print_statement
    -import time
    -import swagger_sdrangel
    -from swagger_sdrangel.rest import ApiException
    -from pprint import pprint
    -
    -# create an instance of the API class
    -api_instance = swagger_sdrangel.FeatureSetApi()
    -
    -try: 
    -    api_response = api_instance.instance_feature_set_delete()
    -    pprint(api_response)
    -except ApiException as e:
    -    print("Exception when calling FeatureSetApi->instanceFeatureSetDelete: %s\n" % e)
    -
    -
    - -

    Parameters

    - - - - - - -

    Responses

    -

    Status: 202 - Message to remove last feature set was sent successfully

    - - - -
    -
    -
    - -
    - -
    -
    - -

    Status: 404 - No more feature sets to be deleted

    - - - -
    -
    -
    - -
    - -
    -
    - -

    Status: 500 - Error

    - - - -
    -
    -
    - -
    - -
    -
    - -

    Status: 501 - Function not implemented

    - - - -
    -
    -
    - -
    - -
    -
    - -
    -
    -
    -
    -
    -
    -

    instanceFeatureSetPost

    -

    -
    -
    -
    -

    -

    Add (append) a new feature set

    -

    -
    -
    /sdrangel/featureset
    -

    -

    Usage and SDK Samples

    -

    - - -
    -
    -
    curl -X POST "http://localhost/sdrangel/featureset"
    -
    -
    -
    import SWGSDRangel.*;
    -import SWGSDRangel.auth.*;
    -import SWGSDRangel.model.*;
    -import SWGSDRangel.api.FeatureSetApi;
    -
    -import java.io.File;
    -import java.util.*;
    -
    -public class FeatureSetApiExample {
    -
    -    public static void main(String[] args) {
    -        
    -        FeatureSetApi apiInstance = new FeatureSetApi();
    -        try {
    -            SuccessResponse result = apiInstance.instanceFeatureSetPost();
    -            System.out.println(result);
    -        } catch (ApiException e) {
    -            System.err.println("Exception when calling FeatureSetApi#instanceFeatureSetPost");
    -            e.printStackTrace();
    -        }
    -    }
    -}
    -
    - -
    -
    import SWGSDRangel.api.FeatureSetApi;
    -
    -public class FeatureSetApiExample {
    -
    -    public static void main(String[] args) {
    -        FeatureSetApi apiInstance = new FeatureSetApi();
    -        try {
    -            SuccessResponse result = apiInstance.instanceFeatureSetPost();
    -            System.out.println(result);
    -        } catch (ApiException e) {
    -            System.err.println("Exception when calling FeatureSetApi#instanceFeatureSetPost");
    -            e.printStackTrace();
    -        }
    -    }
    -}
    -
    - -
    -
    
    -FeatureSetApi *apiInstance = [[FeatureSetApi alloc] init];
    -
    -[apiInstance instanceFeatureSetPostWithCompletionHandler: 
    -              ^(SuccessResponse output, NSError* error) {
    -                            if (output) {
    -                                NSLog(@"%@", output);
    -                            }
    -                            if (error) {
    -                                NSLog(@"Error: %@", error);
    -                            }
    -                        }];
    -
    -
    - -
    -
    var SdRangel = require('sd_rangel');
    -
    -var api = new SdRangel.FeatureSetApi()
    -
    -var callback = function(error, data, response) {
    -  if (error) {
    -    console.error(error);
    -  } else {
    -    console.log('API called successfully. Returned data: ' + data);
    -  }
    -};
    -api.instanceFeatureSetPost(callback);
    -
    -
    - - -
    -
    using System;
    -using System.Diagnostics;
    -using SWGSDRangel.Api;
    -using SWGSDRangel.Client;
    -using SWGSDRangel.Model;
    -
    -namespace Example
    -{
    -    public class instanceFeatureSetPostExample
    -    {
    -        public void main()
    -        {
    -            
    -            var apiInstance = new FeatureSetApi();
    -
    -            try
    -            {
    -                SuccessResponse result = apiInstance.instanceFeatureSetPost();
    -                Debug.WriteLine(result);
    -            }
    -            catch (Exception e)
    -            {
    -                Debug.Print("Exception when calling FeatureSetApi.instanceFeatureSetPost: " + e.Message );
    -            }
    -        }
    -    }
    -}
    -
    -
    - -
    -
    <?php
    -require_once(__DIR__ . '/vendor/autoload.php');
    -
    -$api_instance = new Swagger\Client\Api\FeatureSetApi();
    -
    -try {
    -    $result = $api_instance->instanceFeatureSetPost();
    -    print_r($result);
    -} catch (Exception $e) {
    -    echo 'Exception when calling FeatureSetApi->instanceFeatureSetPost: ', $e->getMessage(), PHP_EOL;
    -}
    -?>
    -
    - -
    -
    use Data::Dumper;
    -use SWGSDRangel::Configuration;
    -use SWGSDRangel::FeatureSetApi;
    -
    -my $api_instance = SWGSDRangel::FeatureSetApi->new();
    -
    -eval { 
    -    my $result = $api_instance->instanceFeatureSetPost();
    -    print Dumper($result);
    -};
    -if ($@) {
    -    warn "Exception when calling FeatureSetApi->instanceFeatureSetPost: $@\n";
    -}
    -
    - -
    -
    from __future__ import print_statement
    -import time
    -import swagger_sdrangel
    -from swagger_sdrangel.rest import ApiException
    -from pprint import pprint
    -
    -# create an instance of the API class
    -api_instance = swagger_sdrangel.FeatureSetApi()
    -
    -try: 
    -    api_response = api_instance.instance_feature_set_post()
    -    pprint(api_response)
    -except ApiException as e:
    -    print("Exception when calling FeatureSetApi->instanceFeatureSetPost: %s\n" % e)
    -
    -
    - -

    Parameters

    - - - - - - -

    Responses

    -

    Status: 202 - Message to add a new feature set was sent successfully

    - - - -
    -
    -
    - -
    - -
    -
    - -

    Status: 500 - Error

    - - - -
    -
    -
    - -
    - -
    -
    - -

    Status: 501 - Function not implemented

    - - - -
    -
    -
    - -
    - -
    -
    - -
    -
    -

    Instance

    @@ -44751,341 +44013,6 @@ except ApiException as e:
    -
    -
    -
    -

    instanceFeatureSetsGet

    -

    -
    -
    -
    -

    -

    Get summary information about feature sets opened in the instance

    -

    -
    -
    /sdrangel/featuresets
    -

    -

    Usage and SDK Samples

    -

    - - -
    -
    -
    curl -X GET "http://localhost/sdrangel/featuresets"
    -
    -
    -
    import SWGSDRangel.*;
    -import SWGSDRangel.auth.*;
    -import SWGSDRangel.model.*;
    -import SWGSDRangel.api.InstanceApi;
    -
    -import java.io.File;
    -import java.util.*;
    -
    -public class InstanceApiExample {
    -
    -    public static void main(String[] args) {
    -        
    -        InstanceApi apiInstance = new InstanceApi();
    -        try {
    -            FeatureSetList result = apiInstance.instanceFeatureSetsGet();
    -            System.out.println(result);
    -        } catch (ApiException e) {
    -            System.err.println("Exception when calling InstanceApi#instanceFeatureSetsGet");
    -            e.printStackTrace();
    -        }
    -    }
    -}
    -
    - -
    -
    import SWGSDRangel.api.InstanceApi;
    -
    -public class InstanceApiExample {
    -
    -    public static void main(String[] args) {
    -        InstanceApi apiInstance = new InstanceApi();
    -        try {
    -            FeatureSetList result = apiInstance.instanceFeatureSetsGet();
    -            System.out.println(result);
    -        } catch (ApiException e) {
    -            System.err.println("Exception when calling InstanceApi#instanceFeatureSetsGet");
    -            e.printStackTrace();
    -        }
    -    }
    -}
    -
    - -
    -
    
    -InstanceApi *apiInstance = [[InstanceApi alloc] init];
    -
    -[apiInstance instanceFeatureSetsGetWithCompletionHandler: 
    -              ^(FeatureSetList output, NSError* error) {
    -                            if (output) {
    -                                NSLog(@"%@", output);
    -                            }
    -                            if (error) {
    -                                NSLog(@"Error: %@", error);
    -                            }
    -                        }];
    -
    -
    - -
    -
    var SdRangel = require('sd_rangel');
    -
    -var api = new SdRangel.InstanceApi()
    -
    -var callback = function(error, data, response) {
    -  if (error) {
    -    console.error(error);
    -  } else {
    -    console.log('API called successfully. Returned data: ' + data);
    -  }
    -};
    -api.instanceFeatureSetsGet(callback);
    -
    -
    - - -
    -
    using System;
    -using System.Diagnostics;
    -using SWGSDRangel.Api;
    -using SWGSDRangel.Client;
    -using SWGSDRangel.Model;
    -
    -namespace Example
    -{
    -    public class instanceFeatureSetsGetExample
    -    {
    -        public void main()
    -        {
    -            
    -            var apiInstance = new InstanceApi();
    -
    -            try
    -            {
    -                FeatureSetList result = apiInstance.instanceFeatureSetsGet();
    -                Debug.WriteLine(result);
    -            }
    -            catch (Exception e)
    -            {
    -                Debug.Print("Exception when calling InstanceApi.instanceFeatureSetsGet: " + e.Message );
    -            }
    -        }
    -    }
    -}
    -
    -
    - -
    -
    <?php
    -require_once(__DIR__ . '/vendor/autoload.php');
    -
    -$api_instance = new Swagger\Client\Api\InstanceApi();
    -
    -try {
    -    $result = $api_instance->instanceFeatureSetsGet();
    -    print_r($result);
    -} catch (Exception $e) {
    -    echo 'Exception when calling InstanceApi->instanceFeatureSetsGet: ', $e->getMessage(), PHP_EOL;
    -}
    -?>
    -
    - -
    -
    use Data::Dumper;
    -use SWGSDRangel::Configuration;
    -use SWGSDRangel::InstanceApi;
    -
    -my $api_instance = SWGSDRangel::InstanceApi->new();
    -
    -eval { 
    -    my $result = $api_instance->instanceFeatureSetsGet();
    -    print Dumper($result);
    -};
    -if ($@) {
    -    warn "Exception when calling InstanceApi->instanceFeatureSetsGet: $@\n";
    -}
    -
    - -
    -
    from __future__ import print_statement
    -import time
    -import swagger_sdrangel
    -from swagger_sdrangel.rest import ApiException
    -from pprint import pprint
    -
    -# create an instance of the API class
    -api_instance = swagger_sdrangel.InstanceApi()
    -
    -try: 
    -    api_response = api_instance.instance_feature_sets_get()
    -    pprint(api_response)
    -except ApiException as e:
    -    print("Exception when calling InstanceApi->instanceFeatureSetsGet: %s\n" % e)
    -
    -
    - -

    Parameters

    - - - - - - -

    Responses

    -

    Status: 200 - On success return feature set list

    - - - -
    -
    -
    - -
    - -
    -
    - -

    Status: 500 - Error

    - - - -
    -
    -
    - -
    - -
    -
    - -

    Status: 501 - Function not implemented

    - - - -
    -
    -
    - -
    - -
    -
    - -
    -
    -
    @@ -52469,7 +51396,7 @@ except ApiException as e:
    - Generated 2022-05-01T22:14:53.451+02:00 + Generated 2022-05-03T04:22:59.079+02:00
    diff --git a/sdrbase/resources/webapi/doc/swagger/swagger.yaml b/sdrbase/resources/webapi/doc/swagger/swagger.yaml index 610404a6f..28a7afef6 100644 --- a/sdrbase/resources/webapi/doc/swagger/swagger.yaml +++ b/sdrbase/resources/webapi/doc/swagger/swagger.yaml @@ -951,23 +951,6 @@ paths: "501": $ref: "#/responses/Response_501" - /sdrangel/featuresets: - x-swagger-router-controller: instance - get: - description: Get summary information about feature sets opened in the instance - operationId: instanceFeatureSetsGet - tags: - - Instance - responses: - "200": - description: On success return feature set list - schema: - $ref: "#/definitions/FeatureSetList" - "500": - $ref: "#/responses/Response_500" - "501": - $ref: "#/responses/Response_501" - /sdrangel/deviceset: x-swagger-router-controller: instance post: @@ -1941,41 +1924,6 @@ paths: "501": $ref: "#/responses/Response_501" - /sdrangel/featureset: - x-swagger-router-controller: instance - post: - description: Add (append) a new feature set - operationId: instanceFeatureSetPost - tags: - - FeatureSet - responses: - "202": - description: Message to add a new feature set was sent successfully - schema: - $ref: "#/definitions/SuccessResponse" - "500": - $ref: "#/responses/Response_500" - "501": - $ref: "#/responses/Response_501" - delete: - description: Remove last feature set. The GUI version does not remove the first feature set. - operationId: instanceFeatureSetDelete - tags: - - FeatureSet - responses: - "202": - description: Message to remove last feature set was sent successfully - schema: - $ref: "#/definitions/SuccessResponse" - "404": - description: "No more feature sets to be deleted" - schema: - $ref: "#/definitions/ErrorResponse" - "500": - $ref: "#/responses/Response_500" - "501": - $ref: "#/responses/Response_501" - /sdrangel/featureset/{featureSetIndex}: x-swagger-router-controller: featureset get: @@ -2532,8 +2480,8 @@ definitions: $ref: "#/definitions/LoggingInfo" devicesetlist: $ref: "#/definitions/DeviceSetList" - featuresetlist: - $ref: "#/definitions/FeatureSetList" + featureset: + $ref: "#/definitions/FeatureSet" InstanceConfigResponse: description: Preferences, Presets and Commands of the MainSettings saved by Qt @@ -2773,18 +2721,6 @@ definitions: type: array items: $ref: "#/definitions/Feature" - FeatureSetList: - description: "List of feature sets opened in this instance" - required: - - featuresetcount - properties: - featuresetcount: - description: "Number of feature sets opened in this instance" - type: integer - featureSets: - type: array - items: - $ref: "#/definitions/FeatureSet" SpectrumServer: description: "Spectrum server state" diff --git a/sdrbase/webapi/webapiadapter.cpp b/sdrbase/webapi/webapiadapter.cpp index c3266425f..133f9d386 100644 --- a/sdrbase/webapi/webapiadapter.cpp +++ b/sdrbase/webapi/webapiadapter.cpp @@ -75,8 +75,6 @@ #include "SWGFeaturePresetGroup.h" #include "SWGFeaturePresetItem.h" #include "SWGFeaturePresetIdentifier.h" -#include "SWGFeaturePresetTransfer.h" -#include "SWGFeatureSetList.h" #include "SWGFeatureSettings.h" #include "SWGFeatureReport.h" #include "SWGFeatureActions.h" @@ -127,8 +125,8 @@ int WebAPIAdapter::instanceSummary( SWGSDRangel::SWGDeviceSetList *deviceSetList = response.getDevicesetlist(); getDeviceSetList(deviceSetList); - SWGSDRangel::SWGFeatureSetList *featureSetList = response.getFeaturesetlist(); - getFeatureSetList(featureSetList); + SWGSDRangel::SWGFeatureSet *featureSet = response.getFeatureset(); + getFeatureSet(featureSet, m_mainCore->m_featureSets.back(), 0); return 200; } @@ -1489,15 +1487,6 @@ int WebAPIAdapter::instanceDeviceSetsGet( return 200; } -int WebAPIAdapter::instanceFeatureSetsGet( - SWGSDRangel::SWGFeatureSetList& response, - SWGSDRangel::SWGErrorResponse& error) -{ - (void) error; - getFeatureSetList(&response); - return 200; -} - int WebAPIAdapter::instanceDeviceSetPost( int direction, SWGSDRangel::SWGSuccessResponse& response, @@ -1538,45 +1527,6 @@ int WebAPIAdapter::instanceDeviceSetDelete( } } -int WebAPIAdapter::instanceFeatureSetPost( - SWGSDRangel::SWGSuccessResponse& response, - SWGSDRangel::SWGErrorResponse& error) -{ - (void) error; - MainCore::MsgAddFeatureSet *msg = MainCore::MsgAddFeatureSet::create(); - m_mainCore->m_mainMessageQueue->push(msg); - - response.init(); - *response.getMessage() = QString("Message to add a new feature set (MsgAddFeatureSet) was submitted successfully"); - - return 202; -} - -int WebAPIAdapter::instanceFeatureSetDelete( - SWGSDRangel::SWGSuccessResponse& response, - SWGSDRangel::SWGErrorResponse& error) -{ - unsigned int minFeatureSets = QCoreApplication::applicationName() == "SDRangelSrv" ? 0 : 1; - - if (m_mainCore->m_featureSets.size() > minFeatureSets) - { - MainCore::MsgRemoveLastFeatureSet *msg = MainCore::MsgRemoveLastFeatureSet::create(); - m_mainCore->m_mainMessageQueue->push(msg); - - response.init(); - *response.getMessage() = QString("Message to remove last feature set (MsgRemoveLastFeatureSet) was submitted successfully"); - - return 202; - } - else - { - error.init(); - *error.getMessage() = "No more feature sets to be removed"; - - return 404; - } -} - int WebAPIAdapter::devicesetGet( int deviceSetIndex, SWGSDRangel::SWGDeviceSet& response, @@ -3746,21 +3696,6 @@ int WebAPIAdapter::featuresetGet( } } -void WebAPIAdapter::getFeatureSetList(SWGSDRangel::SWGFeatureSetList* featureSetList) -{ - featureSetList->init(); - featureSetList->setFeaturesetcount((int) m_mainCore->m_featureSets.size()); - - std::vector::const_iterator it = m_mainCore->m_featureSets.begin(); - - for (int i = 0; it != m_mainCore->m_featureSets.end(); ++it, i++) - { - QList *featureSets = featureSetList->getFeatureSets(); - featureSets->append(new SWGSDRangel::SWGFeatureSet()); - getFeatureSet(featureSets->back(), *it, i); - } -} - void WebAPIAdapter::getFeatureSet(SWGSDRangel::SWGFeatureSet *swgFeatureSet, const FeatureSet* featureSet, int featureSetIndex) { swgFeatureSet->init(); diff --git a/sdrbase/webapi/webapiadapter.h b/sdrbase/webapi/webapiadapter.h index 0a80e437f..5dbfdb511 100644 --- a/sdrbase/webapi/webapiadapter.h +++ b/sdrbase/webapi/webapiadapter.h @@ -207,10 +207,6 @@ public: SWGSDRangel::SWGDeviceSetList& response, SWGSDRangel::SWGErrorResponse& error); - virtual int instanceFeatureSetsGet( - SWGSDRangel::SWGFeatureSetList& response, - SWGSDRangel::SWGErrorResponse& error); - virtual int instanceDeviceSetPost( int direction, SWGSDRangel::SWGSuccessResponse& response, @@ -220,14 +216,6 @@ public: SWGSDRangel::SWGSuccessResponse& response, SWGSDRangel::SWGErrorResponse& error); - virtual int instanceFeatureSetPost( - SWGSDRangel::SWGSuccessResponse& response, - SWGSDRangel::SWGErrorResponse& error); - - virtual int instanceFeatureSetDelete( - SWGSDRangel::SWGSuccessResponse& response, - SWGSDRangel::SWGErrorResponse& error); - virtual int devicesetGet( int deviceSetIndex, SWGSDRangel::SWGDeviceSet& response, @@ -457,7 +445,6 @@ private: void getDeviceSetList(SWGSDRangel::SWGDeviceSetList* deviceSetList); void getDeviceSet(SWGSDRangel::SWGDeviceSet *swgDeviceSet, const DeviceSet* deviceSet, int deviceSetIndex); void getChannelsDetail(SWGSDRangel::SWGChannelsDetail *channelsDetail, const DeviceSet* deviceSet); - void getFeatureSetList(SWGSDRangel::SWGFeatureSetList* featureSetList); void getFeatureSet(SWGSDRangel::SWGFeatureSet *swgFeatureSet, const FeatureSet* featureSet, int featureSetIndex); static QtMsgType getMsgTypeFromString(const QString& msgTypeString); static void getMsgTypeString(const QtMsgType& msgType, QString& level); diff --git a/sdrbase/webapi/webapiadapterinterface.cpp b/sdrbase/webapi/webapiadapterinterface.cpp index 2b6d30743..a62cce066 100644 --- a/sdrbase/webapi/webapiadapterinterface.cpp +++ b/sdrbase/webapi/webapiadapterinterface.cpp @@ -44,8 +44,6 @@ QString WebAPIAdapterInterface::instanceFeaturePresetsURL = "/sdrangel/featurepr QString WebAPIAdapterInterface::instanceFeaturePresetURL = "/sdrangel/featurepreset"; QString WebAPIAdapterInterface::instanceDeviceSetsURL = "/sdrangel/devicesets"; QString WebAPIAdapterInterface::instanceDeviceSetURL = "/sdrangel/deviceset"; -QString WebAPIAdapterInterface::instanceFeatureSetsURL = "/sdrangel/featuresets"; -QString WebAPIAdapterInterface::instanceFeatureSetURL = "/sdrangel/featureset"; std::regex WebAPIAdapterInterface::devicesetURLRe("^/sdrangel/deviceset/([0-9]{1,2})$"); std::regex WebAPIAdapterInterface::devicesetFocusURLRe("^/sdrangel/deviceset/([0-9]{1,2})/focus$"); diff --git a/sdrbase/webapi/webapiadapterinterface.h b/sdrbase/webapi/webapiadapterinterface.h index 88cd9d121..28807661e 100644 --- a/sdrbase/webapi/webapiadapterinterface.h +++ b/sdrbase/webapi/webapiadapterinterface.h @@ -1472,8 +1472,6 @@ public: static QString instanceFeaturePresetURL; static QString instanceDeviceSetsURL; static QString instanceDeviceSetURL; - static QString instanceFeatureSetsURL; - static QString instanceFeatureSetURL; static std::regex devicesetURLRe; static std::regex devicesetFocusURLRe; static std::regex devicesetSpectrumSettingsURLRe; diff --git a/sdrbase/webapi/webapirequestmapper.cpp b/sdrbase/webapi/webapirequestmapper.cpp index ad7abb888..13f49a939 100644 --- a/sdrbase/webapi/webapirequestmapper.cpp +++ b/sdrbase/webapi/webapirequestmapper.cpp @@ -55,8 +55,6 @@ #include "SWGErrorResponse.h" #include "SWGFeaturePresets.h" #include "SWGFeaturePresetIdentifier.h" -#include "SWGFeaturePresetTransfer.h" -#include "SWGFeatureSetList.h" #include "SWGFeatureSettings.h" #include "SWGFeatureReport.h" #include "SWGFeatureActions.h" @@ -161,10 +159,6 @@ void WebAPIRequestMapper::service(qtwebapp::HttpRequest& request, qtwebapp::Http instanceDeviceSetsService(request, response); } else if (path == WebAPIAdapterInterface::instanceDeviceSetURL) { instanceDeviceSetService(request, response); - } else if (path == WebAPIAdapterInterface::instanceFeatureSetsURL) { - instanceFeatureSetsService(request, response); - } else if (path == WebAPIAdapterInterface::instanceFeatureSetURL) { - instanceFeatureSetService(request, response); } else { @@ -1475,33 +1469,6 @@ void WebAPIRequestMapper::instanceDeviceSetsService(qtwebapp::HttpRequest& reque } } -void WebAPIRequestMapper::instanceFeatureSetsService(qtwebapp::HttpRequest& request, qtwebapp::HttpResponse& response) -{ - SWGSDRangel::SWGErrorResponse errorResponse; - response.setHeader("Content-Type", "application/json"); - response.setHeader("Access-Control-Allow-Origin", "*"); - - if (request.getMethod() == "GET") - { - SWGSDRangel::SWGFeatureSetList normalResponse; - int status = m_adapter->instanceFeatureSetsGet(normalResponse, errorResponse); - response.setStatus(status); - - if (status/100 == 2) { - response.write(normalResponse.asJson().toUtf8()); - } else { - response.write(errorResponse.asJson().toUtf8()); - } - } - else - { - response.setStatus(405,"Invalid HTTP method"); - errorResponse.init(); - *errorResponse.getMessage() = "Invalid HTTP method"; - response.write(errorResponse.asJson().toUtf8()); - } -} - void WebAPIRequestMapper::instanceDeviceSetService(qtwebapp::HttpRequest& request, qtwebapp::HttpResponse& response) { SWGSDRangel::SWGErrorResponse errorResponse; @@ -1553,46 +1520,6 @@ void WebAPIRequestMapper::instanceDeviceSetService(qtwebapp::HttpRequest& reques } } -void WebAPIRequestMapper::instanceFeatureSetService(qtwebapp::HttpRequest& request, qtwebapp::HttpResponse& response) -{ - SWGSDRangel::SWGErrorResponse errorResponse; - response.setHeader("Content-Type", "application/json"); - response.setHeader("Access-Control-Allow-Origin", "*"); - - if (request.getMethod() == "POST") - { - SWGSDRangel::SWGSuccessResponse normalResponse; - - int status = m_adapter->instanceFeatureSetPost(normalResponse, errorResponse); - response.setStatus(status); - - if (status/100 == 2) { - response.write(normalResponse.asJson().toUtf8()); - } else { - response.write(errorResponse.asJson().toUtf8()); - } - } - else if (request.getMethod() == "DELETE") - { - SWGSDRangel::SWGSuccessResponse normalResponse; - int status = m_adapter->instanceFeatureSetDelete(normalResponse, errorResponse); - response.setStatus(status); - - if (status/100 == 2) { - response.write(normalResponse.asJson().toUtf8()); - } else { - response.write(errorResponse.asJson().toUtf8()); - } - } - else - { - response.setStatus(405,"Invalid HTTP method"); - errorResponse.init(); - *errorResponse.getMessage() = "Invalid HTTP method"; - response.write(errorResponse.asJson().toUtf8()); - } -} - void WebAPIRequestMapper::devicesetService(const std::string& indexStr, qtwebapp::HttpRequest& request, qtwebapp::HttpResponse& response) { SWGSDRangel::SWGErrorResponse errorResponse; @@ -3216,17 +3143,6 @@ bool WebAPIRequestMapper::validatePresetIdentifer(SWGSDRangel::SWGPresetIdentifi return (presetIdentifier.getGroupName() && presetIdentifier.getName() && presetIdentifier.getType()); } -bool WebAPIRequestMapper::validateFeaturePresetTransfer(SWGSDRangel::SWGFeaturePresetTransfer& presetTransfer) -{ - SWGSDRangel::SWGFeaturePresetIdentifier *presetIdentifier = presetTransfer.getPreset(); - - if (presetIdentifier == 0) { - return false; - } - - return validateFeaturePresetIdentifer(*presetIdentifier); -} - bool WebAPIRequestMapper::validateFeaturePresetIdentifer(SWGSDRangel::SWGFeaturePresetIdentifier& presetIdentifier) { return (presetIdentifier.getGroupName() && presetIdentifier.getDescription()); diff --git a/sdrbase/webapi/webapirequestmapper.h b/sdrbase/webapi/webapirequestmapper.h index babd977a8..454472d06 100644 --- a/sdrbase/webapi/webapirequestmapper.h +++ b/sdrbase/webapi/webapirequestmapper.h @@ -80,8 +80,6 @@ private: void instanceFeaturePresetService(qtwebapp::HttpRequest& request, qtwebapp::HttpResponse& response); void instanceDeviceSetsService(qtwebapp::HttpRequest& request, qtwebapp::HttpResponse& response); void instanceDeviceSetService(qtwebapp::HttpRequest& request, qtwebapp::HttpResponse& response); - void instanceFeatureSetsService(qtwebapp::HttpRequest& request, qtwebapp::HttpResponse& response); - void instanceFeatureSetService(qtwebapp::HttpRequest& request, qtwebapp::HttpResponse& response); void devicesetService(const std::string& indexStr, qtwebapp::HttpRequest& request, qtwebapp::HttpResponse& response); void devicesetFocusService(const std::string& indexStr, qtwebapp::HttpRequest& request, qtwebapp::HttpResponse& response); @@ -118,7 +116,6 @@ private: bool validateDeviceActions(SWGSDRangel::SWGDeviceActions& deviceActions, QJsonObject& jsonObject, QStringList& deviceActionsKeys); bool validateChannelSettings(SWGSDRangel::SWGChannelSettings& channelSettings, QJsonObject& jsonObject, QStringList& channelSettingsKeys); bool validateChannelActions(SWGSDRangel::SWGChannelActions& channelActions, QJsonObject& jsonObject, QStringList& channelActionsKeys); - bool validateFeaturePresetTransfer(SWGSDRangel::SWGFeaturePresetTransfer& presetTransfer); bool validateFeaturePresetIdentifer(SWGSDRangel::SWGFeaturePresetIdentifier& presetIdentifier); bool validateFeatureSettings(SWGSDRangel::SWGFeatureSettings& featureSettings, QJsonObject& jsonObject, QStringList& featureSettingsKeys); bool validateFeatureActions(SWGSDRangel::SWGFeatureActions& featureActions, QJsonObject& jsonObject, QStringList& featureActionsKeys); diff --git a/swagger/sdrangel/api/swagger/swagger.yaml b/swagger/sdrangel/api/swagger/swagger.yaml index 06c590fee..2b9b5087d 100644 --- a/swagger/sdrangel/api/swagger/swagger.yaml +++ b/swagger/sdrangel/api/swagger/swagger.yaml @@ -951,23 +951,6 @@ paths: "501": $ref: "#/responses/Response_501" - /sdrangel/featuresets: - x-swagger-router-controller: instance - get: - description: Get summary information about feature sets opened in the instance - operationId: instanceFeatureSetsGet - tags: - - Instance - responses: - "200": - description: On success return feature set list - schema: - $ref: "#/definitions/FeatureSetList" - "500": - $ref: "#/responses/Response_500" - "501": - $ref: "#/responses/Response_501" - /sdrangel/deviceset: x-swagger-router-controller: instance post: @@ -1941,41 +1924,6 @@ paths: "501": $ref: "#/responses/Response_501" - /sdrangel/featureset: - x-swagger-router-controller: instance - post: - description: Add (append) a new feature set - operationId: instanceFeatureSetPost - tags: - - FeatureSet - responses: - "202": - description: Message to add a new feature set was sent successfully - schema: - $ref: "#/definitions/SuccessResponse" - "500": - $ref: "#/responses/Response_500" - "501": - $ref: "#/responses/Response_501" - delete: - description: Remove last feature set. The GUI version does not remove the first feature set. - operationId: instanceFeatureSetDelete - tags: - - FeatureSet - responses: - "202": - description: Message to remove last feature set was sent successfully - schema: - $ref: "#/definitions/SuccessResponse" - "404": - description: "No more feature sets to be deleted" - schema: - $ref: "#/definitions/ErrorResponse" - "500": - $ref: "#/responses/Response_500" - "501": - $ref: "#/responses/Response_501" - /sdrangel/featureset/{featureSetIndex}: x-swagger-router-controller: featureset get: @@ -2532,8 +2480,8 @@ definitions: $ref: "#/definitions/LoggingInfo" devicesetlist: $ref: "#/definitions/DeviceSetList" - featuresetlist: - $ref: "#/definitions/FeatureSetList" + featureset: + $ref: "#/definitions/FeatureSet" InstanceConfigResponse: description: Preferences, Presets and Commands of the MainSettings saved by Qt @@ -2773,18 +2721,6 @@ definitions: type: array items: $ref: "#/definitions/Feature" - FeatureSetList: - description: "List of feature sets opened in this instance" - required: - - featuresetcount - properties: - featuresetcount: - description: "Number of feature sets opened in this instance" - type: integer - featureSets: - type: array - items: - $ref: "#/definitions/FeatureSet" SpectrumServer: description: "Spectrum server state" diff --git a/swagger/sdrangel/code/html2/index.html b/swagger/sdrangel/code/html2/index.html index 51fc25d47..b58b5a96e 100644 --- a/swagger/sdrangel/code/html2/index.html +++ b/swagger/sdrangel/code/html2/index.html @@ -5370,22 +5370,6 @@ margin-bottom: 20px; } }, "description" : "Grouping of features" -}; - defs.FeatureSetList = { - "required" : [ "featuresetcount" ], - "properties" : { - "featuresetcount" : { - "type" : "integer", - "description" : "Number of feature sets opened in this instance" - }, - "featureSets" : { - "type" : "array", - "items" : { - "$ref" : "#/definitions/FeatureSet" - } - } - }, - "description" : "List of feature sets opened in this instance" }; defs.FeatureSetPreset = { "properties" : { @@ -6867,8 +6851,8 @@ margin-bottom: 20px; "devicesetlist" : { "$ref" : "#/definitions/DeviceSetList" }, - "featuresetlist" : { - "$ref" : "#/definitions/FeatureSetList" + "featureset" : { + "$ref" : "#/definitions/FeatureSet" } }, "description" : "Summarized information about this SDRangel instance" @@ -14533,12 +14517,6 @@ margin-bottom: 20px;
  • featuresetPresetPut
  • -
  • - instanceFeatureSetDelete -
  • -
  • - instanceFeatureSetPost -
  • instanceAMBEDevicesDelete @@ -14603,9 +14581,6 @@ margin-bottom: 20px;
  • instanceFeaturePresetGet
  • -
  • - instanceFeatureSetsGet -
  • instanceFeatures
  • @@ -36155,719 +36130,6 @@ $(document).ready(function() {
    -
    -
    -
    -

    instanceFeatureSetDelete

    -

    -
    -
    -
    -

    -

    Remove last feature set. The GUI version does not remove the first feature set.

    -

    -
    -
    /sdrangel/featureset
    -

    -

    Usage and SDK Samples

    -

    - - -
    -
    -
    curl -X DELETE "http://localhost/sdrangel/featureset"
    -
    -
    -
    import SWGSDRangel.*;
    -import SWGSDRangel.auth.*;
    -import SWGSDRangel.model.*;
    -import SWGSDRangel.api.FeatureSetApi;
    -
    -import java.io.File;
    -import java.util.*;
    -
    -public class FeatureSetApiExample {
    -
    -    public static void main(String[] args) {
    -        
    -        FeatureSetApi apiInstance = new FeatureSetApi();
    -        try {
    -            SuccessResponse result = apiInstance.instanceFeatureSetDelete();
    -            System.out.println(result);
    -        } catch (ApiException e) {
    -            System.err.println("Exception when calling FeatureSetApi#instanceFeatureSetDelete");
    -            e.printStackTrace();
    -        }
    -    }
    -}
    -
    - -
    -
    import SWGSDRangel.api.FeatureSetApi;
    -
    -public class FeatureSetApiExample {
    -
    -    public static void main(String[] args) {
    -        FeatureSetApi apiInstance = new FeatureSetApi();
    -        try {
    -            SuccessResponse result = apiInstance.instanceFeatureSetDelete();
    -            System.out.println(result);
    -        } catch (ApiException e) {
    -            System.err.println("Exception when calling FeatureSetApi#instanceFeatureSetDelete");
    -            e.printStackTrace();
    -        }
    -    }
    -}
    -
    - -
    -
    
    -FeatureSetApi *apiInstance = [[FeatureSetApi alloc] init];
    -
    -[apiInstance instanceFeatureSetDeleteWithCompletionHandler: 
    -              ^(SuccessResponse output, NSError* error) {
    -                            if (output) {
    -                                NSLog(@"%@", output);
    -                            }
    -                            if (error) {
    -                                NSLog(@"Error: %@", error);
    -                            }
    -                        }];
    -
    -
    - -
    -
    var SdRangel = require('sd_rangel');
    -
    -var api = new SdRangel.FeatureSetApi()
    -
    -var callback = function(error, data, response) {
    -  if (error) {
    -    console.error(error);
    -  } else {
    -    console.log('API called successfully. Returned data: ' + data);
    -  }
    -};
    -api.instanceFeatureSetDelete(callback);
    -
    -
    - - -
    -
    using System;
    -using System.Diagnostics;
    -using SWGSDRangel.Api;
    -using SWGSDRangel.Client;
    -using SWGSDRangel.Model;
    -
    -namespace Example
    -{
    -    public class instanceFeatureSetDeleteExample
    -    {
    -        public void main()
    -        {
    -            
    -            var apiInstance = new FeatureSetApi();
    -
    -            try
    -            {
    -                SuccessResponse result = apiInstance.instanceFeatureSetDelete();
    -                Debug.WriteLine(result);
    -            }
    -            catch (Exception e)
    -            {
    -                Debug.Print("Exception when calling FeatureSetApi.instanceFeatureSetDelete: " + e.Message );
    -            }
    -        }
    -    }
    -}
    -
    -
    - -
    -
    <?php
    -require_once(__DIR__ . '/vendor/autoload.php');
    -
    -$api_instance = new Swagger\Client\Api\FeatureSetApi();
    -
    -try {
    -    $result = $api_instance->instanceFeatureSetDelete();
    -    print_r($result);
    -} catch (Exception $e) {
    -    echo 'Exception when calling FeatureSetApi->instanceFeatureSetDelete: ', $e->getMessage(), PHP_EOL;
    -}
    -?>
    -
    - -
    -
    use Data::Dumper;
    -use SWGSDRangel::Configuration;
    -use SWGSDRangel::FeatureSetApi;
    -
    -my $api_instance = SWGSDRangel::FeatureSetApi->new();
    -
    -eval { 
    -    my $result = $api_instance->instanceFeatureSetDelete();
    -    print Dumper($result);
    -};
    -if ($@) {
    -    warn "Exception when calling FeatureSetApi->instanceFeatureSetDelete: $@\n";
    -}
    -
    - -
    -
    from __future__ import print_statement
    -import time
    -import swagger_sdrangel
    -from swagger_sdrangel.rest import ApiException
    -from pprint import pprint
    -
    -# create an instance of the API class
    -api_instance = swagger_sdrangel.FeatureSetApi()
    -
    -try: 
    -    api_response = api_instance.instance_feature_set_delete()
    -    pprint(api_response)
    -except ApiException as e:
    -    print("Exception when calling FeatureSetApi->instanceFeatureSetDelete: %s\n" % e)
    -
    -
    - -

    Parameters

    - - - - - - -

    Responses

    -

    Status: 202 - Message to remove last feature set was sent successfully

    - - - -
    -
    -
    - -
    - -
    -
    - -

    Status: 404 - No more feature sets to be deleted

    - - - -
    -
    -
    - -
    - -
    -
    - -

    Status: 500 - Error

    - - - -
    -
    -
    - -
    - -
    -
    - -

    Status: 501 - Function not implemented

    - - - -
    -
    -
    - -
    - -
    -
    - -
    -
    -
    -
    -
    -
    -

    instanceFeatureSetPost

    -

    -
    -
    -
    -

    -

    Add (append) a new feature set

    -

    -
    -
    /sdrangel/featureset
    -

    -

    Usage and SDK Samples

    -

    - - -
    -
    -
    curl -X POST "http://localhost/sdrangel/featureset"
    -
    -
    -
    import SWGSDRangel.*;
    -import SWGSDRangel.auth.*;
    -import SWGSDRangel.model.*;
    -import SWGSDRangel.api.FeatureSetApi;
    -
    -import java.io.File;
    -import java.util.*;
    -
    -public class FeatureSetApiExample {
    -
    -    public static void main(String[] args) {
    -        
    -        FeatureSetApi apiInstance = new FeatureSetApi();
    -        try {
    -            SuccessResponse result = apiInstance.instanceFeatureSetPost();
    -            System.out.println(result);
    -        } catch (ApiException e) {
    -            System.err.println("Exception when calling FeatureSetApi#instanceFeatureSetPost");
    -            e.printStackTrace();
    -        }
    -    }
    -}
    -
    - -
    -
    import SWGSDRangel.api.FeatureSetApi;
    -
    -public class FeatureSetApiExample {
    -
    -    public static void main(String[] args) {
    -        FeatureSetApi apiInstance = new FeatureSetApi();
    -        try {
    -            SuccessResponse result = apiInstance.instanceFeatureSetPost();
    -            System.out.println(result);
    -        } catch (ApiException e) {
    -            System.err.println("Exception when calling FeatureSetApi#instanceFeatureSetPost");
    -            e.printStackTrace();
    -        }
    -    }
    -}
    -
    - -
    -
    
    -FeatureSetApi *apiInstance = [[FeatureSetApi alloc] init];
    -
    -[apiInstance instanceFeatureSetPostWithCompletionHandler: 
    -              ^(SuccessResponse output, NSError* error) {
    -                            if (output) {
    -                                NSLog(@"%@", output);
    -                            }
    -                            if (error) {
    -                                NSLog(@"Error: %@", error);
    -                            }
    -                        }];
    -
    -
    - -
    -
    var SdRangel = require('sd_rangel');
    -
    -var api = new SdRangel.FeatureSetApi()
    -
    -var callback = function(error, data, response) {
    -  if (error) {
    -    console.error(error);
    -  } else {
    -    console.log('API called successfully. Returned data: ' + data);
    -  }
    -};
    -api.instanceFeatureSetPost(callback);
    -
    -
    - - -
    -
    using System;
    -using System.Diagnostics;
    -using SWGSDRangel.Api;
    -using SWGSDRangel.Client;
    -using SWGSDRangel.Model;
    -
    -namespace Example
    -{
    -    public class instanceFeatureSetPostExample
    -    {
    -        public void main()
    -        {
    -            
    -            var apiInstance = new FeatureSetApi();
    -
    -            try
    -            {
    -                SuccessResponse result = apiInstance.instanceFeatureSetPost();
    -                Debug.WriteLine(result);
    -            }
    -            catch (Exception e)
    -            {
    -                Debug.Print("Exception when calling FeatureSetApi.instanceFeatureSetPost: " + e.Message );
    -            }
    -        }
    -    }
    -}
    -
    -
    - -
    -
    <?php
    -require_once(__DIR__ . '/vendor/autoload.php');
    -
    -$api_instance = new Swagger\Client\Api\FeatureSetApi();
    -
    -try {
    -    $result = $api_instance->instanceFeatureSetPost();
    -    print_r($result);
    -} catch (Exception $e) {
    -    echo 'Exception when calling FeatureSetApi->instanceFeatureSetPost: ', $e->getMessage(), PHP_EOL;
    -}
    -?>
    -
    - -
    -
    use Data::Dumper;
    -use SWGSDRangel::Configuration;
    -use SWGSDRangel::FeatureSetApi;
    -
    -my $api_instance = SWGSDRangel::FeatureSetApi->new();
    -
    -eval { 
    -    my $result = $api_instance->instanceFeatureSetPost();
    -    print Dumper($result);
    -};
    -if ($@) {
    -    warn "Exception when calling FeatureSetApi->instanceFeatureSetPost: $@\n";
    -}
    -
    - -
    -
    from __future__ import print_statement
    -import time
    -import swagger_sdrangel
    -from swagger_sdrangel.rest import ApiException
    -from pprint import pprint
    -
    -# create an instance of the API class
    -api_instance = swagger_sdrangel.FeatureSetApi()
    -
    -try: 
    -    api_response = api_instance.instance_feature_set_post()
    -    pprint(api_response)
    -except ApiException as e:
    -    print("Exception when calling FeatureSetApi->instanceFeatureSetPost: %s\n" % e)
    -
    -
    - -

    Parameters

    - - - - - - -

    Responses

    -

    Status: 202 - Message to add a new feature set was sent successfully

    - - - -
    -
    -
    - -
    - -
    -
    - -

    Status: 500 - Error

    - - - -
    -
    -
    - -
    - -
    -
    - -

    Status: 501 - Function not implemented

    - - - -
    -
    -
    - -
    - -
    -
    - -
    -
    -

    Instance

    @@ -44751,341 +44013,6 @@ except ApiException as e:
    -
    -
    -
    -

    instanceFeatureSetsGet

    -

    -
    -
    -
    -

    -

    Get summary information about feature sets opened in the instance

    -

    -
    -
    /sdrangel/featuresets
    -

    -

    Usage and SDK Samples

    -

    - - -
    -
    -
    curl -X GET "http://localhost/sdrangel/featuresets"
    -
    -
    -
    import SWGSDRangel.*;
    -import SWGSDRangel.auth.*;
    -import SWGSDRangel.model.*;
    -import SWGSDRangel.api.InstanceApi;
    -
    -import java.io.File;
    -import java.util.*;
    -
    -public class InstanceApiExample {
    -
    -    public static void main(String[] args) {
    -        
    -        InstanceApi apiInstance = new InstanceApi();
    -        try {
    -            FeatureSetList result = apiInstance.instanceFeatureSetsGet();
    -            System.out.println(result);
    -        } catch (ApiException e) {
    -            System.err.println("Exception when calling InstanceApi#instanceFeatureSetsGet");
    -            e.printStackTrace();
    -        }
    -    }
    -}
    -
    - -
    -
    import SWGSDRangel.api.InstanceApi;
    -
    -public class InstanceApiExample {
    -
    -    public static void main(String[] args) {
    -        InstanceApi apiInstance = new InstanceApi();
    -        try {
    -            FeatureSetList result = apiInstance.instanceFeatureSetsGet();
    -            System.out.println(result);
    -        } catch (ApiException e) {
    -            System.err.println("Exception when calling InstanceApi#instanceFeatureSetsGet");
    -            e.printStackTrace();
    -        }
    -    }
    -}
    -
    - -
    -
    
    -InstanceApi *apiInstance = [[InstanceApi alloc] init];
    -
    -[apiInstance instanceFeatureSetsGetWithCompletionHandler: 
    -              ^(FeatureSetList output, NSError* error) {
    -                            if (output) {
    -                                NSLog(@"%@", output);
    -                            }
    -                            if (error) {
    -                                NSLog(@"Error: %@", error);
    -                            }
    -                        }];
    -
    -
    - -
    -
    var SdRangel = require('sd_rangel');
    -
    -var api = new SdRangel.InstanceApi()
    -
    -var callback = function(error, data, response) {
    -  if (error) {
    -    console.error(error);
    -  } else {
    -    console.log('API called successfully. Returned data: ' + data);
    -  }
    -};
    -api.instanceFeatureSetsGet(callback);
    -
    -
    - - -
    -
    using System;
    -using System.Diagnostics;
    -using SWGSDRangel.Api;
    -using SWGSDRangel.Client;
    -using SWGSDRangel.Model;
    -
    -namespace Example
    -{
    -    public class instanceFeatureSetsGetExample
    -    {
    -        public void main()
    -        {
    -            
    -            var apiInstance = new InstanceApi();
    -
    -            try
    -            {
    -                FeatureSetList result = apiInstance.instanceFeatureSetsGet();
    -                Debug.WriteLine(result);
    -            }
    -            catch (Exception e)
    -            {
    -                Debug.Print("Exception when calling InstanceApi.instanceFeatureSetsGet: " + e.Message );
    -            }
    -        }
    -    }
    -}
    -
    -
    - -
    -
    <?php
    -require_once(__DIR__ . '/vendor/autoload.php');
    -
    -$api_instance = new Swagger\Client\Api\InstanceApi();
    -
    -try {
    -    $result = $api_instance->instanceFeatureSetsGet();
    -    print_r($result);
    -} catch (Exception $e) {
    -    echo 'Exception when calling InstanceApi->instanceFeatureSetsGet: ', $e->getMessage(), PHP_EOL;
    -}
    -?>
    -
    - -
    -
    use Data::Dumper;
    -use SWGSDRangel::Configuration;
    -use SWGSDRangel::InstanceApi;
    -
    -my $api_instance = SWGSDRangel::InstanceApi->new();
    -
    -eval { 
    -    my $result = $api_instance->instanceFeatureSetsGet();
    -    print Dumper($result);
    -};
    -if ($@) {
    -    warn "Exception when calling InstanceApi->instanceFeatureSetsGet: $@\n";
    -}
    -
    - -
    -
    from __future__ import print_statement
    -import time
    -import swagger_sdrangel
    -from swagger_sdrangel.rest import ApiException
    -from pprint import pprint
    -
    -# create an instance of the API class
    -api_instance = swagger_sdrangel.InstanceApi()
    -
    -try: 
    -    api_response = api_instance.instance_feature_sets_get()
    -    pprint(api_response)
    -except ApiException as e:
    -    print("Exception when calling InstanceApi->instanceFeatureSetsGet: %s\n" % e)
    -
    -
    - -

    Parameters

    - - - - - - -

    Responses

    -

    Status: 200 - On success return feature set list

    - - - -
    -
    -
    - -
    - -
    -
    - -

    Status: 500 - Error

    - - - -
    -
    -
    - -
    - -
    -
    - -

    Status: 501 - Function not implemented

    - - - -
    -
    -
    - -
    - -
    -
    - -
    -
    -
    @@ -52469,7 +51396,7 @@ except ApiException as e:
    - Generated 2022-05-01T22:14:53.451+02:00 + Generated 2022-05-03T04:22:59.079+02:00
    diff --git a/swagger/sdrangel/code/qt5/client/SWGAISModActions_tx.cpp b/swagger/sdrangel/code/qt5/client/SWGAISModActions_tx.cpp deleted file mode 100644 index 26eddd1b2..000000000 --- a/swagger/sdrangel/code/qt5/client/SWGAISModActions_tx.cpp +++ /dev/null @@ -1,110 +0,0 @@ -/** - * SDRangel - * This is the web REST/JSON API of SDRangel SDR software. SDRangel is an Open Source Qt5/OpenGL 3.0+ (4.3+ in Windows) GUI and server Software Defined Radio and signal analyzer in software. It supports Airspy, BladeRF, HackRF, LimeSDR, PlutoSDR, RTL-SDR, SDRplay RSP1 and FunCube --- Limitations and specifcities: * In SDRangel GUI the first Rx device set cannot be deleted. Conversely the server starts with no device sets and its number of device sets can be reduced to zero by as many calls as necessary to /sdrangel/deviceset with DELETE method. * Preset import and export from/to file is a server only feature. * Device set focus is a GUI only feature. * The following channels are not implemented (status 501 is returned): ATV and DATV demodulators, Channel Analyzer NG, LoRa demodulator * The device settings and report structures contains only the sub-structure corresponding to the device type. The DeviceSettings and DeviceReport structures documented here shows all of them but only one will be or should be present at a time * The channel settings and report structures contains only the sub-structure corresponding to the channel type. The ChannelSettings and ChannelReport structures documented here shows all of them but only one will be or should be present at a time --- - * - * OpenAPI spec version: 6.0.0 - * Contact: f4exb06@gmail.com - * - * NOTE: This class is auto generated by the swagger code generator program. - * https://github.com/swagger-api/swagger-codegen.git - * Do not edit the class manually. - */ - - -#include "SWGAISModActions_tx.h" - -#include "SWGHelpers.h" - -#include -#include -#include -#include - -namespace SWGSDRangel { - -SWGAISModActions_tx::SWGAISModActions_tx(QString* json) { - init(); - this->fromJson(*json); -} - -SWGAISModActions_tx::SWGAISModActions_tx() { - data = nullptr; - m_data_isSet = false; -} - -SWGAISModActions_tx::~SWGAISModActions_tx() { - this->cleanup(); -} - -void -SWGAISModActions_tx::init() { - data = new QString(""); - m_data_isSet = false; -} - -void -SWGAISModActions_tx::cleanup() { - if(data != nullptr) { - delete data; - } -} - -SWGAISModActions_tx* -SWGAISModActions_tx::fromJson(QString &json) { - QByteArray array (json.toStdString().c_str()); - QJsonDocument doc = QJsonDocument::fromJson(array); - QJsonObject jsonObject = doc.object(); - this->fromJsonObject(jsonObject); - return this; -} - -void -SWGAISModActions_tx::fromJsonObject(QJsonObject &pJson) { - ::SWGSDRangel::setValue(&data, pJson["data"], "QString", "QString"); - -} - -QString -SWGAISModActions_tx::asJson () -{ - QJsonObject* obj = this->asJsonObject(); - - QJsonDocument doc(*obj); - QByteArray bytes = doc.toJson(); - delete obj; - return QString(bytes); -} - -QJsonObject* -SWGAISModActions_tx::asJsonObject() { - QJsonObject* obj = new QJsonObject(); - if(data != nullptr && *data != QString("")){ - toJsonValue(QString("data"), data, obj, QString("QString")); - } - - return obj; -} - -QString* -SWGAISModActions_tx::getData() { - return data; -} -void -SWGAISModActions_tx::setData(QString* data) { - this->data = data; - this->m_data_isSet = true; -} - - -bool -SWGAISModActions_tx::isSet(){ - bool isObjectUpdated = false; - do{ - if(data && *data != QString("")){ - isObjectUpdated = true; break; - } - }while(false); - return isObjectUpdated; -} -} - diff --git a/swagger/sdrangel/code/qt5/client/SWGAISModActions_tx.h b/swagger/sdrangel/code/qt5/client/SWGAISModActions_tx.h deleted file mode 100644 index e03dd89a6..000000000 --- a/swagger/sdrangel/code/qt5/client/SWGAISModActions_tx.h +++ /dev/null @@ -1,59 +0,0 @@ -/** - * SDRangel - * This is the web REST/JSON API of SDRangel SDR software. SDRangel is an Open Source Qt5/OpenGL 3.0+ (4.3+ in Windows) GUI and server Software Defined Radio and signal analyzer in software. It supports Airspy, BladeRF, HackRF, LimeSDR, PlutoSDR, RTL-SDR, SDRplay RSP1 and FunCube --- Limitations and specifcities: * In SDRangel GUI the first Rx device set cannot be deleted. Conversely the server starts with no device sets and its number of device sets can be reduced to zero by as many calls as necessary to /sdrangel/deviceset with DELETE method. * Preset import and export from/to file is a server only feature. * Device set focus is a GUI only feature. * The following channels are not implemented (status 501 is returned): ATV and DATV demodulators, Channel Analyzer NG, LoRa demodulator * The device settings and report structures contains only the sub-structure corresponding to the device type. The DeviceSettings and DeviceReport structures documented here shows all of them but only one will be or should be present at a time * The channel settings and report structures contains only the sub-structure corresponding to the channel type. The ChannelSettings and ChannelReport structures documented here shows all of them but only one will be or should be present at a time --- - * - * OpenAPI spec version: 6.0.0 - * Contact: f4exb06@gmail.com - * - * NOTE: This class is auto generated by the swagger code generator program. - * https://github.com/swagger-api/swagger-codegen.git - * Do not edit the class manually. - */ - -/* - * SWGAISModActions_tx.h - * - * Transmit a message - */ - -#ifndef SWGAISModActions_tx_H_ -#define SWGAISModActions_tx_H_ - -#include - - -#include - -#include "SWGObject.h" -#include "export.h" - -namespace SWGSDRangel { - -class SWG_API SWGAISModActions_tx: public SWGObject { -public: - SWGAISModActions_tx(); - SWGAISModActions_tx(QString* json); - virtual ~SWGAISModActions_tx(); - void init(); - void cleanup(); - - virtual QString asJson () override; - virtual QJsonObject* asJsonObject() override; - virtual void fromJsonObject(QJsonObject &json) override; - virtual SWGAISModActions_tx* fromJson(QString &jsonString) override; - - QString* getData(); - void setData(QString* data); - - - virtual bool isSet() override; - -private: - QString* data; - bool m_data_isSet; - -}; - -} - -#endif /* SWGAISModActions_tx_H_ */ diff --git a/swagger/sdrangel/code/qt5/client/SWGFeaturePresetTransfer.cpp b/swagger/sdrangel/code/qt5/client/SWGFeaturePresetTransfer.cpp deleted file mode 100644 index c08d58840..000000000 --- a/swagger/sdrangel/code/qt5/client/SWGFeaturePresetTransfer.cpp +++ /dev/null @@ -1,133 +0,0 @@ -/** - * SDRangel - * This is the web REST/JSON API of SDRangel SDR software. SDRangel is an Open Source Qt5/OpenGL 3.0+ (4.3+ in Windows) GUI and server Software Defined Radio and signal analyzer in software. It supports Airspy, BladeRF, HackRF, LimeSDR, PlutoSDR, RTL-SDR, SDRplay RSP1 and FunCube --- Limitations and specifcities: * In SDRangel GUI the first Rx device set cannot be deleted. Conversely the server starts with no device sets and its number of device sets can be reduced to zero by as many calls as necessary to /sdrangel/deviceset with DELETE method. * Preset import and export from/to file is a server only feature. * Device set focus is a GUI only feature. * The following channels are not implemented (status 501 is returned): ATV and DATV demodulators, Channel Analyzer NG, LoRa demodulator * The device settings and report structures contains only the sub-structure corresponding to the device type. The DeviceSettings and DeviceReport structures documented here shows all of them but only one will be or should be present at a time * The channel settings and report structures contains only the sub-structure corresponding to the channel type. The ChannelSettings and ChannelReport structures documented here shows all of them but only one will be or should be present at a time --- - * - * OpenAPI spec version: 6.0.0 - * Contact: f4exb06@gmail.com - * - * NOTE: This class is auto generated by the swagger code generator program. - * https://github.com/swagger-api/swagger-codegen.git - * Do not edit the class manually. - */ - - -#include "SWGFeaturePresetTransfer.h" - -#include "SWGHelpers.h" - -#include -#include -#include -#include - -namespace SWGSDRangel { - -SWGFeaturePresetTransfer::SWGFeaturePresetTransfer(QString* json) { - init(); - this->fromJson(*json); -} - -SWGFeaturePresetTransfer::SWGFeaturePresetTransfer() { - feature_set_index = 0; - m_feature_set_index_isSet = false; - preset = nullptr; - m_preset_isSet = false; -} - -SWGFeaturePresetTransfer::~SWGFeaturePresetTransfer() { - this->cleanup(); -} - -void -SWGFeaturePresetTransfer::init() { - feature_set_index = 0; - m_feature_set_index_isSet = false; - preset = new SWGFeaturePresetIdentifier(); - m_preset_isSet = false; -} - -void -SWGFeaturePresetTransfer::cleanup() { - - if(preset != nullptr) { - delete preset; - } -} - -SWGFeaturePresetTransfer* -SWGFeaturePresetTransfer::fromJson(QString &json) { - QByteArray array (json.toStdString().c_str()); - QJsonDocument doc = QJsonDocument::fromJson(array); - QJsonObject jsonObject = doc.object(); - this->fromJsonObject(jsonObject); - return this; -} - -void -SWGFeaturePresetTransfer::fromJsonObject(QJsonObject &pJson) { - ::SWGSDRangel::setValue(&feature_set_index, pJson["featureSetIndex"], "qint32", ""); - - ::SWGSDRangel::setValue(&preset, pJson["preset"], "SWGFeaturePresetIdentifier", "SWGFeaturePresetIdentifier"); - -} - -QString -SWGFeaturePresetTransfer::asJson () -{ - QJsonObject* obj = this->asJsonObject(); - - QJsonDocument doc(*obj); - QByteArray bytes = doc.toJson(); - delete obj; - return QString(bytes); -} - -QJsonObject* -SWGFeaturePresetTransfer::asJsonObject() { - QJsonObject* obj = new QJsonObject(); - if(m_feature_set_index_isSet){ - obj->insert("featureSetIndex", QJsonValue(feature_set_index)); - } - if((preset != nullptr) && (preset->isSet())){ - toJsonValue(QString("preset"), preset, obj, QString("SWGFeaturePresetIdentifier")); - } - - return obj; -} - -qint32 -SWGFeaturePresetTransfer::getFeatureSetIndex() { - return feature_set_index; -} -void -SWGFeaturePresetTransfer::setFeatureSetIndex(qint32 feature_set_index) { - this->feature_set_index = feature_set_index; - this->m_feature_set_index_isSet = true; -} - -SWGFeaturePresetIdentifier* -SWGFeaturePresetTransfer::getPreset() { - return preset; -} -void -SWGFeaturePresetTransfer::setPreset(SWGFeaturePresetIdentifier* preset) { - this->preset = preset; - this->m_preset_isSet = true; -} - - -bool -SWGFeaturePresetTransfer::isSet(){ - bool isObjectUpdated = false; - do{ - if(m_feature_set_index_isSet){ - isObjectUpdated = true; break; - } - if(preset && preset->isSet()){ - isObjectUpdated = true; break; - } - }while(false); - return isObjectUpdated; -} -} - diff --git a/swagger/sdrangel/code/qt5/client/SWGFeaturePresetTransfer.h b/swagger/sdrangel/code/qt5/client/SWGFeaturePresetTransfer.h deleted file mode 100644 index f2cc014c0..000000000 --- a/swagger/sdrangel/code/qt5/client/SWGFeaturePresetTransfer.h +++ /dev/null @@ -1,65 +0,0 @@ -/** - * SDRangel - * This is the web REST/JSON API of SDRangel SDR software. SDRangel is an Open Source Qt5/OpenGL 3.0+ (4.3+ in Windows) GUI and server Software Defined Radio and signal analyzer in software. It supports Airspy, BladeRF, HackRF, LimeSDR, PlutoSDR, RTL-SDR, SDRplay RSP1 and FunCube --- Limitations and specifcities: * In SDRangel GUI the first Rx device set cannot be deleted. Conversely the server starts with no device sets and its number of device sets can be reduced to zero by as many calls as necessary to /sdrangel/deviceset with DELETE method. * Preset import and export from/to file is a server only feature. * Device set focus is a GUI only feature. * The following channels are not implemented (status 501 is returned): ATV and DATV demodulators, Channel Analyzer NG, LoRa demodulator * The device settings and report structures contains only the sub-structure corresponding to the device type. The DeviceSettings and DeviceReport structures documented here shows all of them but only one will be or should be present at a time * The channel settings and report structures contains only the sub-structure corresponding to the channel type. The ChannelSettings and ChannelReport structures documented here shows all of them but only one will be or should be present at a time --- - * - * OpenAPI spec version: 6.0.0 - * Contact: f4exb06@gmail.com - * - * NOTE: This class is auto generated by the swagger code generator program. - * https://github.com/swagger-api/swagger-codegen.git - * Do not edit the class manually. - */ - -/* - * SWGFeaturePresetTransfer.h - * - * Preset transfer to or from a feature set - */ - -#ifndef SWGFeaturePresetTransfer_H_ -#define SWGFeaturePresetTransfer_H_ - -#include - - -#include "SWGFeaturePresetIdentifier.h" - -#include "SWGObject.h" -#include "export.h" - -namespace SWGSDRangel { - -class SWG_API SWGFeaturePresetTransfer: public SWGObject { -public: - SWGFeaturePresetTransfer(); - SWGFeaturePresetTransfer(QString* json); - virtual ~SWGFeaturePresetTransfer(); - void init(); - void cleanup(); - - virtual QString asJson () override; - virtual QJsonObject* asJsonObject() override; - virtual void fromJsonObject(QJsonObject &json) override; - virtual SWGFeaturePresetTransfer* fromJson(QString &jsonString) override; - - qint32 getFeatureSetIndex(); - void setFeatureSetIndex(qint32 feature_set_index); - - SWGFeaturePresetIdentifier* getPreset(); - void setPreset(SWGFeaturePresetIdentifier* preset); - - - virtual bool isSet() override; - -private: - qint32 feature_set_index; - bool m_feature_set_index_isSet; - - SWGFeaturePresetIdentifier* preset; - bool m_preset_isSet; - -}; - -} - -#endif /* SWGFeaturePresetTransfer_H_ */ diff --git a/swagger/sdrangel/code/qt5/client/SWGFeatureSetApi.cpp b/swagger/sdrangel/code/qt5/client/SWGFeatureSetApi.cpp index 66ae3a754..cce203117 100644 --- a/swagger/sdrangel/code/qt5/client/SWGFeatureSetApi.cpp +++ b/swagger/sdrangel/code/qt5/client/SWGFeatureSetApi.cpp @@ -823,109 +823,5 @@ SWGFeatureSetApi::featuresetPresetPutCallback(SWGHttpRequestWorker * worker) { } } -void -SWGFeatureSetApi::instanceFeatureSetDelete() { - QString fullPath; - fullPath.append(this->host).append(this->basePath).append("/sdrangel/featureset"); - - - - SWGHttpRequestWorker *worker = new SWGHttpRequestWorker(); - SWGHttpRequestInput input(fullPath, "DELETE"); - - - - - - foreach(QString key, this->defaultHeaders.keys()) { - input.headers.insert(key, this->defaultHeaders.value(key)); - } - - connect(worker, - &SWGHttpRequestWorker::on_execution_finished, - this, - &SWGFeatureSetApi::instanceFeatureSetDeleteCallback); - - worker->execute(&input); -} - -void -SWGFeatureSetApi::instanceFeatureSetDeleteCallback(SWGHttpRequestWorker * worker) { - QString msg; - QString error_str = worker->error_str; - QNetworkReply::NetworkError error_type = worker->error_type; - - if (worker->error_type == QNetworkReply::NoError) { - msg = QString("Success! %1 bytes").arg(worker->response.length()); - } - else { - msg = "Error: " + worker->error_str; - } - - - QString json(worker->response); - SWGSuccessResponse* output = static_cast(create(json, QString("SWGSuccessResponse"))); - worker->deleteLater(); - - if (worker->error_type == QNetworkReply::NoError) { - emit instanceFeatureSetDeleteSignal(output); - } else { - emit instanceFeatureSetDeleteSignalE(output, error_type, error_str); - emit instanceFeatureSetDeleteSignalEFull(worker, error_type, error_str); - } -} - -void -SWGFeatureSetApi::instanceFeatureSetPost() { - QString fullPath; - fullPath.append(this->host).append(this->basePath).append("/sdrangel/featureset"); - - - - SWGHttpRequestWorker *worker = new SWGHttpRequestWorker(); - SWGHttpRequestInput input(fullPath, "POST"); - - - - - - foreach(QString key, this->defaultHeaders.keys()) { - input.headers.insert(key, this->defaultHeaders.value(key)); - } - - connect(worker, - &SWGHttpRequestWorker::on_execution_finished, - this, - &SWGFeatureSetApi::instanceFeatureSetPostCallback); - - worker->execute(&input); -} - -void -SWGFeatureSetApi::instanceFeatureSetPostCallback(SWGHttpRequestWorker * worker) { - QString msg; - QString error_str = worker->error_str; - QNetworkReply::NetworkError error_type = worker->error_type; - - if (worker->error_type == QNetworkReply::NoError) { - msg = QString("Success! %1 bytes").arg(worker->response.length()); - } - else { - msg = "Error: " + worker->error_str; - } - - - QString json(worker->response); - SWGSuccessResponse* output = static_cast(create(json, QString("SWGSuccessResponse"))); - worker->deleteLater(); - - if (worker->error_type == QNetworkReply::NoError) { - emit instanceFeatureSetPostSignal(output); - } else { - emit instanceFeatureSetPostSignalE(output, error_type, error_str); - emit instanceFeatureSetPostSignalEFull(worker, error_type, error_str); - } -} - } diff --git a/swagger/sdrangel/code/qt5/client/SWGFeatureSetApi.h b/swagger/sdrangel/code/qt5/client/SWGFeatureSetApi.h index 4aaf9f60f..2952a7a7f 100644 --- a/swagger/sdrangel/code/qt5/client/SWGFeatureSetApi.h +++ b/swagger/sdrangel/code/qt5/client/SWGFeatureSetApi.h @@ -55,8 +55,6 @@ public: void featuresetPresetPatch(qint32 feature_set_index, SWGFeaturePresetIdentifier& body); void featuresetPresetPost(qint32 feature_set_index, SWGFeaturePresetIdentifier& body); void featuresetPresetPut(qint32 feature_set_index, SWGFeaturePresetIdentifier& body); - void instanceFeatureSetDelete(); - void instanceFeatureSetPost(); private: void featuresetFEatureSettingsPutCallback (SWGHttpRequestWorker * worker); @@ -73,8 +71,6 @@ private: void featuresetPresetPatchCallback (SWGHttpRequestWorker * worker); void featuresetPresetPostCallback (SWGHttpRequestWorker * worker); void featuresetPresetPutCallback (SWGHttpRequestWorker * worker); - void instanceFeatureSetDeleteCallback (SWGHttpRequestWorker * worker); - void instanceFeatureSetPostCallback (SWGHttpRequestWorker * worker); signals: void featuresetFEatureSettingsPutSignal(SWGFeatureSettings* summary); @@ -91,8 +87,6 @@ signals: void featuresetPresetPatchSignal(SWGFeaturePresetIdentifier* summary); void featuresetPresetPostSignal(SWGPresetIdentifier* summary); void featuresetPresetPutSignal(SWGFeaturePresetIdentifier* summary); - void instanceFeatureSetDeleteSignal(SWGSuccessResponse* summary); - void instanceFeatureSetPostSignal(SWGSuccessResponse* summary); void featuresetFEatureSettingsPutSignalE(SWGFeatureSettings* summary, QNetworkReply::NetworkError error_type, QString& error_str); void featuresetFeatureActionsPostSignalE(SWGSuccessResponse* summary, QNetworkReply::NetworkError error_type, QString& error_str); @@ -108,8 +102,6 @@ signals: void featuresetPresetPatchSignalE(SWGFeaturePresetIdentifier* summary, QNetworkReply::NetworkError error_type, QString& error_str); void featuresetPresetPostSignalE(SWGPresetIdentifier* summary, QNetworkReply::NetworkError error_type, QString& error_str); void featuresetPresetPutSignalE(SWGFeaturePresetIdentifier* summary, QNetworkReply::NetworkError error_type, QString& error_str); - void instanceFeatureSetDeleteSignalE(SWGSuccessResponse* summary, QNetworkReply::NetworkError error_type, QString& error_str); - void instanceFeatureSetPostSignalE(SWGSuccessResponse* summary, QNetworkReply::NetworkError error_type, QString& error_str); void featuresetFEatureSettingsPutSignalEFull(SWGHttpRequestWorker* worker, QNetworkReply::NetworkError error_type, QString& error_str); void featuresetFeatureActionsPostSignalEFull(SWGHttpRequestWorker* worker, QNetworkReply::NetworkError error_type, QString& error_str); @@ -125,8 +117,6 @@ signals: void featuresetPresetPatchSignalEFull(SWGHttpRequestWorker* worker, QNetworkReply::NetworkError error_type, QString& error_str); void featuresetPresetPostSignalEFull(SWGHttpRequestWorker* worker, QNetworkReply::NetworkError error_type, QString& error_str); void featuresetPresetPutSignalEFull(SWGHttpRequestWorker* worker, QNetworkReply::NetworkError error_type, QString& error_str); - void instanceFeatureSetDeleteSignalEFull(SWGHttpRequestWorker* worker, QNetworkReply::NetworkError error_type, QString& error_str); - void instanceFeatureSetPostSignalEFull(SWGHttpRequestWorker* worker, QNetworkReply::NetworkError error_type, QString& error_str); }; diff --git a/swagger/sdrangel/code/qt5/client/SWGFeatureSetList.cpp b/swagger/sdrangel/code/qt5/client/SWGFeatureSetList.cpp deleted file mode 100644 index 08779afdc..000000000 --- a/swagger/sdrangel/code/qt5/client/SWGFeatureSetList.cpp +++ /dev/null @@ -1,137 +0,0 @@ -/** - * SDRangel - * This is the web REST/JSON API of SDRangel SDR software. SDRangel is an Open Source Qt5/OpenGL 3.0+ (4.3+ in Windows) GUI and server Software Defined Radio and signal analyzer in software. It supports Airspy, BladeRF, HackRF, LimeSDR, PlutoSDR, RTL-SDR, SDRplay RSP1 and FunCube --- Limitations and specifcities: * In SDRangel GUI the first Rx device set cannot be deleted. Conversely the server starts with no device sets and its number of device sets can be reduced to zero by as many calls as necessary to /sdrangel/deviceset with DELETE method. * Preset import and export from/to file is a server only feature. * Device set focus is a GUI only feature. * The following channels are not implemented (status 501 is returned): ATV and DATV demodulators, Channel Analyzer NG, LoRa demodulator * The device settings and report structures contains only the sub-structure corresponding to the device type. The DeviceSettings and DeviceReport structures documented here shows all of them but only one will be or should be present at a time * The channel settings and report structures contains only the sub-structure corresponding to the channel type. The ChannelSettings and ChannelReport structures documented here shows all of them but only one will be or should be present at a time --- - * - * OpenAPI spec version: 6.0.0 - * Contact: f4exb06@gmail.com - * - * NOTE: This class is auto generated by the swagger code generator program. - * https://github.com/swagger-api/swagger-codegen.git - * Do not edit the class manually. - */ - - -#include "SWGFeatureSetList.h" - -#include "SWGHelpers.h" - -#include -#include -#include -#include - -namespace SWGSDRangel { - -SWGFeatureSetList::SWGFeatureSetList(QString* json) { - init(); - this->fromJson(*json); -} - -SWGFeatureSetList::SWGFeatureSetList() { - featuresetcount = 0; - m_featuresetcount_isSet = false; - feature_sets = nullptr; - m_feature_sets_isSet = false; -} - -SWGFeatureSetList::~SWGFeatureSetList() { - this->cleanup(); -} - -void -SWGFeatureSetList::init() { - featuresetcount = 0; - m_featuresetcount_isSet = false; - feature_sets = new QList(); - m_feature_sets_isSet = false; -} - -void -SWGFeatureSetList::cleanup() { - - if(feature_sets != nullptr) { - auto arr = feature_sets; - for(auto o: *arr) { - delete o; - } - delete feature_sets; - } -} - -SWGFeatureSetList* -SWGFeatureSetList::fromJson(QString &json) { - QByteArray array (json.toStdString().c_str()); - QJsonDocument doc = QJsonDocument::fromJson(array); - QJsonObject jsonObject = doc.object(); - this->fromJsonObject(jsonObject); - return this; -} - -void -SWGFeatureSetList::fromJsonObject(QJsonObject &pJson) { - ::SWGSDRangel::setValue(&featuresetcount, pJson["featuresetcount"], "qint32", ""); - - - ::SWGSDRangel::setValue(&feature_sets, pJson["featureSets"], "QList", "SWGFeatureSet"); -} - -QString -SWGFeatureSetList::asJson () -{ - QJsonObject* obj = this->asJsonObject(); - - QJsonDocument doc(*obj); - QByteArray bytes = doc.toJson(); - delete obj; - return QString(bytes); -} - -QJsonObject* -SWGFeatureSetList::asJsonObject() { - QJsonObject* obj = new QJsonObject(); - if(m_featuresetcount_isSet){ - obj->insert("featuresetcount", QJsonValue(featuresetcount)); - } - if(feature_sets && feature_sets->size() > 0){ - toJsonArray((QList*)feature_sets, obj, "featureSets", "SWGFeatureSet"); - } - - return obj; -} - -qint32 -SWGFeatureSetList::getFeaturesetcount() { - return featuresetcount; -} -void -SWGFeatureSetList::setFeaturesetcount(qint32 featuresetcount) { - this->featuresetcount = featuresetcount; - this->m_featuresetcount_isSet = true; -} - -QList* -SWGFeatureSetList::getFeatureSets() { - return feature_sets; -} -void -SWGFeatureSetList::setFeatureSets(QList* feature_sets) { - this->feature_sets = feature_sets; - this->m_feature_sets_isSet = true; -} - - -bool -SWGFeatureSetList::isSet(){ - bool isObjectUpdated = false; - do{ - if(m_featuresetcount_isSet){ - isObjectUpdated = true; break; - } - if(feature_sets && (feature_sets->size() > 0)){ - isObjectUpdated = true; break; - } - }while(false); - return isObjectUpdated; -} -} - diff --git a/swagger/sdrangel/code/qt5/client/SWGFeatureSetList.h b/swagger/sdrangel/code/qt5/client/SWGFeatureSetList.h deleted file mode 100644 index 16f695f62..000000000 --- a/swagger/sdrangel/code/qt5/client/SWGFeatureSetList.h +++ /dev/null @@ -1,66 +0,0 @@ -/** - * SDRangel - * This is the web REST/JSON API of SDRangel SDR software. SDRangel is an Open Source Qt5/OpenGL 3.0+ (4.3+ in Windows) GUI and server Software Defined Radio and signal analyzer in software. It supports Airspy, BladeRF, HackRF, LimeSDR, PlutoSDR, RTL-SDR, SDRplay RSP1 and FunCube --- Limitations and specifcities: * In SDRangel GUI the first Rx device set cannot be deleted. Conversely the server starts with no device sets and its number of device sets can be reduced to zero by as many calls as necessary to /sdrangel/deviceset with DELETE method. * Preset import and export from/to file is a server only feature. * Device set focus is a GUI only feature. * The following channels are not implemented (status 501 is returned): ATV and DATV demodulators, Channel Analyzer NG, LoRa demodulator * The device settings and report structures contains only the sub-structure corresponding to the device type. The DeviceSettings and DeviceReport structures documented here shows all of them but only one will be or should be present at a time * The channel settings and report structures contains only the sub-structure corresponding to the channel type. The ChannelSettings and ChannelReport structures documented here shows all of them but only one will be or should be present at a time --- - * - * OpenAPI spec version: 6.0.0 - * Contact: f4exb06@gmail.com - * - * NOTE: This class is auto generated by the swagger code generator program. - * https://github.com/swagger-api/swagger-codegen.git - * Do not edit the class manually. - */ - -/* - * SWGFeatureSetList.h - * - * List of feature sets opened in this instance - */ - -#ifndef SWGFeatureSetList_H_ -#define SWGFeatureSetList_H_ - -#include - - -#include "SWGFeatureSet.h" -#include - -#include "SWGObject.h" -#include "export.h" - -namespace SWGSDRangel { - -class SWG_API SWGFeatureSetList: public SWGObject { -public: - SWGFeatureSetList(); - SWGFeatureSetList(QString* json); - virtual ~SWGFeatureSetList(); - void init(); - void cleanup(); - - virtual QString asJson () override; - virtual QJsonObject* asJsonObject() override; - virtual void fromJsonObject(QJsonObject &json) override; - virtual SWGFeatureSetList* fromJson(QString &jsonString) override; - - qint32 getFeaturesetcount(); - void setFeaturesetcount(qint32 featuresetcount); - - QList* getFeatureSets(); - void setFeatureSets(QList* feature_sets); - - - virtual bool isSet() override; - -private: - qint32 featuresetcount; - bool m_featuresetcount_isSet; - - QList* feature_sets; - bool m_feature_sets_isSet; - -}; - -} - -#endif /* SWGFeatureSetList_H_ */ diff --git a/swagger/sdrangel/code/qt5/client/SWGIEEE_802_15_4_ModActions_tx.cpp b/swagger/sdrangel/code/qt5/client/SWGIEEE_802_15_4_ModActions_tx.cpp deleted file mode 100644 index 454c37131..000000000 --- a/swagger/sdrangel/code/qt5/client/SWGIEEE_802_15_4_ModActions_tx.cpp +++ /dev/null @@ -1,110 +0,0 @@ -/** - * SDRangel - * This is the web REST/JSON API of SDRangel SDR software. SDRangel is an Open Source Qt5/OpenGL 3.0+ (4.3+ in Windows) GUI and server Software Defined Radio and signal analyzer in software. It supports Airspy, BladeRF, HackRF, LimeSDR, PlutoSDR, RTL-SDR, SDRplay RSP1 and FunCube --- Limitations and specifcities: * In SDRangel GUI the first Rx device set cannot be deleted. Conversely the server starts with no device sets and its number of device sets can be reduced to zero by as many calls as necessary to /sdrangel/deviceset with DELETE method. * Preset import and export from/to file is a server only feature. * Device set focus is a GUI only feature. * The following channels are not implemented (status 501 is returned): ATV and DATV demodulators, Channel Analyzer NG, LoRa demodulator * The device settings and report structures contains only the sub-structure corresponding to the device type. The DeviceSettings and DeviceReport structures documented here shows all of them but only one will be or should be present at a time * The channel settings and report structures contains only the sub-structure corresponding to the channel type. The ChannelSettings and ChannelReport structures documented here shows all of them but only one will be or should be present at a time --- - * - * OpenAPI spec version: 6.0.0 - * Contact: f4exb06@gmail.com - * - * NOTE: This class is auto generated by the swagger code generator program. - * https://github.com/swagger-api/swagger-codegen.git - * Do not edit the class manually. - */ - - -#include "SWGIEEE_802_15_4_ModActions_tx.h" - -#include "SWGHelpers.h" - -#include -#include -#include -#include - -namespace SWGSDRangel { - -SWGIEEE_802_15_4_ModActions_tx::SWGIEEE_802_15_4_ModActions_tx(QString* json) { - init(); - this->fromJson(*json); -} - -SWGIEEE_802_15_4_ModActions_tx::SWGIEEE_802_15_4_ModActions_tx() { - data = nullptr; - m_data_isSet = false; -} - -SWGIEEE_802_15_4_ModActions_tx::~SWGIEEE_802_15_4_ModActions_tx() { - this->cleanup(); -} - -void -SWGIEEE_802_15_4_ModActions_tx::init() { - data = new QString(""); - m_data_isSet = false; -} - -void -SWGIEEE_802_15_4_ModActions_tx::cleanup() { - if(data != nullptr) { - delete data; - } -} - -SWGIEEE_802_15_4_ModActions_tx* -SWGIEEE_802_15_4_ModActions_tx::fromJson(QString &json) { - QByteArray array (json.toStdString().c_str()); - QJsonDocument doc = QJsonDocument::fromJson(array); - QJsonObject jsonObject = doc.object(); - this->fromJsonObject(jsonObject); - return this; -} - -void -SWGIEEE_802_15_4_ModActions_tx::fromJsonObject(QJsonObject &pJson) { - ::SWGSDRangel::setValue(&data, pJson["data"], "QString", "QString"); - -} - -QString -SWGIEEE_802_15_4_ModActions_tx::asJson () -{ - QJsonObject* obj = this->asJsonObject(); - - QJsonDocument doc(*obj); - QByteArray bytes = doc.toJson(); - delete obj; - return QString(bytes); -} - -QJsonObject* -SWGIEEE_802_15_4_ModActions_tx::asJsonObject() { - QJsonObject* obj = new QJsonObject(); - if(data != nullptr && *data != QString("")){ - toJsonValue(QString("data"), data, obj, QString("QString")); - } - - return obj; -} - -QString* -SWGIEEE_802_15_4_ModActions_tx::getData() { - return data; -} -void -SWGIEEE_802_15_4_ModActions_tx::setData(QString* data) { - this->data = data; - this->m_data_isSet = true; -} - - -bool -SWGIEEE_802_15_4_ModActions_tx::isSet(){ - bool isObjectUpdated = false; - do{ - if(data && *data != QString("")){ - isObjectUpdated = true; break; - } - }while(false); - return isObjectUpdated; -} -} - diff --git a/swagger/sdrangel/code/qt5/client/SWGIEEE_802_15_4_ModActions_tx.h b/swagger/sdrangel/code/qt5/client/SWGIEEE_802_15_4_ModActions_tx.h deleted file mode 100644 index 824dd1f44..000000000 --- a/swagger/sdrangel/code/qt5/client/SWGIEEE_802_15_4_ModActions_tx.h +++ /dev/null @@ -1,59 +0,0 @@ -/** - * SDRangel - * This is the web REST/JSON API of SDRangel SDR software. SDRangel is an Open Source Qt5/OpenGL 3.0+ (4.3+ in Windows) GUI and server Software Defined Radio and signal analyzer in software. It supports Airspy, BladeRF, HackRF, LimeSDR, PlutoSDR, RTL-SDR, SDRplay RSP1 and FunCube --- Limitations and specifcities: * In SDRangel GUI the first Rx device set cannot be deleted. Conversely the server starts with no device sets and its number of device sets can be reduced to zero by as many calls as necessary to /sdrangel/deviceset with DELETE method. * Preset import and export from/to file is a server only feature. * Device set focus is a GUI only feature. * The following channels are not implemented (status 501 is returned): ATV and DATV demodulators, Channel Analyzer NG, LoRa demodulator * The device settings and report structures contains only the sub-structure corresponding to the device type. The DeviceSettings and DeviceReport structures documented here shows all of them but only one will be or should be present at a time * The channel settings and report structures contains only the sub-structure corresponding to the channel type. The ChannelSettings and ChannelReport structures documented here shows all of them but only one will be or should be present at a time --- - * - * OpenAPI spec version: 6.0.0 - * Contact: f4exb06@gmail.com - * - * NOTE: This class is auto generated by the swagger code generator program. - * https://github.com/swagger-api/swagger-codegen.git - * Do not edit the class manually. - */ - -/* - * SWGIEEE_802_15_4_ModActions_tx.h - * - * Transmit a frame - */ - -#ifndef SWGIEEE_802_15_4_ModActions_tx_H_ -#define SWGIEEE_802_15_4_ModActions_tx_H_ - -#include - - -#include - -#include "SWGObject.h" -#include "export.h" - -namespace SWGSDRangel { - -class SWG_API SWGIEEE_802_15_4_ModActions_tx: public SWGObject { -public: - SWGIEEE_802_15_4_ModActions_tx(); - SWGIEEE_802_15_4_ModActions_tx(QString* json); - virtual ~SWGIEEE_802_15_4_ModActions_tx(); - void init(); - void cleanup(); - - virtual QString asJson () override; - virtual QJsonObject* asJsonObject() override; - virtual void fromJsonObject(QJsonObject &json) override; - virtual SWGIEEE_802_15_4_ModActions_tx* fromJson(QString &jsonString) override; - - QString* getData(); - void setData(QString* data); - - - virtual bool isSet() override; - -private: - QString* data; - bool m_data_isSet; - -}; - -} - -#endif /* SWGIEEE_802_15_4_ModActions_tx_H_ */ diff --git a/swagger/sdrangel/code/qt5/client/SWGInstanceApi.cpp b/swagger/sdrangel/code/qt5/client/SWGInstanceApi.cpp index 057eb6492..42cf8de55 100644 --- a/swagger/sdrangel/code/qt5/client/SWGInstanceApi.cpp +++ b/swagger/sdrangel/code/qt5/client/SWGInstanceApi.cpp @@ -1163,58 +1163,6 @@ SWGInstanceApi::instanceFeaturePresetGetCallback(SWGHttpRequestWorker * worker) } } -void -SWGInstanceApi::instanceFeatureSetsGet() { - QString fullPath; - fullPath.append(this->host).append(this->basePath).append("/sdrangel/featuresets"); - - - - SWGHttpRequestWorker *worker = new SWGHttpRequestWorker(); - SWGHttpRequestInput input(fullPath, "GET"); - - - - - - foreach(QString key, this->defaultHeaders.keys()) { - input.headers.insert(key, this->defaultHeaders.value(key)); - } - - connect(worker, - &SWGHttpRequestWorker::on_execution_finished, - this, - &SWGInstanceApi::instanceFeatureSetsGetCallback); - - worker->execute(&input); -} - -void -SWGInstanceApi::instanceFeatureSetsGetCallback(SWGHttpRequestWorker * worker) { - QString msg; - QString error_str = worker->error_str; - QNetworkReply::NetworkError error_type = worker->error_type; - - if (worker->error_type == QNetworkReply::NoError) { - msg = QString("Success! %1 bytes").arg(worker->response.length()); - } - else { - msg = "Error: " + worker->error_str; - } - - - QString json(worker->response); - SWGFeatureSetList* output = static_cast(create(json, QString("SWGFeatureSetList"))); - worker->deleteLater(); - - if (worker->error_type == QNetworkReply::NoError) { - emit instanceFeatureSetsGetSignal(output); - } else { - emit instanceFeatureSetsGetSignalE(output, error_type, error_str); - emit instanceFeatureSetsGetSignalEFull(worker, error_type, error_str); - } -} - void SWGInstanceApi::instanceFeatures() { QString fullPath; diff --git a/swagger/sdrangel/code/qt5/client/SWGInstanceApi.h b/swagger/sdrangel/code/qt5/client/SWGInstanceApi.h index faeefcfcc..8aeb3db09 100644 --- a/swagger/sdrangel/code/qt5/client/SWGInstanceApi.h +++ b/swagger/sdrangel/code/qt5/client/SWGInstanceApi.h @@ -25,7 +25,6 @@ #include "SWGErrorResponse.h" #include "SWGFeaturePresetIdentifier.h" #include "SWGFeaturePresets.h" -#include "SWGFeatureSetList.h" #include "SWGInstanceChannelsResponse.h" #include "SWGInstanceConfigResponse.h" #include "SWGInstanceDevicesResponse.h" @@ -80,7 +79,6 @@ public: void instanceDevices(qint32 direction); void instanceFeaturePresetDelete(SWGFeaturePresetIdentifier& body); void instanceFeaturePresetGet(); - void instanceFeatureSetsGet(); void instanceFeatures(); void instanceLimeRFEConfigGet(QString* serial); void instanceLimeRFEConfigPut(SWGLimeRFESettings& body); @@ -122,7 +120,6 @@ private: void instanceDevicesCallback (SWGHttpRequestWorker * worker); void instanceFeaturePresetDeleteCallback (SWGHttpRequestWorker * worker); void instanceFeaturePresetGetCallback (SWGHttpRequestWorker * worker); - void instanceFeatureSetsGetCallback (SWGHttpRequestWorker * worker); void instanceFeaturesCallback (SWGHttpRequestWorker * worker); void instanceLimeRFEConfigGetCallback (SWGHttpRequestWorker * worker); void instanceLimeRFEConfigPutCallback (SWGHttpRequestWorker * worker); @@ -164,7 +161,6 @@ signals: void instanceDevicesSignal(SWGInstanceDevicesResponse* summary); void instanceFeaturePresetDeleteSignal(SWGFeaturePresetIdentifier* summary); void instanceFeaturePresetGetSignal(SWGFeaturePresets* summary); - void instanceFeatureSetsGetSignal(SWGFeatureSetList* summary); void instanceFeaturesSignal(SWGInstanceFeaturesResponse* summary); void instanceLimeRFEConfigGetSignal(SWGLimeRFESettings* summary); void instanceLimeRFEConfigPutSignal(SWGSuccessResponse* summary); @@ -205,7 +201,6 @@ signals: void instanceDevicesSignalE(SWGInstanceDevicesResponse* summary, QNetworkReply::NetworkError error_type, QString& error_str); void instanceFeaturePresetDeleteSignalE(SWGFeaturePresetIdentifier* summary, QNetworkReply::NetworkError error_type, QString& error_str); void instanceFeaturePresetGetSignalE(SWGFeaturePresets* summary, QNetworkReply::NetworkError error_type, QString& error_str); - void instanceFeatureSetsGetSignalE(SWGFeatureSetList* summary, QNetworkReply::NetworkError error_type, QString& error_str); void instanceFeaturesSignalE(SWGInstanceFeaturesResponse* summary, QNetworkReply::NetworkError error_type, QString& error_str); void instanceLimeRFEConfigGetSignalE(SWGLimeRFESettings* summary, QNetworkReply::NetworkError error_type, QString& error_str); void instanceLimeRFEConfigPutSignalE(SWGSuccessResponse* summary, QNetworkReply::NetworkError error_type, QString& error_str); @@ -246,7 +241,6 @@ signals: void instanceDevicesSignalEFull(SWGHttpRequestWorker* worker, QNetworkReply::NetworkError error_type, QString& error_str); void instanceFeaturePresetDeleteSignalEFull(SWGHttpRequestWorker* worker, QNetworkReply::NetworkError error_type, QString& error_str); void instanceFeaturePresetGetSignalEFull(SWGHttpRequestWorker* worker, QNetworkReply::NetworkError error_type, QString& error_str); - void instanceFeatureSetsGetSignalEFull(SWGHttpRequestWorker* worker, QNetworkReply::NetworkError error_type, QString& error_str); void instanceFeaturesSignalEFull(SWGHttpRequestWorker* worker, QNetworkReply::NetworkError error_type, QString& error_str); void instanceLimeRFEConfigGetSignalEFull(SWGHttpRequestWorker* worker, QNetworkReply::NetworkError error_type, QString& error_str); void instanceLimeRFEConfigPutSignalEFull(SWGHttpRequestWorker* worker, QNetworkReply::NetworkError error_type, QString& error_str); diff --git a/swagger/sdrangel/code/qt5/client/SWGInstanceSummaryResponse.cpp b/swagger/sdrangel/code/qt5/client/SWGInstanceSummaryResponse.cpp index 49a1928c3..e53c4f180 100644 --- a/swagger/sdrangel/code/qt5/client/SWGInstanceSummaryResponse.cpp +++ b/swagger/sdrangel/code/qt5/client/SWGInstanceSummaryResponse.cpp @@ -48,8 +48,8 @@ SWGInstanceSummaryResponse::SWGInstanceSummaryResponse() { m_logging_isSet = false; devicesetlist = nullptr; m_devicesetlist_isSet = false; - featuresetlist = nullptr; - m_featuresetlist_isSet = false; + featureset = nullptr; + m_featureset_isSet = false; } SWGInstanceSummaryResponse::~SWGInstanceSummaryResponse() { @@ -78,8 +78,8 @@ SWGInstanceSummaryResponse::init() { m_logging_isSet = false; devicesetlist = new SWGDeviceSetList(); m_devicesetlist_isSet = false; - featuresetlist = new SWGFeatureSetList(); - m_featuresetlist_isSet = false; + featureset = new SWGFeatureSet(); + m_featureset_isSet = false; } void @@ -108,8 +108,8 @@ SWGInstanceSummaryResponse::cleanup() { if(devicesetlist != nullptr) { delete devicesetlist; } - if(featuresetlist != nullptr) { - delete featuresetlist; + if(featureset != nullptr) { + delete featureset; } } @@ -144,7 +144,7 @@ SWGInstanceSummaryResponse::fromJsonObject(QJsonObject &pJson) { ::SWGSDRangel::setValue(&devicesetlist, pJson["devicesetlist"], "SWGDeviceSetList", "SWGDeviceSetList"); - ::SWGSDRangel::setValue(&featuresetlist, pJson["featuresetlist"], "SWGFeatureSetList", "SWGFeatureSetList"); + ::SWGSDRangel::setValue(&featureset, pJson["featureset"], "SWGFeatureSet", "SWGFeatureSet"); } @@ -192,8 +192,8 @@ SWGInstanceSummaryResponse::asJsonObject() { if((devicesetlist != nullptr) && (devicesetlist->isSet())){ toJsonValue(QString("devicesetlist"), devicesetlist, obj, QString("SWGDeviceSetList")); } - if((featuresetlist != nullptr) && (featuresetlist->isSet())){ - toJsonValue(QString("featuresetlist"), featuresetlist, obj, QString("SWGFeatureSetList")); + if((featureset != nullptr) && (featureset->isSet())){ + toJsonValue(QString("featureset"), featureset, obj, QString("SWGFeatureSet")); } return obj; @@ -299,14 +299,14 @@ SWGInstanceSummaryResponse::setDevicesetlist(SWGDeviceSetList* devicesetlist) { this->m_devicesetlist_isSet = true; } -SWGFeatureSetList* -SWGInstanceSummaryResponse::getFeaturesetlist() { - return featuresetlist; +SWGFeatureSet* +SWGInstanceSummaryResponse::getFeatureset() { + return featureset; } void -SWGInstanceSummaryResponse::setFeaturesetlist(SWGFeatureSetList* featuresetlist) { - this->featuresetlist = featuresetlist; - this->m_featuresetlist_isSet = true; +SWGInstanceSummaryResponse::setFeatureset(SWGFeatureSet* featureset) { + this->featureset = featureset; + this->m_featureset_isSet = true; } @@ -344,7 +344,7 @@ SWGInstanceSummaryResponse::isSet(){ if(devicesetlist && devicesetlist->isSet()){ isObjectUpdated = true; break; } - if(featuresetlist && featuresetlist->isSet()){ + if(featureset && featureset->isSet()){ isObjectUpdated = true; break; } }while(false); diff --git a/swagger/sdrangel/code/qt5/client/SWGInstanceSummaryResponse.h b/swagger/sdrangel/code/qt5/client/SWGInstanceSummaryResponse.h index 43a56f7a1..58d1e53b4 100644 --- a/swagger/sdrangel/code/qt5/client/SWGInstanceSummaryResponse.h +++ b/swagger/sdrangel/code/qt5/client/SWGInstanceSummaryResponse.h @@ -23,7 +23,7 @@ #include "SWGDeviceSetList.h" -#include "SWGFeatureSetList.h" +#include "SWGFeatureSet.h" #include "SWGLoggingInfo.h" #include @@ -75,8 +75,8 @@ public: SWGDeviceSetList* getDevicesetlist(); void setDevicesetlist(SWGDeviceSetList* devicesetlist); - SWGFeatureSetList* getFeaturesetlist(); - void setFeaturesetlist(SWGFeatureSetList* featuresetlist); + SWGFeatureSet* getFeatureset(); + void setFeatureset(SWGFeatureSet* featureset); virtual bool isSet() override; @@ -112,8 +112,8 @@ private: SWGDeviceSetList* devicesetlist; bool m_devicesetlist_isSet; - SWGFeatureSetList* featuresetlist; - bool m_featuresetlist_isSet; + SWGFeatureSet* featureset; + bool m_featureset_isSet; }; diff --git a/swagger/sdrangel/code/qt5/client/SWGLRPTDemodSettings.cpp b/swagger/sdrangel/code/qt5/client/SWGLRPTDemodSettings.cpp deleted file mode 100644 index b1acba920..000000000 --- a/swagger/sdrangel/code/qt5/client/SWGLRPTDemodSettings.cpp +++ /dev/null @@ -1,597 +0,0 @@ -/** - * SDRangel - * This is the web REST/JSON API of SDRangel SDR software. SDRangel is an Open Source Qt5/OpenGL 3.0+ (4.3+ in Windows) GUI and server Software Defined Radio and signal analyzer in software. It supports Airspy, BladeRF, HackRF, LimeSDR, PlutoSDR, RTL-SDR, SDRplay RSP1 and FunCube --- Limitations and specifcities: * In SDRangel GUI the first Rx device set cannot be deleted. Conversely the server starts with no device sets and its number of device sets can be reduced to zero by as many calls as necessary to /sdrangel/deviceset with DELETE method. * Preset import and export from/to file is a server only feature. * Device set focus is a GUI only feature. * The following channels are not implemented (status 501 is returned): ATV and DATV demodulators, Channel Analyzer NG, LoRa demodulator * The device settings and report structures contains only the sub-structure corresponding to the device type. The DeviceSettings and DeviceReport structures documented here shows all of them but only one will be or should be present at a time * The channel settings and report structures contains only the sub-structure corresponding to the channel type. The ChannelSettings and ChannelReport structures documented here shows all of them but only one will be or should be present at a time --- - * - * OpenAPI spec version: 6.0.0 - * Contact: f4exb06@gmail.com - * - * NOTE: This class is auto generated by the swagger code generator program. - * https://github.com/swagger-api/swagger-codegen.git - * Do not edit the class manually. - */ - - -#include "SWGLRPTDemodSettings.h" - -#include "SWGHelpers.h" - -#include -#include -#include -#include - -namespace SWGSDRangel { - -SWGLRPTDemodSettings::SWGLRPTDemodSettings(QString* json) { - init(); - this->fromJson(*json); -} - -SWGLRPTDemodSettings::SWGLRPTDemodSettings() { - input_frequency_offset = 0L; - m_input_frequency_offset_isSet = false; - rf_bandwidth = 0.0f; - m_rf_bandwidth_isSet = false; - fm_deviation = 0; - m_fm_deviation_isSet = false; - crop_noise = 0; - m_crop_noise_isSet = false; - denoise = 0; - m_denoise_isSet = false; - linear_equalise = 0; - m_linear_equalise_isSet = false; - histogram_equalise = 0; - m_histogram_equalise_isSet = false; - precipitation_overlay = 0; - m_precipitation_overlay_isSet = false; - flip = 0; - m_flip_isSet = false; - channels = 0; - m_channels_isSet = false; - decode_enabled = 0; - m_decode_enabled_isSet = false; - auto_save = 0; - m_auto_save_isSet = false; - auto_save_path = nullptr; - m_auto_save_path_isSet = false; - auto_save_min_scan_lines = 0; - m_auto_save_min_scan_lines_isSet = false; - rgb_color = 0; - m_rgb_color_isSet = false; - title = nullptr; - m_title_isSet = false; - stream_index = 0; - m_stream_index_isSet = false; - use_reverse_api = 0; - m_use_reverse_api_isSet = false; - reverse_api_address = nullptr; - m_reverse_api_address_isSet = false; - reverse_api_port = 0; - m_reverse_api_port_isSet = false; - reverse_api_device_index = 0; - m_reverse_api_device_index_isSet = false; - reverse_api_channel_index = 0; - m_reverse_api_channel_index_isSet = false; -} - -SWGLRPTDemodSettings::~SWGLRPTDemodSettings() { - this->cleanup(); -} - -void -SWGLRPTDemodSettings::init() { - input_frequency_offset = 0L; - m_input_frequency_offset_isSet = false; - rf_bandwidth = 0.0f; - m_rf_bandwidth_isSet = false; - fm_deviation = 0; - m_fm_deviation_isSet = false; - crop_noise = 0; - m_crop_noise_isSet = false; - denoise = 0; - m_denoise_isSet = false; - linear_equalise = 0; - m_linear_equalise_isSet = false; - histogram_equalise = 0; - m_histogram_equalise_isSet = false; - precipitation_overlay = 0; - m_precipitation_overlay_isSet = false; - flip = 0; - m_flip_isSet = false; - channels = 0; - m_channels_isSet = false; - decode_enabled = 0; - m_decode_enabled_isSet = false; - auto_save = 0; - m_auto_save_isSet = false; - auto_save_path = new QString(""); - m_auto_save_path_isSet = false; - auto_save_min_scan_lines = 0; - m_auto_save_min_scan_lines_isSet = false; - rgb_color = 0; - m_rgb_color_isSet = false; - title = new QString(""); - m_title_isSet = false; - stream_index = 0; - m_stream_index_isSet = false; - use_reverse_api = 0; - m_use_reverse_api_isSet = false; - reverse_api_address = new QString(""); - m_reverse_api_address_isSet = false; - reverse_api_port = 0; - m_reverse_api_port_isSet = false; - reverse_api_device_index = 0; - m_reverse_api_device_index_isSet = false; - reverse_api_channel_index = 0; - m_reverse_api_channel_index_isSet = false; -} - -void -SWGLRPTDemodSettings::cleanup() { - - - - - - - - - - - - - if(auto_save_path != nullptr) { - delete auto_save_path; - } - - - if(title != nullptr) { - delete title; - } - - - if(reverse_api_address != nullptr) { - delete reverse_api_address; - } - - - -} - -SWGLRPTDemodSettings* -SWGLRPTDemodSettings::fromJson(QString &json) { - QByteArray array (json.toStdString().c_str()); - QJsonDocument doc = QJsonDocument::fromJson(array); - QJsonObject jsonObject = doc.object(); - this->fromJsonObject(jsonObject); - return this; -} - -void -SWGLRPTDemodSettings::fromJsonObject(QJsonObject &pJson) { - ::SWGSDRangel::setValue(&input_frequency_offset, pJson["inputFrequencyOffset"], "qint64", ""); - - ::SWGSDRangel::setValue(&rf_bandwidth, pJson["rfBandwidth"], "float", ""); - - ::SWGSDRangel::setValue(&fm_deviation, pJson["fmDeviation"], "qint32", ""); - - ::SWGSDRangel::setValue(&crop_noise, pJson["cropNoise"], "qint32", ""); - - ::SWGSDRangel::setValue(&denoise, pJson["denoise"], "qint32", ""); - - ::SWGSDRangel::setValue(&linear_equalise, pJson["linearEqualise"], "qint32", ""); - - ::SWGSDRangel::setValue(&histogram_equalise, pJson["histogramEqualise"], "qint32", ""); - - ::SWGSDRangel::setValue(&precipitation_overlay, pJson["precipitationOverlay"], "qint32", ""); - - ::SWGSDRangel::setValue(&flip, pJson["flip"], "qint32", ""); - - ::SWGSDRangel::setValue(&channels, pJson["channels"], "qint32", ""); - - ::SWGSDRangel::setValue(&decode_enabled, pJson["decodeEnabled"], "qint32", ""); - - ::SWGSDRangel::setValue(&auto_save, pJson["autoSave"], "qint32", ""); - - ::SWGSDRangel::setValue(&auto_save_path, pJson["autoSavePath"], "QString", "QString"); - - ::SWGSDRangel::setValue(&auto_save_min_scan_lines, pJson["autoSaveMinScanLines"], "qint32", ""); - - ::SWGSDRangel::setValue(&rgb_color, pJson["rgbColor"], "qint32", ""); - - ::SWGSDRangel::setValue(&title, pJson["title"], "QString", "QString"); - - ::SWGSDRangel::setValue(&stream_index, pJson["streamIndex"], "qint32", ""); - - ::SWGSDRangel::setValue(&use_reverse_api, pJson["useReverseAPI"], "qint32", ""); - - ::SWGSDRangel::setValue(&reverse_api_address, pJson["reverseAPIAddress"], "QString", "QString"); - - ::SWGSDRangel::setValue(&reverse_api_port, pJson["reverseAPIPort"], "qint32", ""); - - ::SWGSDRangel::setValue(&reverse_api_device_index, pJson["reverseAPIDeviceIndex"], "qint32", ""); - - ::SWGSDRangel::setValue(&reverse_api_channel_index, pJson["reverseAPIChannelIndex"], "qint32", ""); - -} - -QString -SWGLRPTDemodSettings::asJson () -{ - QJsonObject* obj = this->asJsonObject(); - - QJsonDocument doc(*obj); - QByteArray bytes = doc.toJson(); - delete obj; - return QString(bytes); -} - -QJsonObject* -SWGLRPTDemodSettings::asJsonObject() { - QJsonObject* obj = new QJsonObject(); - if(m_input_frequency_offset_isSet){ - obj->insert("inputFrequencyOffset", QJsonValue(input_frequency_offset)); - } - if(m_rf_bandwidth_isSet){ - obj->insert("rfBandwidth", QJsonValue(rf_bandwidth)); - } - if(m_fm_deviation_isSet){ - obj->insert("fmDeviation", QJsonValue(fm_deviation)); - } - if(m_crop_noise_isSet){ - obj->insert("cropNoise", QJsonValue(crop_noise)); - } - if(m_denoise_isSet){ - obj->insert("denoise", QJsonValue(denoise)); - } - if(m_linear_equalise_isSet){ - obj->insert("linearEqualise", QJsonValue(linear_equalise)); - } - if(m_histogram_equalise_isSet){ - obj->insert("histogramEqualise", QJsonValue(histogram_equalise)); - } - if(m_precipitation_overlay_isSet){ - obj->insert("precipitationOverlay", QJsonValue(precipitation_overlay)); - } - if(m_flip_isSet){ - obj->insert("flip", QJsonValue(flip)); - } - if(m_channels_isSet){ - obj->insert("channels", QJsonValue(channels)); - } - if(m_decode_enabled_isSet){ - obj->insert("decodeEnabled", QJsonValue(decode_enabled)); - } - if(m_auto_save_isSet){ - obj->insert("autoSave", QJsonValue(auto_save)); - } - if(auto_save_path != nullptr && *auto_save_path != QString("")){ - toJsonValue(QString("autoSavePath"), auto_save_path, obj, QString("QString")); - } - if(m_auto_save_min_scan_lines_isSet){ - obj->insert("autoSaveMinScanLines", QJsonValue(auto_save_min_scan_lines)); - } - if(m_rgb_color_isSet){ - obj->insert("rgbColor", QJsonValue(rgb_color)); - } - if(title != nullptr && *title != QString("")){ - toJsonValue(QString("title"), title, obj, QString("QString")); - } - if(m_stream_index_isSet){ - obj->insert("streamIndex", QJsonValue(stream_index)); - } - if(m_use_reverse_api_isSet){ - obj->insert("useReverseAPI", QJsonValue(use_reverse_api)); - } - if(reverse_api_address != nullptr && *reverse_api_address != QString("")){ - toJsonValue(QString("reverseAPIAddress"), reverse_api_address, obj, QString("QString")); - } - if(m_reverse_api_port_isSet){ - obj->insert("reverseAPIPort", QJsonValue(reverse_api_port)); - } - if(m_reverse_api_device_index_isSet){ - obj->insert("reverseAPIDeviceIndex", QJsonValue(reverse_api_device_index)); - } - if(m_reverse_api_channel_index_isSet){ - obj->insert("reverseAPIChannelIndex", QJsonValue(reverse_api_channel_index)); - } - - return obj; -} - -qint64 -SWGLRPTDemodSettings::getInputFrequencyOffset() { - return input_frequency_offset; -} -void -SWGLRPTDemodSettings::setInputFrequencyOffset(qint64 input_frequency_offset) { - this->input_frequency_offset = input_frequency_offset; - this->m_input_frequency_offset_isSet = true; -} - -float -SWGLRPTDemodSettings::getRfBandwidth() { - return rf_bandwidth; -} -void -SWGLRPTDemodSettings::setRfBandwidth(float rf_bandwidth) { - this->rf_bandwidth = rf_bandwidth; - this->m_rf_bandwidth_isSet = true; -} - -qint32 -SWGLRPTDemodSettings::getFmDeviation() { - return fm_deviation; -} -void -SWGLRPTDemodSettings::setFmDeviation(qint32 fm_deviation) { - this->fm_deviation = fm_deviation; - this->m_fm_deviation_isSet = true; -} - -qint32 -SWGLRPTDemodSettings::getCropNoise() { - return crop_noise; -} -void -SWGLRPTDemodSettings::setCropNoise(qint32 crop_noise) { - this->crop_noise = crop_noise; - this->m_crop_noise_isSet = true; -} - -qint32 -SWGLRPTDemodSettings::getDenoise() { - return denoise; -} -void -SWGLRPTDemodSettings::setDenoise(qint32 denoise) { - this->denoise = denoise; - this->m_denoise_isSet = true; -} - -qint32 -SWGLRPTDemodSettings::getLinearEqualise() { - return linear_equalise; -} -void -SWGLRPTDemodSettings::setLinearEqualise(qint32 linear_equalise) { - this->linear_equalise = linear_equalise; - this->m_linear_equalise_isSet = true; -} - -qint32 -SWGLRPTDemodSettings::getHistogramEqualise() { - return histogram_equalise; -} -void -SWGLRPTDemodSettings::setHistogramEqualise(qint32 histogram_equalise) { - this->histogram_equalise = histogram_equalise; - this->m_histogram_equalise_isSet = true; -} - -qint32 -SWGLRPTDemodSettings::getPrecipitationOverlay() { - return precipitation_overlay; -} -void -SWGLRPTDemodSettings::setPrecipitationOverlay(qint32 precipitation_overlay) { - this->precipitation_overlay = precipitation_overlay; - this->m_precipitation_overlay_isSet = true; -} - -qint32 -SWGLRPTDemodSettings::getFlip() { - return flip; -} -void -SWGLRPTDemodSettings::setFlip(qint32 flip) { - this->flip = flip; - this->m_flip_isSet = true; -} - -qint32 -SWGLRPTDemodSettings::getChannels() { - return channels; -} -void -SWGLRPTDemodSettings::setChannels(qint32 channels) { - this->channels = channels; - this->m_channels_isSet = true; -} - -qint32 -SWGLRPTDemodSettings::getDecodeEnabled() { - return decode_enabled; -} -void -SWGLRPTDemodSettings::setDecodeEnabled(qint32 decode_enabled) { - this->decode_enabled = decode_enabled; - this->m_decode_enabled_isSet = true; -} - -qint32 -SWGLRPTDemodSettings::getAutoSave() { - return auto_save; -} -void -SWGLRPTDemodSettings::setAutoSave(qint32 auto_save) { - this->auto_save = auto_save; - this->m_auto_save_isSet = true; -} - -QString* -SWGLRPTDemodSettings::getAutoSavePath() { - return auto_save_path; -} -void -SWGLRPTDemodSettings::setAutoSavePath(QString* auto_save_path) { - this->auto_save_path = auto_save_path; - this->m_auto_save_path_isSet = true; -} - -qint32 -SWGLRPTDemodSettings::getAutoSaveMinScanLines() { - return auto_save_min_scan_lines; -} -void -SWGLRPTDemodSettings::setAutoSaveMinScanLines(qint32 auto_save_min_scan_lines) { - this->auto_save_min_scan_lines = auto_save_min_scan_lines; - this->m_auto_save_min_scan_lines_isSet = true; -} - -qint32 -SWGLRPTDemodSettings::getRgbColor() { - return rgb_color; -} -void -SWGLRPTDemodSettings::setRgbColor(qint32 rgb_color) { - this->rgb_color = rgb_color; - this->m_rgb_color_isSet = true; -} - -QString* -SWGLRPTDemodSettings::getTitle() { - return title; -} -void -SWGLRPTDemodSettings::setTitle(QString* title) { - this->title = title; - this->m_title_isSet = true; -} - -qint32 -SWGLRPTDemodSettings::getStreamIndex() { - return stream_index; -} -void -SWGLRPTDemodSettings::setStreamIndex(qint32 stream_index) { - this->stream_index = stream_index; - this->m_stream_index_isSet = true; -} - -qint32 -SWGLRPTDemodSettings::getUseReverseApi() { - return use_reverse_api; -} -void -SWGLRPTDemodSettings::setUseReverseApi(qint32 use_reverse_api) { - this->use_reverse_api = use_reverse_api; - this->m_use_reverse_api_isSet = true; -} - -QString* -SWGLRPTDemodSettings::getReverseApiAddress() { - return reverse_api_address; -} -void -SWGLRPTDemodSettings::setReverseApiAddress(QString* reverse_api_address) { - this->reverse_api_address = reverse_api_address; - this->m_reverse_api_address_isSet = true; -} - -qint32 -SWGLRPTDemodSettings::getReverseApiPort() { - return reverse_api_port; -} -void -SWGLRPTDemodSettings::setReverseApiPort(qint32 reverse_api_port) { - this->reverse_api_port = reverse_api_port; - this->m_reverse_api_port_isSet = true; -} - -qint32 -SWGLRPTDemodSettings::getReverseApiDeviceIndex() { - return reverse_api_device_index; -} -void -SWGLRPTDemodSettings::setReverseApiDeviceIndex(qint32 reverse_api_device_index) { - this->reverse_api_device_index = reverse_api_device_index; - this->m_reverse_api_device_index_isSet = true; -} - -qint32 -SWGLRPTDemodSettings::getReverseApiChannelIndex() { - return reverse_api_channel_index; -} -void -SWGLRPTDemodSettings::setReverseApiChannelIndex(qint32 reverse_api_channel_index) { - this->reverse_api_channel_index = reverse_api_channel_index; - this->m_reverse_api_channel_index_isSet = true; -} - - -bool -SWGLRPTDemodSettings::isSet(){ - bool isObjectUpdated = false; - do{ - if(m_input_frequency_offset_isSet){ - isObjectUpdated = true; break; - } - if(m_rf_bandwidth_isSet){ - isObjectUpdated = true; break; - } - if(m_fm_deviation_isSet){ - isObjectUpdated = true; break; - } - if(m_crop_noise_isSet){ - isObjectUpdated = true; break; - } - if(m_denoise_isSet){ - isObjectUpdated = true; break; - } - if(m_linear_equalise_isSet){ - isObjectUpdated = true; break; - } - if(m_histogram_equalise_isSet){ - isObjectUpdated = true; break; - } - if(m_precipitation_overlay_isSet){ - isObjectUpdated = true; break; - } - if(m_flip_isSet){ - isObjectUpdated = true; break; - } - if(m_channels_isSet){ - isObjectUpdated = true; break; - } - if(m_decode_enabled_isSet){ - isObjectUpdated = true; break; - } - if(m_auto_save_isSet){ - isObjectUpdated = true; break; - } - if(auto_save_path && *auto_save_path != QString("")){ - isObjectUpdated = true; break; - } - if(m_auto_save_min_scan_lines_isSet){ - isObjectUpdated = true; break; - } - if(m_rgb_color_isSet){ - isObjectUpdated = true; break; - } - if(title && *title != QString("")){ - isObjectUpdated = true; break; - } - if(m_stream_index_isSet){ - isObjectUpdated = true; break; - } - if(m_use_reverse_api_isSet){ - isObjectUpdated = true; break; - } - if(reverse_api_address && *reverse_api_address != QString("")){ - isObjectUpdated = true; break; - } - if(m_reverse_api_port_isSet){ - isObjectUpdated = true; break; - } - if(m_reverse_api_device_index_isSet){ - isObjectUpdated = true; break; - } - if(m_reverse_api_channel_index_isSet){ - isObjectUpdated = true; break; - } - }while(false); - return isObjectUpdated; -} -} - diff --git a/swagger/sdrangel/code/qt5/client/SWGLRPTDemodSettings.h b/swagger/sdrangel/code/qt5/client/SWGLRPTDemodSettings.h deleted file mode 100644 index 474759372..000000000 --- a/swagger/sdrangel/code/qt5/client/SWGLRPTDemodSettings.h +++ /dev/null @@ -1,185 +0,0 @@ -/** - * SDRangel - * This is the web REST/JSON API of SDRangel SDR software. SDRangel is an Open Source Qt5/OpenGL 3.0+ (4.3+ in Windows) GUI and server Software Defined Radio and signal analyzer in software. It supports Airspy, BladeRF, HackRF, LimeSDR, PlutoSDR, RTL-SDR, SDRplay RSP1 and FunCube --- Limitations and specifcities: * In SDRangel GUI the first Rx device set cannot be deleted. Conversely the server starts with no device sets and its number of device sets can be reduced to zero by as many calls as necessary to /sdrangel/deviceset with DELETE method. * Preset import and export from/to file is a server only feature. * Device set focus is a GUI only feature. * The following channels are not implemented (status 501 is returned): ATV and DATV demodulators, Channel Analyzer NG, LoRa demodulator * The device settings and report structures contains only the sub-structure corresponding to the device type. The DeviceSettings and DeviceReport structures documented here shows all of them but only one will be or should be present at a time * The channel settings and report structures contains only the sub-structure corresponding to the channel type. The ChannelSettings and ChannelReport structures documented here shows all of them but only one will be or should be present at a time --- - * - * OpenAPI spec version: 6.0.0 - * Contact: f4exb06@gmail.com - * - * NOTE: This class is auto generated by the swagger code generator program. - * https://github.com/swagger-api/swagger-codegen.git - * Do not edit the class manually. - */ - -/* - * SWGLRPTDemodSettings.h - * - * LRPTDemod - */ - -#ifndef SWGLRPTDemodSettings_H_ -#define SWGLRPTDemodSettings_H_ - -#include - - -#include - -#include "SWGObject.h" -#include "export.h" - -namespace SWGSDRangel { - -class SWG_API SWGLRPTDemodSettings: public SWGObject { -public: - SWGLRPTDemodSettings(); - SWGLRPTDemodSettings(QString* json); - virtual ~SWGLRPTDemodSettings(); - void init(); - void cleanup(); - - virtual QString asJson () override; - virtual QJsonObject* asJsonObject() override; - virtual void fromJsonObject(QJsonObject &json) override; - virtual SWGLRPTDemodSettings* fromJson(QString &jsonString) override; - - qint64 getInputFrequencyOffset(); - void setInputFrequencyOffset(qint64 input_frequency_offset); - - float getRfBandwidth(); - void setRfBandwidth(float rf_bandwidth); - - qint32 getFmDeviation(); - void setFmDeviation(qint32 fm_deviation); - - qint32 getCropNoise(); - void setCropNoise(qint32 crop_noise); - - qint32 getDenoise(); - void setDenoise(qint32 denoise); - - qint32 getLinearEqualise(); - void setLinearEqualise(qint32 linear_equalise); - - qint32 getHistogramEqualise(); - void setHistogramEqualise(qint32 histogram_equalise); - - qint32 getPrecipitationOverlay(); - void setPrecipitationOverlay(qint32 precipitation_overlay); - - qint32 getFlip(); - void setFlip(qint32 flip); - - qint32 getChannels(); - void setChannels(qint32 channels); - - qint32 getDecodeEnabled(); - void setDecodeEnabled(qint32 decode_enabled); - - qint32 getAutoSave(); - void setAutoSave(qint32 auto_save); - - QString* getAutoSavePath(); - void setAutoSavePath(QString* auto_save_path); - - qint32 getAutoSaveMinScanLines(); - void setAutoSaveMinScanLines(qint32 auto_save_min_scan_lines); - - qint32 getRgbColor(); - void setRgbColor(qint32 rgb_color); - - QString* getTitle(); - void setTitle(QString* title); - - qint32 getStreamIndex(); - void setStreamIndex(qint32 stream_index); - - qint32 getUseReverseApi(); - void setUseReverseApi(qint32 use_reverse_api); - - QString* getReverseApiAddress(); - void setReverseApiAddress(QString* reverse_api_address); - - qint32 getReverseApiPort(); - void setReverseApiPort(qint32 reverse_api_port); - - qint32 getReverseApiDeviceIndex(); - void setReverseApiDeviceIndex(qint32 reverse_api_device_index); - - qint32 getReverseApiChannelIndex(); - void setReverseApiChannelIndex(qint32 reverse_api_channel_index); - - - virtual bool isSet() override; - -private: - qint64 input_frequency_offset; - bool m_input_frequency_offset_isSet; - - float rf_bandwidth; - bool m_rf_bandwidth_isSet; - - qint32 fm_deviation; - bool m_fm_deviation_isSet; - - qint32 crop_noise; - bool m_crop_noise_isSet; - - qint32 denoise; - bool m_denoise_isSet; - - qint32 linear_equalise; - bool m_linear_equalise_isSet; - - qint32 histogram_equalise; - bool m_histogram_equalise_isSet; - - qint32 precipitation_overlay; - bool m_precipitation_overlay_isSet; - - qint32 flip; - bool m_flip_isSet; - - qint32 channels; - bool m_channels_isSet; - - qint32 decode_enabled; - bool m_decode_enabled_isSet; - - qint32 auto_save; - bool m_auto_save_isSet; - - QString* auto_save_path; - bool m_auto_save_path_isSet; - - qint32 auto_save_min_scan_lines; - bool m_auto_save_min_scan_lines_isSet; - - qint32 rgb_color; - bool m_rgb_color_isSet; - - QString* title; - bool m_title_isSet; - - qint32 stream_index; - bool m_stream_index_isSet; - - qint32 use_reverse_api; - bool m_use_reverse_api_isSet; - - QString* reverse_api_address; - bool m_reverse_api_address_isSet; - - qint32 reverse_api_port; - bool m_reverse_api_port_isSet; - - qint32 reverse_api_device_index; - bool m_reverse_api_device_index_isSet; - - qint32 reverse_api_channel_index; - bool m_reverse_api_channel_index_isSet; - -}; - -} - -#endif /* SWGLRPTDemodSettings_H_ */ diff --git a/swagger/sdrangel/code/qt5/client/SWGMapActions_find.cpp b/swagger/sdrangel/code/qt5/client/SWGMapActions_find.cpp deleted file mode 100644 index 0e9607141..000000000 --- a/swagger/sdrangel/code/qt5/client/SWGMapActions_find.cpp +++ /dev/null @@ -1,110 +0,0 @@ -/** - * SDRangel - * This is the web REST/JSON API of SDRangel SDR software. SDRangel is an Open Source Qt5/OpenGL 3.0+ (4.3+ in Windows) GUI and server Software Defined Radio and signal analyzer in software. It supports Airspy, BladeRF, HackRF, LimeSDR, PlutoSDR, RTL-SDR, SDRplay RSP1 and FunCube --- Limitations and specifcities: * In SDRangel GUI the first Rx device set cannot be deleted. Conversely the server starts with no device sets and its number of device sets can be reduced to zero by as many calls as necessary to /sdrangel/deviceset with DELETE method. * Preset import and export from/to file is a server only feature. * Device set focus is a GUI only feature. * The following channels are not implemented (status 501 is returned): ATV and DATV demodulators, Channel Analyzer NG, LoRa demodulator * The device settings and report structures contains only the sub-structure corresponding to the device type. The DeviceSettings and DeviceReport structures documented here shows all of them but only one will be or should be present at a time * The channel settings and report structures contains only the sub-structure corresponding to the channel type. The ChannelSettings and ChannelReport structures documented here shows all of them but only one will be or should be present at a time --- - * - * OpenAPI spec version: 6.0.0 - * Contact: f4exb06@gmail.com - * - * NOTE: This class is auto generated by the swagger code generator program. - * https://github.com/swagger-api/swagger-codegen.git - * Do not edit the class manually. - */ - - -#include "SWGMapActions_find.h" - -#include "SWGHelpers.h" - -#include -#include -#include -#include - -namespace SWGSDRangel { - -SWGMapActions_find::SWGMapActions_find(QString* json) { - init(); - this->fromJson(*json); -} - -SWGMapActions_find::SWGMapActions_find() { - id = nullptr; - m_id_isSet = false; -} - -SWGMapActions_find::~SWGMapActions_find() { - this->cleanup(); -} - -void -SWGMapActions_find::init() { - id = new QString(""); - m_id_isSet = false; -} - -void -SWGMapActions_find::cleanup() { - if(id != nullptr) { - delete id; - } -} - -SWGMapActions_find* -SWGMapActions_find::fromJson(QString &json) { - QByteArray array (json.toStdString().c_str()); - QJsonDocument doc = QJsonDocument::fromJson(array); - QJsonObject jsonObject = doc.object(); - this->fromJsonObject(jsonObject); - return this; -} - -void -SWGMapActions_find::fromJsonObject(QJsonObject &pJson) { - ::SWGSDRangel::setValue(&id, pJson["id"], "QString", "QString"); - -} - -QString -SWGMapActions_find::asJson () -{ - QJsonObject* obj = this->asJsonObject(); - - QJsonDocument doc(*obj); - QByteArray bytes = doc.toJson(); - delete obj; - return QString(bytes); -} - -QJsonObject* -SWGMapActions_find::asJsonObject() { - QJsonObject* obj = new QJsonObject(); - if(id != nullptr && *id != QString("")){ - toJsonValue(QString("id"), id, obj, QString("QString")); - } - - return obj; -} - -QString* -SWGMapActions_find::getId() { - return id; -} -void -SWGMapActions_find::setId(QString* id) { - this->id = id; - this->m_id_isSet = true; -} - - -bool -SWGMapActions_find::isSet(){ - bool isObjectUpdated = false; - do{ - if(id && *id != QString("")){ - isObjectUpdated = true; break; - } - }while(false); - return isObjectUpdated; -} -} - diff --git a/swagger/sdrangel/code/qt5/client/SWGMapActions_find.h b/swagger/sdrangel/code/qt5/client/SWGMapActions_find.h deleted file mode 100644 index cce1988c8..000000000 --- a/swagger/sdrangel/code/qt5/client/SWGMapActions_find.h +++ /dev/null @@ -1,59 +0,0 @@ -/** - * SDRangel - * This is the web REST/JSON API of SDRangel SDR software. SDRangel is an Open Source Qt5/OpenGL 3.0+ (4.3+ in Windows) GUI and server Software Defined Radio and signal analyzer in software. It supports Airspy, BladeRF, HackRF, LimeSDR, PlutoSDR, RTL-SDR, SDRplay RSP1 and FunCube --- Limitations and specifcities: * In SDRangel GUI the first Rx device set cannot be deleted. Conversely the server starts with no device sets and its number of device sets can be reduced to zero by as many calls as necessary to /sdrangel/deviceset with DELETE method. * Preset import and export from/to file is a server only feature. * Device set focus is a GUI only feature. * The following channels are not implemented (status 501 is returned): ATV and DATV demodulators, Channel Analyzer NG, LoRa demodulator * The device settings and report structures contains only the sub-structure corresponding to the device type. The DeviceSettings and DeviceReport structures documented here shows all of them but only one will be or should be present at a time * The channel settings and report structures contains only the sub-structure corresponding to the channel type. The ChannelSettings and ChannelReport structures documented here shows all of them but only one will be or should be present at a time --- - * - * OpenAPI spec version: 6.0.0 - * Contact: f4exb06@gmail.com - * - * NOTE: This class is auto generated by the swagger code generator program. - * https://github.com/swagger-api/swagger-codegen.git - * Do not edit the class manually. - */ - -/* - * SWGMapActions_find.h - * - * A request to centre the map on an item. - */ - -#ifndef SWGMapActions_find_H_ -#define SWGMapActions_find_H_ - -#include - - -#include - -#include "SWGObject.h" -#include "export.h" - -namespace SWGSDRangel { - -class SWG_API SWGMapActions_find: public SWGObject { -public: - SWGMapActions_find(); - SWGMapActions_find(QString* json); - virtual ~SWGMapActions_find(); - void init(); - void cleanup(); - - virtual QString asJson () override; - virtual QJsonObject* asJsonObject() override; - virtual void fromJsonObject(QJsonObject &json) override; - virtual SWGMapActions_find* fromJson(QString &jsonString) override; - - QString* getId(); - void setId(QString* id); - - - virtual bool isSet() override; - -private: - QString* id; - bool m_id_isSet; - -}; - -} - -#endif /* SWGMapActions_find_H_ */ diff --git a/swagger/sdrangel/code/qt5/client/SWGMapItem_track.cpp b/swagger/sdrangel/code/qt5/client/SWGMapItem_track.cpp deleted file mode 100644 index 589a18c57..000000000 --- a/swagger/sdrangel/code/qt5/client/SWGMapItem_track.cpp +++ /dev/null @@ -1,154 +0,0 @@ -/** - * SDRangel - * This is the web REST/JSON API of SDRangel SDR software. SDRangel is an Open Source Qt5/OpenGL 3.0+ (4.3+ in Windows) GUI and server Software Defined Radio and signal analyzer in software. It supports Airspy, BladeRF, HackRF, LimeSDR, PlutoSDR, RTL-SDR, SDRplay RSP1 and FunCube --- Limitations and specifcities: * In SDRangel GUI the first Rx device set cannot be deleted. Conversely the server starts with no device sets and its number of device sets can be reduced to zero by as many calls as necessary to /sdrangel/deviceset with DELETE method. * Preset import and export from/to file is a server only feature. * Device set focus is a GUI only feature. * The following channels are not implemented (status 501 is returned): ATV and DATV demodulators, Channel Analyzer NG, LoRa demodulator * The device settings and report structures contains only the sub-structure corresponding to the device type. The DeviceSettings and DeviceReport structures documented here shows all of them but only one will be or should be present at a time * The channel settings and report structures contains only the sub-structure corresponding to the channel type. The ChannelSettings and ChannelReport structures documented here shows all of them but only one will be or should be present at a time --- - * - * OpenAPI spec version: 6.0.0 - * Contact: f4exb06@gmail.com - * - * NOTE: This class is auto generated by the swagger code generator program. - * https://github.com/swagger-api/swagger-codegen.git - * Do not edit the class manually. - */ - - -#include "SWGMapItem_track.h" - -#include "SWGHelpers.h" - -#include -#include -#include -#include - -namespace SWGSDRangel { - -SWGMapItem_track::SWGMapItem_track(QString* json) { - init(); - this->fromJson(*json); -} - -SWGMapItem_track::SWGMapItem_track() { - latitude = 0.0f; - m_latitude_isSet = false; - longitude = 0.0f; - m_longitude_isSet = false; - altitude = 0.0f; - m_altitude_isSet = false; -} - -SWGMapItem_track::~SWGMapItem_track() { - this->cleanup(); -} - -void -SWGMapItem_track::init() { - latitude = 0.0f; - m_latitude_isSet = false; - longitude = 0.0f; - m_longitude_isSet = false; - altitude = 0.0f; - m_altitude_isSet = false; -} - -void -SWGMapItem_track::cleanup() { - - - -} - -SWGMapItem_track* -SWGMapItem_track::fromJson(QString &json) { - QByteArray array (json.toStdString().c_str()); - QJsonDocument doc = QJsonDocument::fromJson(array); - QJsonObject jsonObject = doc.object(); - this->fromJsonObject(jsonObject); - return this; -} - -void -SWGMapItem_track::fromJsonObject(QJsonObject &pJson) { - ::SWGSDRangel::setValue(&latitude, pJson["latitude"], "float", ""); - - ::SWGSDRangel::setValue(&longitude, pJson["longitude"], "float", ""); - - ::SWGSDRangel::setValue(&altitude, pJson["altitude"], "float", ""); - -} - -QString -SWGMapItem_track::asJson () -{ - QJsonObject* obj = this->asJsonObject(); - - QJsonDocument doc(*obj); - QByteArray bytes = doc.toJson(); - delete obj; - return QString(bytes); -} - -QJsonObject* -SWGMapItem_track::asJsonObject() { - QJsonObject* obj = new QJsonObject(); - if(m_latitude_isSet){ - obj->insert("latitude", QJsonValue(latitude)); - } - if(m_longitude_isSet){ - obj->insert("longitude", QJsonValue(longitude)); - } - if(m_altitude_isSet){ - obj->insert("altitude", QJsonValue(altitude)); - } - - return obj; -} - -float -SWGMapItem_track::getLatitude() { - return latitude; -} -void -SWGMapItem_track::setLatitude(float latitude) { - this->latitude = latitude; - this->m_latitude_isSet = true; -} - -float -SWGMapItem_track::getLongitude() { - return longitude; -} -void -SWGMapItem_track::setLongitude(float longitude) { - this->longitude = longitude; - this->m_longitude_isSet = true; -} - -float -SWGMapItem_track::getAltitude() { - return altitude; -} -void -SWGMapItem_track::setAltitude(float altitude) { - this->altitude = altitude; - this->m_altitude_isSet = true; -} - - -bool -SWGMapItem_track::isSet(){ - bool isObjectUpdated = false; - do{ - if(m_latitude_isSet){ - isObjectUpdated = true; break; - } - if(m_longitude_isSet){ - isObjectUpdated = true; break; - } - if(m_altitude_isSet){ - isObjectUpdated = true; break; - } - }while(false); - return isObjectUpdated; -} -} - diff --git a/swagger/sdrangel/code/qt5/client/SWGMapItem_track.h b/swagger/sdrangel/code/qt5/client/SWGMapItem_track.h deleted file mode 100644 index ec738bd51..000000000 --- a/swagger/sdrangel/code/qt5/client/SWGMapItem_track.h +++ /dev/null @@ -1,70 +0,0 @@ -/** - * SDRangel - * This is the web REST/JSON API of SDRangel SDR software. SDRangel is an Open Source Qt5/OpenGL 3.0+ (4.3+ in Windows) GUI and server Software Defined Radio and signal analyzer in software. It supports Airspy, BladeRF, HackRF, LimeSDR, PlutoSDR, RTL-SDR, SDRplay RSP1 and FunCube --- Limitations and specifcities: * In SDRangel GUI the first Rx device set cannot be deleted. Conversely the server starts with no device sets and its number of device sets can be reduced to zero by as many calls as necessary to /sdrangel/deviceset with DELETE method. * Preset import and export from/to file is a server only feature. * Device set focus is a GUI only feature. * The following channels are not implemented (status 501 is returned): ATV and DATV demodulators, Channel Analyzer NG, LoRa demodulator * The device settings and report structures contains only the sub-structure corresponding to the device type. The DeviceSettings and DeviceReport structures documented here shows all of them but only one will be or should be present at a time * The channel settings and report structures contains only the sub-structure corresponding to the channel type. The ChannelSettings and ChannelReport structures documented here shows all of them but only one will be or should be present at a time --- - * - * OpenAPI spec version: 6.0.0 - * Contact: f4exb06@gmail.com - * - * NOTE: This class is auto generated by the swagger code generator program. - * https://github.com/swagger-api/swagger-codegen.git - * Do not edit the class manually. - */ - -/* - * SWGMapItem_track.h - * - * - */ - -#ifndef SWGMapItem_track_H_ -#define SWGMapItem_track_H_ - -#include - - - -#include "SWGObject.h" -#include "export.h" - -namespace SWGSDRangel { - -class SWG_API SWGMapItem_track: public SWGObject { -public: - SWGMapItem_track(); - SWGMapItem_track(QString* json); - virtual ~SWGMapItem_track(); - void init(); - void cleanup(); - - virtual QString asJson () override; - virtual QJsonObject* asJsonObject() override; - virtual void fromJsonObject(QJsonObject &json) override; - virtual SWGMapItem_track* fromJson(QString &jsonString) override; - - float getLatitude(); - void setLatitude(float latitude); - - float getLongitude(); - void setLongitude(float longitude); - - float getAltitude(); - void setAltitude(float altitude); - - - virtual bool isSet() override; - -private: - float latitude; - bool m_latitude_isSet; - - float longitude; - bool m_longitude_isSet; - - float altitude; - bool m_altitude_isSet; - -}; - -} - -#endif /* SWGMapItem_track_H_ */ diff --git a/swagger/sdrangel/code/qt5/client/SWGModelFactory.h b/swagger/sdrangel/code/qt5/client/SWGModelFactory.h index 6ff46686e..6594f5cff 100644 --- a/swagger/sdrangel/code/qt5/client/SWGModelFactory.h +++ b/swagger/sdrangel/code/qt5/client/SWGModelFactory.h @@ -111,7 +111,6 @@ #include "SWGFeaturePresets.h" #include "SWGFeatureReport.h" #include "SWGFeatureSet.h" -#include "SWGFeatureSetList.h" #include "SWGFeatureSetPreset.h" #include "SWGFeatureSettings.h" #include "SWGFileInputReport.h" @@ -806,11 +805,6 @@ namespace SWGSDRangel { obj->init(); return obj; } - if(QString("SWGFeatureSetList").compare(type) == 0) { - SWGFeatureSetList *obj = new SWGFeatureSetList(); - obj->init(); - return obj; - } if(QString("SWGFeatureSetPreset").compare(type) == 0) { SWGFeatureSetPreset *obj = new SWGFeatureSetPreset(); obj->init(); diff --git a/swagger/sdrangel/code/qt5/client/SWGPacketModActions_tx.cpp b/swagger/sdrangel/code/qt5/client/SWGPacketModActions_tx.cpp deleted file mode 100644 index 46d09fef3..000000000 --- a/swagger/sdrangel/code/qt5/client/SWGPacketModActions_tx.cpp +++ /dev/null @@ -1,185 +0,0 @@ -/** - * SDRangel - * This is the web REST/JSON API of SDRangel SDR software. SDRangel is an Open Source Qt5/OpenGL 3.0+ (4.3+ in Windows) GUI and server Software Defined Radio and signal analyzer in software. It supports Airspy, BladeRF, HackRF, LimeSDR, PlutoSDR, RTL-SDR, SDRplay RSP1 and FunCube --- Limitations and specifcities: * In SDRangel GUI the first Rx device set cannot be deleted. Conversely the server starts with no device sets and its number of device sets can be reduced to zero by as many calls as necessary to /sdrangel/deviceset with DELETE method. * Preset import and export from/to file is a server only feature. * Device set focus is a GUI only feature. * The following channels are not implemented (status 501 is returned): ATV and DATV demodulators, Channel Analyzer NG, LoRa demodulator * The device settings and report structures contains only the sub-structure corresponding to the device type. The DeviceSettings and DeviceReport structures documented here shows all of them but only one will be or should be present at a time * The channel settings and report structures contains only the sub-structure corresponding to the channel type. The ChannelSettings and ChannelReport structures documented here shows all of them but only one will be or should be present at a time --- - * - * OpenAPI spec version: 6.0.0 - * Contact: f4exb06@gmail.com - * - * NOTE: This class is auto generated by the swagger code generator program. - * https://github.com/swagger-api/swagger-codegen.git - * Do not edit the class manually. - */ - - -#include "SWGPacketModActions_tx.h" - -#include "SWGHelpers.h" - -#include -#include -#include -#include - -namespace SWGSDRangel { - -SWGPacketModActions_tx::SWGPacketModActions_tx(QString* json) { - init(); - this->fromJson(*json); -} - -SWGPacketModActions_tx::SWGPacketModActions_tx() { - callsign = nullptr; - m_callsign_isSet = false; - to = nullptr; - m_to_isSet = false; - via = nullptr; - m_via_isSet = false; - data = nullptr; - m_data_isSet = false; -} - -SWGPacketModActions_tx::~SWGPacketModActions_tx() { - this->cleanup(); -} - -void -SWGPacketModActions_tx::init() { - callsign = new QString(""); - m_callsign_isSet = false; - to = new QString(""); - m_to_isSet = false; - via = new QString(""); - m_via_isSet = false; - data = new QString(""); - m_data_isSet = false; -} - -void -SWGPacketModActions_tx::cleanup() { - if(callsign != nullptr) { - delete callsign; - } - if(to != nullptr) { - delete to; - } - if(via != nullptr) { - delete via; - } - if(data != nullptr) { - delete data; - } -} - -SWGPacketModActions_tx* -SWGPacketModActions_tx::fromJson(QString &json) { - QByteArray array (json.toStdString().c_str()); - QJsonDocument doc = QJsonDocument::fromJson(array); - QJsonObject jsonObject = doc.object(); - this->fromJsonObject(jsonObject); - return this; -} - -void -SWGPacketModActions_tx::fromJsonObject(QJsonObject &pJson) { - ::SWGSDRangel::setValue(&callsign, pJson["callsign"], "QString", "QString"); - - ::SWGSDRangel::setValue(&to, pJson["to"], "QString", "QString"); - - ::SWGSDRangel::setValue(&via, pJson["via"], "QString", "QString"); - - ::SWGSDRangel::setValue(&data, pJson["data"], "QString", "QString"); - -} - -QString -SWGPacketModActions_tx::asJson () -{ - QJsonObject* obj = this->asJsonObject(); - - QJsonDocument doc(*obj); - QByteArray bytes = doc.toJson(); - delete obj; - return QString(bytes); -} - -QJsonObject* -SWGPacketModActions_tx::asJsonObject() { - QJsonObject* obj = new QJsonObject(); - if(callsign != nullptr && *callsign != QString("")){ - toJsonValue(QString("callsign"), callsign, obj, QString("QString")); - } - if(to != nullptr && *to != QString("")){ - toJsonValue(QString("to"), to, obj, QString("QString")); - } - if(via != nullptr && *via != QString("")){ - toJsonValue(QString("via"), via, obj, QString("QString")); - } - if(data != nullptr && *data != QString("")){ - toJsonValue(QString("data"), data, obj, QString("QString")); - } - - return obj; -} - -QString* -SWGPacketModActions_tx::getCallsign() { - return callsign; -} -void -SWGPacketModActions_tx::setCallsign(QString* callsign) { - this->callsign = callsign; - this->m_callsign_isSet = true; -} - -QString* -SWGPacketModActions_tx::getTo() { - return to; -} -void -SWGPacketModActions_tx::setTo(QString* to) { - this->to = to; - this->m_to_isSet = true; -} - -QString* -SWGPacketModActions_tx::getVia() { - return via; -} -void -SWGPacketModActions_tx::setVia(QString* via) { - this->via = via; - this->m_via_isSet = true; -} - -QString* -SWGPacketModActions_tx::getData() { - return data; -} -void -SWGPacketModActions_tx::setData(QString* data) { - this->data = data; - this->m_data_isSet = true; -} - - -bool -SWGPacketModActions_tx::isSet(){ - bool isObjectUpdated = false; - do{ - if(callsign && *callsign != QString("")){ - isObjectUpdated = true; break; - } - if(to && *to != QString("")){ - isObjectUpdated = true; break; - } - if(via && *via != QString("")){ - isObjectUpdated = true; break; - } - if(data && *data != QString("")){ - isObjectUpdated = true; break; - } - }while(false); - return isObjectUpdated; -} -} - diff --git a/swagger/sdrangel/code/qt5/client/SWGPacketModActions_tx.h b/swagger/sdrangel/code/qt5/client/SWGPacketModActions_tx.h deleted file mode 100644 index f29149ddf..000000000 --- a/swagger/sdrangel/code/qt5/client/SWGPacketModActions_tx.h +++ /dev/null @@ -1,77 +0,0 @@ -/** - * SDRangel - * This is the web REST/JSON API of SDRangel SDR software. SDRangel is an Open Source Qt5/OpenGL 3.0+ (4.3+ in Windows) GUI and server Software Defined Radio and signal analyzer in software. It supports Airspy, BladeRF, HackRF, LimeSDR, PlutoSDR, RTL-SDR, SDRplay RSP1 and FunCube --- Limitations and specifcities: * In SDRangel GUI the first Rx device set cannot be deleted. Conversely the server starts with no device sets and its number of device sets can be reduced to zero by as many calls as necessary to /sdrangel/deviceset with DELETE method. * Preset import and export from/to file is a server only feature. * Device set focus is a GUI only feature. * The following channels are not implemented (status 501 is returned): ATV and DATV demodulators, Channel Analyzer NG, LoRa demodulator * The device settings and report structures contains only the sub-structure corresponding to the device type. The DeviceSettings and DeviceReport structures documented here shows all of them but only one will be or should be present at a time * The channel settings and report structures contains only the sub-structure corresponding to the channel type. The ChannelSettings and ChannelReport structures documented here shows all of them but only one will be or should be present at a time --- - * - * OpenAPI spec version: 6.0.0 - * Contact: f4exb06@gmail.com - * - * NOTE: This class is auto generated by the swagger code generator program. - * https://github.com/swagger-api/swagger-codegen.git - * Do not edit the class manually. - */ - -/* - * SWGPacketModActions_tx.h - * - * Transmit a packet - */ - -#ifndef SWGPacketModActions_tx_H_ -#define SWGPacketModActions_tx_H_ - -#include - - -#include - -#include "SWGObject.h" -#include "export.h" - -namespace SWGSDRangel { - -class SWG_API SWGPacketModActions_tx: public SWGObject { -public: - SWGPacketModActions_tx(); - SWGPacketModActions_tx(QString* json); - virtual ~SWGPacketModActions_tx(); - void init(); - void cleanup(); - - virtual QString asJson () override; - virtual QJsonObject* asJsonObject() override; - virtual void fromJsonObject(QJsonObject &json) override; - virtual SWGPacketModActions_tx* fromJson(QString &jsonString) override; - - QString* getCallsign(); - void setCallsign(QString* callsign); - - QString* getTo(); - void setTo(QString* to); - - QString* getVia(); - void setVia(QString* via); - - QString* getData(); - void setData(QString* data); - - - virtual bool isSet() override; - -private: - QString* callsign; - bool m_callsign_isSet; - - QString* to; - bool m_to_isSet; - - QString* via; - bool m_via_isSet; - - QString* data; - bool m_data_isSet; - -}; - -} - -#endif /* SWGPacketModActions_tx_H_ */ diff --git a/swagger/sdrangel/code/qt5/client/SWGVORLocalizerSettings_vorDemodChannels.cpp b/swagger/sdrangel/code/qt5/client/SWGVORLocalizerSettings_vorDemodChannels.cpp deleted file mode 100644 index 4004d5ecb..000000000 --- a/swagger/sdrangel/code/qt5/client/SWGVORLocalizerSettings_vorDemodChannels.cpp +++ /dev/null @@ -1,131 +0,0 @@ -/** - * SDRangel - * This is the web REST/JSON API of SDRangel SDR software. SDRangel is an Open Source Qt5/OpenGL 3.0+ (4.3+ in Windows) GUI and server Software Defined Radio and signal analyzer in software. It supports Airspy, BladeRF, HackRF, LimeSDR, PlutoSDR, RTL-SDR, SDRplay RSP1 and FunCube --- Limitations and specifcities: * In SDRangel GUI the first Rx device set cannot be deleted. Conversely the server starts with no device sets and its number of device sets can be reduced to zero by as many calls as necessary to /sdrangel/deviceset with DELETE method. * Preset import and export from/to file is a server only feature. * Device set focus is a GUI only feature. * The following channels are not implemented (status 501 is returned): ATV and DATV demodulators, Channel Analyzer NG, LoRa demodulator * The device settings and report structures contains only the sub-structure corresponding to the device type. The DeviceSettings and DeviceReport structures documented here shows all of them but only one will be or should be present at a time * The channel settings and report structures contains only the sub-structure corresponding to the channel type. The ChannelSettings and ChannelReport structures documented here shows all of them but only one will be or should be present at a time --- - * - * OpenAPI spec version: 6.0.0 - * Contact: f4exb06@gmail.com - * - * NOTE: This class is auto generated by the swagger code generator program. - * https://github.com/swagger-api/swagger-codegen.git - * Do not edit the class manually. - */ - - -#include "SWGVORLocalizerSettings_vorDemodChannels.h" - -#include "SWGHelpers.h" - -#include -#include -#include -#include - -namespace SWGSDRangel { - -SWGVORLocalizerSettings_vorDemodChannels::SWGVORLocalizerSettings_vorDemodChannels(QString* json) { - init(); - this->fromJson(*json); -} - -SWGVORLocalizerSettings_vorDemodChannels::SWGVORLocalizerSettings_vorDemodChannels() { - device_set_index = 0; - m_device_set_index_isSet = false; - channel_index = 0; - m_channel_index_isSet = false; -} - -SWGVORLocalizerSettings_vorDemodChannels::~SWGVORLocalizerSettings_vorDemodChannels() { - this->cleanup(); -} - -void -SWGVORLocalizerSettings_vorDemodChannels::init() { - device_set_index = 0; - m_device_set_index_isSet = false; - channel_index = 0; - m_channel_index_isSet = false; -} - -void -SWGVORLocalizerSettings_vorDemodChannels::cleanup() { - - -} - -SWGVORLocalizerSettings_vorDemodChannels* -SWGVORLocalizerSettings_vorDemodChannels::fromJson(QString &json) { - QByteArray array (json.toStdString().c_str()); - QJsonDocument doc = QJsonDocument::fromJson(array); - QJsonObject jsonObject = doc.object(); - this->fromJsonObject(jsonObject); - return this; -} - -void -SWGVORLocalizerSettings_vorDemodChannels::fromJsonObject(QJsonObject &pJson) { - ::SWGSDRangel::setValue(&device_set_index, pJson["deviceSetIndex"], "qint32", ""); - - ::SWGSDRangel::setValue(&channel_index, pJson["channelIndex"], "qint32", ""); - -} - -QString -SWGVORLocalizerSettings_vorDemodChannels::asJson () -{ - QJsonObject* obj = this->asJsonObject(); - - QJsonDocument doc(*obj); - QByteArray bytes = doc.toJson(); - delete obj; - return QString(bytes); -} - -QJsonObject* -SWGVORLocalizerSettings_vorDemodChannels::asJsonObject() { - QJsonObject* obj = new QJsonObject(); - if(m_device_set_index_isSet){ - obj->insert("deviceSetIndex", QJsonValue(device_set_index)); - } - if(m_channel_index_isSet){ - obj->insert("channelIndex", QJsonValue(channel_index)); - } - - return obj; -} - -qint32 -SWGVORLocalizerSettings_vorDemodChannels::getDeviceSetIndex() { - return device_set_index; -} -void -SWGVORLocalizerSettings_vorDemodChannels::setDeviceSetIndex(qint32 device_set_index) { - this->device_set_index = device_set_index; - this->m_device_set_index_isSet = true; -} - -qint32 -SWGVORLocalizerSettings_vorDemodChannels::getChannelIndex() { - return channel_index; -} -void -SWGVORLocalizerSettings_vorDemodChannels::setChannelIndex(qint32 channel_index) { - this->channel_index = channel_index; - this->m_channel_index_isSet = true; -} - - -bool -SWGVORLocalizerSettings_vorDemodChannels::isSet(){ - bool isObjectUpdated = false; - do{ - if(m_device_set_index_isSet){ - isObjectUpdated = true; break; - } - if(m_channel_index_isSet){ - isObjectUpdated = true; break; - } - }while(false); - return isObjectUpdated; -} -} - diff --git a/swagger/sdrangel/code/qt5/client/SWGVORLocalizerSettings_vorDemodChannels.h b/swagger/sdrangel/code/qt5/client/SWGVORLocalizerSettings_vorDemodChannels.h deleted file mode 100644 index 3416ee3a4..000000000 --- a/swagger/sdrangel/code/qt5/client/SWGVORLocalizerSettings_vorDemodChannels.h +++ /dev/null @@ -1,64 +0,0 @@ -/** - * SDRangel - * This is the web REST/JSON API of SDRangel SDR software. SDRangel is an Open Source Qt5/OpenGL 3.0+ (4.3+ in Windows) GUI and server Software Defined Radio and signal analyzer in software. It supports Airspy, BladeRF, HackRF, LimeSDR, PlutoSDR, RTL-SDR, SDRplay RSP1 and FunCube --- Limitations and specifcities: * In SDRangel GUI the first Rx device set cannot be deleted. Conversely the server starts with no device sets and its number of device sets can be reduced to zero by as many calls as necessary to /sdrangel/deviceset with DELETE method. * Preset import and export from/to file is a server only feature. * Device set focus is a GUI only feature. * The following channels are not implemented (status 501 is returned): ATV and DATV demodulators, Channel Analyzer NG, LoRa demodulator * The device settings and report structures contains only the sub-structure corresponding to the device type. The DeviceSettings and DeviceReport structures documented here shows all of them but only one will be or should be present at a time * The channel settings and report structures contains only the sub-structure corresponding to the channel type. The ChannelSettings and ChannelReport structures documented here shows all of them but only one will be or should be present at a time --- - * - * OpenAPI spec version: 6.0.0 - * Contact: f4exb06@gmail.com - * - * NOTE: This class is auto generated by the swagger code generator program. - * https://github.com/swagger-api/swagger-codegen.git - * Do not edit the class manually. - */ - -/* - * SWGVORLocalizerSettings_vorDemodChannels.h - * - * - */ - -#ifndef SWGVORLocalizerSettings_vorDemodChannels_H_ -#define SWGVORLocalizerSettings_vorDemodChannels_H_ - -#include - - - -#include "SWGObject.h" -#include "export.h" - -namespace SWGSDRangel { - -class SWG_API SWGVORLocalizerSettings_vorDemodChannels: public SWGObject { -public: - SWGVORLocalizerSettings_vorDemodChannels(); - SWGVORLocalizerSettings_vorDemodChannels(QString* json); - virtual ~SWGVORLocalizerSettings_vorDemodChannels(); - void init(); - void cleanup(); - - virtual QString asJson () override; - virtual QJsonObject* asJsonObject() override; - virtual void fromJsonObject(QJsonObject &json) override; - virtual SWGVORLocalizerSettings_vorDemodChannels* fromJson(QString &jsonString) override; - - qint32 getDeviceSetIndex(); - void setDeviceSetIndex(qint32 device_set_index); - - qint32 getChannelIndex(); - void setChannelIndex(qint32 channel_index); - - - virtual bool isSet() override; - -private: - qint32 device_set_index; - bool m_device_set_index_isSet; - - qint32 channel_index; - bool m_channel_index_isSet; - -}; - -} - -#endif /* SWGVORLocalizerSettings_vorDemodChannels_H_ */ From 4eba3bb8716eb6fa445e816945ccff778a8e626f Mon Sep 17 00:00:00 2001 From: f4exb Date: Wed, 4 May 2022 13:27:13 +0200 Subject: [PATCH 106/115] v7: removed feature set index from API. Implements #1231 --- sdrbase/resources/webapi/doc/html2/index.html | 1159 ++++------------- .../resources/webapi/doc/swagger/swagger.yaml | 148 +-- sdrbase/webapi/webapiadapter.cpp | 7 +- sdrbase/webapi/webapiadapter.h | 2 +- sdrbase/webapi/webapiadapterinterface.cpp | 16 +- sdrbase/webapi/webapiadapterinterface.h | 10 +- sdrbase/webapi/webapirequestmapper.cpp | 327 ++--- sdrbase/webapi/webapirequestmapper.h | 16 +- swagger/sdrangel/api/swagger/swagger.yaml | 148 +-- swagger/sdrangel/code/html2/index.html | 1159 ++++------------- .../code/qt5/client/SWGFeatureSet.cpp | 23 - .../sdrangel/code/qt5/client/SWGFeatureSet.h | 6 - .../code/qt5/client/SWGFeatureSetApi.cpp | 84 +- .../code/qt5/client/SWGFeatureSetApi.h | 28 +- 14 files changed, 751 insertions(+), 2382 deletions(-) diff --git a/sdrbase/resources/webapi/doc/html2/index.html b/sdrbase/resources/webapi/doc/html2/index.html index b58b5a96e..e764b24ef 100644 --- a/sdrbase/resources/webapi/doc/html2/index.html +++ b/sdrbase/resources/webapi/doc/html2/index.html @@ -5351,12 +5351,8 @@ margin-bottom: 20px; "description" : "Base feature report. Only the feature report corresponding to the feature specified in the featureType field is or should be present." }; defs.FeatureSet = { - "required" : [ "featurecount", "index" ], + "required" : [ "featurecount" ], "properties" : { - "index" : { - "type" : "integer", - "description" : "Index in the list of feature sets opened in this instance" - }, "featurecount" : { "type" : "integer", "description" : "Number of features in the set" @@ -29027,7 +29023,7 @@ except ApiException as e:

    apply all settings unconditionally (force)


    -
    /sdrangel/featureset/{featureSetIndex}/feature/{featureIndex}/settings
    +
    /sdrangel/featureset/feature/{featureIndex}/settings

    Usage and SDK Samples

    @@ -29047,7 +29043,7 @@ except ApiException as e:
    -
    curl -X PUT "http://localhost/sdrangel/featureset/{featureSetIndex}/feature/{featureIndex}/settings"
    +
    curl -X PUT "http://localhost/sdrangel/featureset/feature/{featureIndex}/settings"
    import SWGSDRangel.*;
    @@ -29063,11 +29059,10 @@ public class FeatureSetApiExample {
         public static void main(String[] args) {
             
             FeatureSetApi apiInstance = new FeatureSetApi();
    -        Integer featureSetIndex = 56; // Integer | Index of feature set in the feature set list
    -        Integer featureIndex = 56; // Integer | Index of the feature in the features list for this feature set
    +        Integer featureIndex = 56; // Integer | Index of the feature in the features list
             FeatureSettings body = ; // FeatureSettings | Feature settings to apply
             try {
    -            FeatureSettings result = apiInstance.featuresetFEatureSettingsPut(featureSetIndex, featureIndex, body);
    +            FeatureSettings result = apiInstance.featuresetFEatureSettingsPut(featureIndex, body);
                 System.out.println(result);
             } catch (ApiException e) {
                 System.err.println("Exception when calling FeatureSetApi#featuresetFEatureSettingsPut");
    @@ -29084,11 +29079,10 @@ public class FeatureSetApiExample {
     
         public static void main(String[] args) {
             FeatureSetApi apiInstance = new FeatureSetApi();
    -        Integer featureSetIndex = 56; // Integer | Index of feature set in the feature set list
    -        Integer featureIndex = 56; // Integer | Index of the feature in the features list for this feature set
    +        Integer featureIndex = 56; // Integer | Index of the feature in the features list
             FeatureSettings body = ; // FeatureSettings | Feature settings to apply
             try {
    -            FeatureSettings result = apiInstance.featuresetFEatureSettingsPut(featureSetIndex, featureIndex, body);
    +            FeatureSettings result = apiInstance.featuresetFEatureSettingsPut(featureIndex, body);
                 System.out.println(result);
             } catch (ApiException e) {
                 System.err.println("Exception when calling FeatureSetApi#featuresetFEatureSettingsPut");
    @@ -29102,14 +29096,12 @@ public class FeatureSetApiExample {
       
    Coming Soon!
    -->
    -
    Integer *featureSetIndex = 56; // Index of feature set in the feature set list
    -Integer *featureIndex = 56; // Index of the feature in the features list for this feature set
    +                              
    Integer *featureIndex = 56; // Index of the feature in the features list
     FeatureSettings *body = ; // Feature settings to apply
     
     FeatureSetApi *apiInstance = [[FeatureSetApi alloc] init];
     
    -[apiInstance featuresetFEatureSettingsPutWith:featureSetIndex
    -    featureIndex:featureIndex
    +[apiInstance featuresetFEatureSettingsPutWith:featureIndex
         body:body
                   completionHandler: ^(FeatureSettings output, NSError* error) {
                                 if (output) {
    @@ -29127,9 +29119,7 @@ FeatureSetApi *apiInstance = [[FeatureSetApi alloc] init];
     
     var api = new SdRangel.FeatureSetApi()
     
    -var featureSetIndex = 56; // {Integer} Index of feature set in the feature set list
    -
    -var featureIndex = 56; // {Integer} Index of the feature in the features list for this feature set
    +var featureIndex = 56; // {Integer} Index of the feature in the features list
     
     var body = ; // {FeatureSettings} Feature settings to apply
     
    @@ -29141,7 +29131,7 @@ var callback = function(error, data, response) {
         console.log('API called successfully. Returned data: ' + data);
       }
     };
    -api.featuresetFEatureSettingsPut(featureSetIndex, featureIndex, body, callback);
    +api.featuresetFEatureSettingsPut(featureIndex, body, callback);
     
    @@ -29163,13 +29153,12 @@ namespace Example { var apiInstance = new FeatureSetApi(); - var featureSetIndex = 56; // Integer | Index of feature set in the feature set list - var featureIndex = 56; // Integer | Index of the feature in the features list for this feature set + var featureIndex = 56; // Integer | Index of the feature in the features list var body = new FeatureSettings(); // FeatureSettings | Feature settings to apply try { - FeatureSettings result = apiInstance.featuresetFEatureSettingsPut(featureSetIndex, featureIndex, body); + FeatureSettings result = apiInstance.featuresetFEatureSettingsPut(featureIndex, body); Debug.WriteLine(result); } catch (Exception e) @@ -29187,12 +29176,11 @@ namespace Example require_once(__DIR__ . '/vendor/autoload.php'); $api_instance = new Swagger\Client\Api\FeatureSetApi(); -$featureSetIndex = 56; // Integer | Index of feature set in the feature set list -$featureIndex = 56; // Integer | Index of the feature in the features list for this feature set +$featureIndex = 56; // Integer | Index of the feature in the features list $body = ; // FeatureSettings | Feature settings to apply try { - $result = $api_instance->featuresetFEatureSettingsPut($featureSetIndex, $featureIndex, $body); + $result = $api_instance->featuresetFEatureSettingsPut($featureIndex, $body); print_r($result); } catch (Exception $e) { echo 'Exception when calling FeatureSetApi->featuresetFEatureSettingsPut: ', $e->getMessage(), PHP_EOL; @@ -29206,12 +29194,11 @@ use SWGSDRangel::Configuration; use SWGSDRangel::FeatureSetApi; my $api_instance = SWGSDRangel::FeatureSetApi->new(); -my $featureSetIndex = 56; # Integer | Index of feature set in the feature set list -my $featureIndex = 56; # Integer | Index of the feature in the features list for this feature set +my $featureIndex = 56; # Integer | Index of the feature in the features list my $body = SWGSDRangel::Object::FeatureSettings->new(); # FeatureSettings | Feature settings to apply eval { - my $result = $api_instance->featuresetFEatureSettingsPut(featureSetIndex => $featureSetIndex, featureIndex => $featureIndex, body => $body); + my $result = $api_instance->featuresetFEatureSettingsPut(featureIndex => $featureIndex, body => $body); print Dumper($result); }; if ($@) { @@ -29228,12 +29215,11 @@ from pprint import pprint # create an instance of the API class api_instance = swagger_sdrangel.FeatureSetApi() -featureSetIndex = 56 # Integer | Index of feature set in the feature set list -featureIndex = 56 # Integer | Index of the feature in the features list for this feature set +featureIndex = 56 # Integer | Index of the feature in the features list body = # FeatureSettings | Feature settings to apply try: - api_response = api_instance.featureset_f_eature_settings_put(featureSetIndex, featureIndex, body) + api_response = api_instance.featureset_f_eature_settings_put(featureIndex, body) pprint(api_response) except ApiException as e: print("Exception when calling FeatureSetApi->featuresetFEatureSettingsPut: %s\n" % e) @@ -29248,29 +29234,6 @@ except ApiException as e: Name Description - featureSetIndex* - - - -
    -
    -
    - - Integer - - -
    - Index of feature set in the feature set list -
    -
    -
    - Required -
    -
    -
    - - - featureIndex* @@ -29283,7 +29246,7 @@ except ApiException as e:
    - Index of the feature in the features list for this feature set + Index of the feature in the features list
    @@ -29386,7 +29349,7 @@ $(document).ready(function() {
    -

    Status: 400 - Invalid feature set or feature index

    +

    Status: 400 - Invalid feature index

    +
    +

    Workspace

    +
    +
    +
    +

    instanceWorkspaceAdd

    +

    +
    +
    +
    +

    +

    Add new workspace

    +

    +
    +
    /sdrangel/workspace
    +

    +

    Usage and SDK Samples

    +

    + + +
    +
    +
    curl -X POST "http://localhost/sdrangel/workspace"
    +
    +
    +
    import SWGSDRangel.*;
    +import SWGSDRangel.auth.*;
    +import SWGSDRangel.model.*;
    +import SWGSDRangel.api.WorkspaceApi;
    +
    +import java.io.File;
    +import java.util.*;
    +
    +public class WorkspaceApiExample {
    +
    +    public static void main(String[] args) {
    +        
    +        WorkspaceApi apiInstance = new WorkspaceApi();
    +        try {
    +            SuccessResponse result = apiInstance.instanceWorkspaceAdd();
    +            System.out.println(result);
    +        } catch (ApiException e) {
    +            System.err.println("Exception when calling WorkspaceApi#instanceWorkspaceAdd");
    +            e.printStackTrace();
    +        }
    +    }
    +}
    +
    + +
    +
    import SWGSDRangel.api.WorkspaceApi;
    +
    +public class WorkspaceApiExample {
    +
    +    public static void main(String[] args) {
    +        WorkspaceApi apiInstance = new WorkspaceApi();
    +        try {
    +            SuccessResponse result = apiInstance.instanceWorkspaceAdd();
    +            System.out.println(result);
    +        } catch (ApiException e) {
    +            System.err.println("Exception when calling WorkspaceApi#instanceWorkspaceAdd");
    +            e.printStackTrace();
    +        }
    +    }
    +}
    +
    + +
    +
    
    +WorkspaceApi *apiInstance = [[WorkspaceApi alloc] init];
    +
    +[apiInstance instanceWorkspaceAddWithCompletionHandler: 
    +              ^(SuccessResponse output, NSError* error) {
    +                            if (output) {
    +                                NSLog(@"%@", output);
    +                            }
    +                            if (error) {
    +                                NSLog(@"Error: %@", error);
    +                            }
    +                        }];
    +
    +
    + +
    +
    var SdRangel = require('sd_rangel');
    +
    +var api = new SdRangel.WorkspaceApi()
    +
    +var callback = function(error, data, response) {
    +  if (error) {
    +    console.error(error);
    +  } else {
    +    console.log('API called successfully. Returned data: ' + data);
    +  }
    +};
    +api.instanceWorkspaceAdd(callback);
    +
    +
    + + +
    +
    using System;
    +using System.Diagnostics;
    +using SWGSDRangel.Api;
    +using SWGSDRangel.Client;
    +using SWGSDRangel.Model;
    +
    +namespace Example
    +{
    +    public class instanceWorkspaceAddExample
    +    {
    +        public void main()
    +        {
    +            
    +            var apiInstance = new WorkspaceApi();
    +
    +            try
    +            {
    +                SuccessResponse result = apiInstance.instanceWorkspaceAdd();
    +                Debug.WriteLine(result);
    +            }
    +            catch (Exception e)
    +            {
    +                Debug.Print("Exception when calling WorkspaceApi.instanceWorkspaceAdd: " + e.Message );
    +            }
    +        }
    +    }
    +}
    +
    +
    + +
    +
    <?php
    +require_once(__DIR__ . '/vendor/autoload.php');
    +
    +$api_instance = new Swagger\Client\Api\WorkspaceApi();
    +
    +try {
    +    $result = $api_instance->instanceWorkspaceAdd();
    +    print_r($result);
    +} catch (Exception $e) {
    +    echo 'Exception when calling WorkspaceApi->instanceWorkspaceAdd: ', $e->getMessage(), PHP_EOL;
    +}
    +?>
    +
    + +
    +
    use Data::Dumper;
    +use SWGSDRangel::Configuration;
    +use SWGSDRangel::WorkspaceApi;
    +
    +my $api_instance = SWGSDRangel::WorkspaceApi->new();
    +
    +eval { 
    +    my $result = $api_instance->instanceWorkspaceAdd();
    +    print Dumper($result);
    +};
    +if ($@) {
    +    warn "Exception when calling WorkspaceApi->instanceWorkspaceAdd: $@\n";
    +}
    +
    + +
    +
    from __future__ import print_statement
    +import time
    +import swagger_sdrangel
    +from swagger_sdrangel.rest import ApiException
    +from pprint import pprint
    +
    +# create an instance of the API class
    +api_instance = swagger_sdrangel.WorkspaceApi()
    +
    +try: 
    +    api_response = api_instance.instance_workspace_add()
    +    pprint(api_response)
    +except ApiException as e:
    +    print("Exception when calling WorkspaceApi->instanceWorkspaceAdd: %s\n" % e)
    +
    +
    + +

    Parameters

    + + + + + + +

    Responses

    +

    Status: 202 -

    + + + +
    +
    +
    + +
    + +
    +
    + +

    Status: 500 - Error

    + + + +
    +
    +
    + +
    + +
    +
    + +

    Status: 501 - Function not implemented

    + + + +
    +
    +
    + +
    + +
    +
    + +
    +
    +
    +
    +
    +
    +

    instanceWorkspacesDeleteEmpty

    +

    +
    +
    +
    +

    +

    Delete empty workspaces

    +

    +
    +
    /sdrangel/workspace
    +

    +

    Usage and SDK Samples

    +

    + + +
    +
    +
    curl -X DELETE "http://localhost/sdrangel/workspace"
    +
    +
    +
    import SWGSDRangel.*;
    +import SWGSDRangel.auth.*;
    +import SWGSDRangel.model.*;
    +import SWGSDRangel.api.WorkspaceApi;
    +
    +import java.io.File;
    +import java.util.*;
    +
    +public class WorkspaceApiExample {
    +
    +    public static void main(String[] args) {
    +        
    +        WorkspaceApi apiInstance = new WorkspaceApi();
    +        try {
    +            SuccessResponse result = apiInstance.instanceWorkspacesDeleteEmpty();
    +            System.out.println(result);
    +        } catch (ApiException e) {
    +            System.err.println("Exception when calling WorkspaceApi#instanceWorkspacesDeleteEmpty");
    +            e.printStackTrace();
    +        }
    +    }
    +}
    +
    + +
    +
    import SWGSDRangel.api.WorkspaceApi;
    +
    +public class WorkspaceApiExample {
    +
    +    public static void main(String[] args) {
    +        WorkspaceApi apiInstance = new WorkspaceApi();
    +        try {
    +            SuccessResponse result = apiInstance.instanceWorkspacesDeleteEmpty();
    +            System.out.println(result);
    +        } catch (ApiException e) {
    +            System.err.println("Exception when calling WorkspaceApi#instanceWorkspacesDeleteEmpty");
    +            e.printStackTrace();
    +        }
    +    }
    +}
    +
    + +
    +
    
    +WorkspaceApi *apiInstance = [[WorkspaceApi alloc] init];
    +
    +[apiInstance instanceWorkspacesDeleteEmptyWithCompletionHandler: 
    +              ^(SuccessResponse output, NSError* error) {
    +                            if (output) {
    +                                NSLog(@"%@", output);
    +                            }
    +                            if (error) {
    +                                NSLog(@"Error: %@", error);
    +                            }
    +                        }];
    +
    +
    + +
    +
    var SdRangel = require('sd_rangel');
    +
    +var api = new SdRangel.WorkspaceApi()
    +
    +var callback = function(error, data, response) {
    +  if (error) {
    +    console.error(error);
    +  } else {
    +    console.log('API called successfully. Returned data: ' + data);
    +  }
    +};
    +api.instanceWorkspacesDeleteEmpty(callback);
    +
    +
    + + +
    +
    using System;
    +using System.Diagnostics;
    +using SWGSDRangel.Api;
    +using SWGSDRangel.Client;
    +using SWGSDRangel.Model;
    +
    +namespace Example
    +{
    +    public class instanceWorkspacesDeleteEmptyExample
    +    {
    +        public void main()
    +        {
    +            
    +            var apiInstance = new WorkspaceApi();
    +
    +            try
    +            {
    +                SuccessResponse result = apiInstance.instanceWorkspacesDeleteEmpty();
    +                Debug.WriteLine(result);
    +            }
    +            catch (Exception e)
    +            {
    +                Debug.Print("Exception when calling WorkspaceApi.instanceWorkspacesDeleteEmpty: " + e.Message );
    +            }
    +        }
    +    }
    +}
    +
    +
    + +
    +
    <?php
    +require_once(__DIR__ . '/vendor/autoload.php');
    +
    +$api_instance = new Swagger\Client\Api\WorkspaceApi();
    +
    +try {
    +    $result = $api_instance->instanceWorkspacesDeleteEmpty();
    +    print_r($result);
    +} catch (Exception $e) {
    +    echo 'Exception when calling WorkspaceApi->instanceWorkspacesDeleteEmpty: ', $e->getMessage(), PHP_EOL;
    +}
    +?>
    +
    + +
    +
    use Data::Dumper;
    +use SWGSDRangel::Configuration;
    +use SWGSDRangel::WorkspaceApi;
    +
    +my $api_instance = SWGSDRangel::WorkspaceApi->new();
    +
    +eval { 
    +    my $result = $api_instance->instanceWorkspacesDeleteEmpty();
    +    print Dumper($result);
    +};
    +if ($@) {
    +    warn "Exception when calling WorkspaceApi->instanceWorkspacesDeleteEmpty: $@\n";
    +}
    +
    + +
    +
    from __future__ import print_statement
    +import time
    +import swagger_sdrangel
    +from swagger_sdrangel.rest import ApiException
    +from pprint import pprint
    +
    +# create an instance of the API class
    +api_instance = swagger_sdrangel.WorkspaceApi()
    +
    +try: 
    +    api_response = api_instance.instance_workspaces_delete_empty()
    +    pprint(api_response)
    +except ApiException as e:
    +    print("Exception when calling WorkspaceApi->instanceWorkspacesDeleteEmpty: %s\n" % e)
    +
    +
    + +

    Parameters

    + + + + + + +

    Responses

    +

    Status: 202 -

    + + + +
    +
    +
    + +
    + +
    +
    + +

    Status: 500 - Error

    + + + +
    +
    +
    + +
    + +
    +
    + +

    Status: 501 - Function not implemented

    + + + +
    +
    +
    + +
    + +
    +
    + +
    +
    +
    +
    diff --git a/sdrbase/resources/webapi/doc/swagger/swagger.yaml b/sdrbase/resources/webapi/doc/swagger/swagger.yaml index f03bcf827..d6b6fda7e 100644 --- a/sdrbase/resources/webapi/doc/swagger/swagger.yaml +++ b/sdrbase/resources/webapi/doc/swagger/swagger.yaml @@ -1282,6 +1282,37 @@ paths: "501": $ref: "#/responses/Response_501" + /sdrangel/workspace: + x-swagger-router-controller: instance + post: + description: Add new workspace + operationId: InstanceWorkspaceAdd + tags: + - Workspace + responses: + "202": + descriptions: Successful sending of the message + schema: + $ref: "#/definitions/SuccessResponse" + "500": + $ref: "#/responses/Response_500" + "501": + $ref: "#/responses/Response_501" + delete: + description: Delete empty workspaces + operationId: InstanceWorkspacesDeleteEmpty + tags: + - Workspace + responses: + "202": + descriptions: Successful sending of the message + schema: + $ref: "#/definitions/SuccessResponse" + "500": + $ref: "#/responses/Response_500" + "501": + $ref: "#/responses/Response_501" + /sdrangel/deviceset: x-swagger-router-controller: instance post: diff --git a/sdrbase/webapi/webapiadapter.cpp b/sdrbase/webapi/webapiadapter.cpp index 2d4ded98d..d9804f000 100644 --- a/sdrbase/webapi/webapiadapter.cpp +++ b/sdrbase/webapi/webapiadapter.cpp @@ -2077,6 +2077,34 @@ int WebAPIAdapter::instanceDeviceSetDelete( } } +int WebAPIAdapter::instanceWorkspacePost( + SWGSDRangel::SWGSuccessResponse& response, + SWGSDRangel::SWGErrorResponse& error) +{ + (void) error; + MainCore::MsgAddWorkspace *msg = MainCore::MsgAddWorkspace::create(); + m_mainCore->m_mainMessageQueue->push(msg); + + response.init(); + *response.getMessage() = QString("Message to add a new workspace (MsgAddWorkspace) was submitted successfully"); + + return 202; +} + +int WebAPIAdapter::instanceWorkspaceDelete( + SWGSDRangel::SWGSuccessResponse& response, + SWGSDRangel::SWGErrorResponse& error) +{ + (void) error; + MainCore::MsgDeleteEmptyWorkspaces *msg = MainCore::MsgDeleteEmptyWorkspaces::create(); + m_mainCore->m_mainMessageQueue->push(msg); + + response.init(); + *response.getMessage() = QString("Message to delete empty workspaces (MsgDeleteEmptyWorkspaces) was submitted successfully"); + + return 202; +} + int WebAPIAdapter::devicesetGet( int deviceSetIndex, SWGSDRangel::SWGDeviceSet& response, diff --git a/sdrbase/webapi/webapiadapter.h b/sdrbase/webapi/webapiadapter.h index 212a9beda..e90d3416a 100644 --- a/sdrbase/webapi/webapiadapter.h +++ b/sdrbase/webapi/webapiadapter.h @@ -276,6 +276,14 @@ public: SWGSDRangel::SWGSuccessResponse& response, SWGSDRangel::SWGErrorResponse& error); + virtual int instanceWorkspacePost( + SWGSDRangel::SWGSuccessResponse& response, + SWGSDRangel::SWGErrorResponse& error); + + virtual int instanceWorkspaceDelete( + SWGSDRangel::SWGSuccessResponse& response, + SWGSDRangel::SWGErrorResponse& error); + virtual int devicesetGet( int deviceSetIndex, SWGSDRangel::SWGDeviceSet& response, diff --git a/sdrbase/webapi/webapiadapterinterface.cpp b/sdrbase/webapi/webapiadapterinterface.cpp index f0deeab98..5aa38447a 100644 --- a/sdrbase/webapi/webapiadapterinterface.cpp +++ b/sdrbase/webapi/webapiadapterinterface.cpp @@ -49,6 +49,7 @@ QString WebAPIAdapterInterface::instanceFeaturePresetsURL = "/sdrangel/featurepr QString WebAPIAdapterInterface::instanceFeaturePresetURL = "/sdrangel/featurepreset"; QString WebAPIAdapterInterface::instanceDeviceSetsURL = "/sdrangel/devicesets"; QString WebAPIAdapterInterface::instanceDeviceSetURL = "/sdrangel/deviceset"; +QString WebAPIAdapterInterface::instanceWorkspaceURL = "/sdrangel/workspace"; QString WebAPIAdapterInterface::featuresetURL("/sdrangel/featureset"); QString WebAPIAdapterInterface::featuresetFeatureURL("/sdrangel/featureset/feature"); QString WebAPIAdapterInterface::featuresetPresetURL("/sdrangel/featureset/preset"); diff --git a/sdrbase/webapi/webapiadapterinterface.h b/sdrbase/webapi/webapiadapterinterface.h index 54a53d4a2..11c4cd31f 100644 --- a/sdrbase/webapi/webapiadapterinterface.h +++ b/sdrbase/webapi/webapiadapterinterface.h @@ -868,6 +868,34 @@ public: return 501; } + /** + * Handler of /sdrangel/workspace (POST) swagger/sdrangel/code/html2/index.html#api-Default-instanceChannels + * returns the Http status code (default 501: not implemented) + */ + virtual int instanceWorkspacePost( + SWGSDRangel::SWGSuccessResponse& response, + SWGSDRangel::SWGErrorResponse& error) + { + (void) response; + error.init(); + *error.getMessage() = QString("Function not implemented"); + return 501; + } + + /** + * Handler of /sdrangel/workspace (DELETE) swagger/sdrangel/code/html2/index.html#api-Default-instanceChannels + * returns the Http status code (default 501: not implemented) + */ + virtual int instanceWorkspaceDelete( + SWGSDRangel::SWGSuccessResponse& response, + SWGSDRangel::SWGErrorResponse& error) + { + (void) response; + error.init(); + *error.getMessage() = QString("Function not implemented"); + return 501; + } + /** * Handler of /sdrangel/deviceset (POST) swagger/sdrangel/code/html2/index.html#api-Default-instanceChannels * returns the Http status code (default 501: not implemented) @@ -1651,6 +1679,7 @@ public: static QString instanceFeaturePresetURL; static QString instanceDeviceSetsURL; static QString instanceDeviceSetURL; + static QString instanceWorkspaceURL; static QString featuresetURL; static QString featuresetFeatureURL; static QString featuresetPresetURL; diff --git a/sdrbase/webapi/webapirequestmapper.cpp b/sdrbase/webapi/webapirequestmapper.cpp index e51727673..e2f7566ec 100644 --- a/sdrbase/webapi/webapirequestmapper.cpp +++ b/sdrbase/webapi/webapirequestmapper.cpp @@ -174,6 +174,8 @@ void WebAPIRequestMapper::service(qtwebapp::HttpRequest& request, qtwebapp::Http instanceDeviceSetsService(request, response); } else if (path == WebAPIAdapterInterface::instanceDeviceSetURL) { instanceDeviceSetService(request, response); + } else if (path == WebAPIAdapterInterface::instanceWorkspaceURL) { + instanceWorkspaceService(request, response); } else if (path == WebAPIAdapterInterface::featuresetURL) { featuresetService(request, response); } else if (path == WebAPIAdapterInterface::featuresetFeatureURL) { @@ -1991,6 +1993,45 @@ void WebAPIRequestMapper::instanceDeviceSetService(qtwebapp::HttpRequest& reques } } +void WebAPIRequestMapper::instanceWorkspaceService(qtwebapp::HttpRequest& request, qtwebapp::HttpResponse& response) +{ + SWGSDRangel::SWGErrorResponse errorResponse; + response.setHeader("Content-Type", "application/json"); + response.setHeader("Access-Control-Allow-Origin", "*"); + + if (request.getMethod() == "POST") + { + SWGSDRangel::SWGSuccessResponse normalResponse; + int status = m_adapter->instanceWorkspacePost(normalResponse, errorResponse); + response.setStatus(status); + + if (status/100 == 2) { + response.write(normalResponse.asJson().toUtf8()); + } else { + response.write(errorResponse.asJson().toUtf8()); + } + } + else if (request.getMethod() == "DELETE") + { + SWGSDRangel::SWGSuccessResponse normalResponse; + int status = m_adapter->instanceWorkspaceDelete(normalResponse, errorResponse); + response.setStatus(status); + + if (status/100 == 2) { + response.write(normalResponse.asJson().toUtf8()); + } else { + response.write(errorResponse.asJson().toUtf8()); + } + } + else + { + response.setStatus(405,"Invalid HTTP method"); + errorResponse.init(); + *errorResponse.getMessage() = "Invalid HTTP method"; + response.write(errorResponse.asJson().toUtf8()); + } +} + void WebAPIRequestMapper::devicesetService(const std::string& indexStr, qtwebapp::HttpRequest& request, qtwebapp::HttpResponse& response) { SWGSDRangel::SWGErrorResponse errorResponse; diff --git a/sdrbase/webapi/webapirequestmapper.h b/sdrbase/webapi/webapirequestmapper.h index 3e9c37b83..3921a5ad4 100644 --- a/sdrbase/webapi/webapirequestmapper.h +++ b/sdrbase/webapi/webapirequestmapper.h @@ -85,6 +85,7 @@ private: void instanceFeaturePresetService(qtwebapp::HttpRequest& request, qtwebapp::HttpResponse& response); void instanceDeviceSetsService(qtwebapp::HttpRequest& request, qtwebapp::HttpResponse& response); void instanceDeviceSetService(qtwebapp::HttpRequest& request, qtwebapp::HttpResponse& response); + void instanceWorkspaceService(qtwebapp::HttpRequest& request, qtwebapp::HttpResponse& response); void devicesetService(const std::string& indexStr, qtwebapp::HttpRequest& request, qtwebapp::HttpResponse& response); void devicesetFocusService(const std::string& indexStr, qtwebapp::HttpRequest& request, qtwebapp::HttpResponse& response); @@ -112,6 +113,7 @@ private: void featuresetFeatureReportService(const std::string& featureIndexStr, qtwebapp::HttpRequest& request, qtwebapp::HttpResponse& response); void featuresetFeatureActionsService(const std::string& featureIndexStr, qtwebapp::HttpRequest& request, qtwebapp::HttpResponse& response); + bool validatePresetTransfer(SWGSDRangel::SWGPresetTransfer& presetTransfer); bool validatePresetIdentifer(SWGSDRangel::SWGPresetIdentifier& presetIdentifier); bool validatePresetExport(SWGSDRangel::SWGPresetExport& presetExport); diff --git a/sdrgui/mainwindow.cpp b/sdrgui/mainwindow.cpp index a3c8a54a6..dcdb11c3f 100644 --- a/sdrgui/mainwindow.cpp +++ b/sdrgui/mainwindow.cpp @@ -1621,6 +1621,16 @@ bool MainWindow::handleMessage(const Message& cmd) m_mainCore->m_settings.deleteConfiguration(configurationToDelete); return true; } + else if (MainCore::MsgDeleteEmptyWorkspaces::match(cmd)) + { + removeEmptyWorkspaces(); + return true; + } + else if (MainCore::MsgAddWorkspace::match(cmd)) + { + addWorkspace(); + return true; + } else if (MainCore::MsgDeleteFeatureSetPreset::match(cmd)) { MainCore::MsgDeleteFeatureSetPreset& notif = (MainCore::MsgDeleteFeatureSetPreset&) cmd; diff --git a/swagger/sdrangel/api/swagger/swagger.yaml b/swagger/sdrangel/api/swagger/swagger.yaml index ea129fa44..cbb350a9b 100644 --- a/swagger/sdrangel/api/swagger/swagger.yaml +++ b/swagger/sdrangel/api/swagger/swagger.yaml @@ -1282,6 +1282,37 @@ paths: "501": $ref: "#/responses/Response_501" + /sdrangel/workspace: + x-swagger-router-controller: instance + post: + description: Add new workspace + operationId: InstanceWorkspaceAdd + tags: + - Workspace + responses: + "202": + descriptions: Successful sending of the message + schema: + $ref: "#/definitions/SuccessResponse" + "500": + $ref: "#/responses/Response_500" + "501": + $ref: "#/responses/Response_501" + delete: + description: Delete empty workspaces + operationId: InstanceWorkspacesDeleteEmpty + tags: + - Workspace + responses: + "202": + descriptions: Successful sending of the message + schema: + $ref: "#/definitions/SuccessResponse" + "500": + $ref: "#/responses/Response_500" + "501": + $ref: "#/responses/Response_501" + /sdrangel/deviceset: x-swagger-router-controller: instance post: diff --git a/swagger/sdrangel/code/html2/index.html b/swagger/sdrangel/code/html2/index.html index 3a117e943..b685023c3 100644 --- a/swagger/sdrangel/code/html2/index.html +++ b/swagger/sdrangel/code/html2/index.html @@ -14754,6 +14754,13 @@ margin-bottom: 20px;
  • instanceSummary
  • + +
  • + instanceWorkspaceAdd +
  • +
  • + instanceWorkspacesDeleteEmpty +
  • @@ -55812,6 +55819,677 @@ except ApiException as e:
    +
    +

    Workspace

    +
    +
    +
    +

    instanceWorkspaceAdd

    +

    +
    +
    +
    +

    +

    Add new workspace

    +

    +
    +
    /sdrangel/workspace
    +

    +

    Usage and SDK Samples

    +

    + + +
    +
    +
    curl -X POST "http://localhost/sdrangel/workspace"
    +
    +
    +
    import SWGSDRangel.*;
    +import SWGSDRangel.auth.*;
    +import SWGSDRangel.model.*;
    +import SWGSDRangel.api.WorkspaceApi;
    +
    +import java.io.File;
    +import java.util.*;
    +
    +public class WorkspaceApiExample {
    +
    +    public static void main(String[] args) {
    +        
    +        WorkspaceApi apiInstance = new WorkspaceApi();
    +        try {
    +            SuccessResponse result = apiInstance.instanceWorkspaceAdd();
    +            System.out.println(result);
    +        } catch (ApiException e) {
    +            System.err.println("Exception when calling WorkspaceApi#instanceWorkspaceAdd");
    +            e.printStackTrace();
    +        }
    +    }
    +}
    +
    + +
    +
    import SWGSDRangel.api.WorkspaceApi;
    +
    +public class WorkspaceApiExample {
    +
    +    public static void main(String[] args) {
    +        WorkspaceApi apiInstance = new WorkspaceApi();
    +        try {
    +            SuccessResponse result = apiInstance.instanceWorkspaceAdd();
    +            System.out.println(result);
    +        } catch (ApiException e) {
    +            System.err.println("Exception when calling WorkspaceApi#instanceWorkspaceAdd");
    +            e.printStackTrace();
    +        }
    +    }
    +}
    +
    + +
    +
    
    +WorkspaceApi *apiInstance = [[WorkspaceApi alloc] init];
    +
    +[apiInstance instanceWorkspaceAddWithCompletionHandler: 
    +              ^(SuccessResponse output, NSError* error) {
    +                            if (output) {
    +                                NSLog(@"%@", output);
    +                            }
    +                            if (error) {
    +                                NSLog(@"Error: %@", error);
    +                            }
    +                        }];
    +
    +
    + +
    +
    var SdRangel = require('sd_rangel');
    +
    +var api = new SdRangel.WorkspaceApi()
    +
    +var callback = function(error, data, response) {
    +  if (error) {
    +    console.error(error);
    +  } else {
    +    console.log('API called successfully. Returned data: ' + data);
    +  }
    +};
    +api.instanceWorkspaceAdd(callback);
    +
    +
    + + +
    +
    using System;
    +using System.Diagnostics;
    +using SWGSDRangel.Api;
    +using SWGSDRangel.Client;
    +using SWGSDRangel.Model;
    +
    +namespace Example
    +{
    +    public class instanceWorkspaceAddExample
    +    {
    +        public void main()
    +        {
    +            
    +            var apiInstance = new WorkspaceApi();
    +
    +            try
    +            {
    +                SuccessResponse result = apiInstance.instanceWorkspaceAdd();
    +                Debug.WriteLine(result);
    +            }
    +            catch (Exception e)
    +            {
    +                Debug.Print("Exception when calling WorkspaceApi.instanceWorkspaceAdd: " + e.Message );
    +            }
    +        }
    +    }
    +}
    +
    +
    + +
    +
    <?php
    +require_once(__DIR__ . '/vendor/autoload.php');
    +
    +$api_instance = new Swagger\Client\Api\WorkspaceApi();
    +
    +try {
    +    $result = $api_instance->instanceWorkspaceAdd();
    +    print_r($result);
    +} catch (Exception $e) {
    +    echo 'Exception when calling WorkspaceApi->instanceWorkspaceAdd: ', $e->getMessage(), PHP_EOL;
    +}
    +?>
    +
    + +
    +
    use Data::Dumper;
    +use SWGSDRangel::Configuration;
    +use SWGSDRangel::WorkspaceApi;
    +
    +my $api_instance = SWGSDRangel::WorkspaceApi->new();
    +
    +eval { 
    +    my $result = $api_instance->instanceWorkspaceAdd();
    +    print Dumper($result);
    +};
    +if ($@) {
    +    warn "Exception when calling WorkspaceApi->instanceWorkspaceAdd: $@\n";
    +}
    +
    + +
    +
    from __future__ import print_statement
    +import time
    +import swagger_sdrangel
    +from swagger_sdrangel.rest import ApiException
    +from pprint import pprint
    +
    +# create an instance of the API class
    +api_instance = swagger_sdrangel.WorkspaceApi()
    +
    +try: 
    +    api_response = api_instance.instance_workspace_add()
    +    pprint(api_response)
    +except ApiException as e:
    +    print("Exception when calling WorkspaceApi->instanceWorkspaceAdd: %s\n" % e)
    +
    +
    + +

    Parameters

    + + + + + + +

    Responses

    +

    Status: 202 -

    + + + +
    +
    +
    + +
    + +
    +
    + +

    Status: 500 - Error

    + + + +
    +
    +
    + +
    + +
    +
    + +

    Status: 501 - Function not implemented

    + + + +
    +
    +
    + +
    + +
    +
    + +
    +
    +
    +
    +
    +
    +

    instanceWorkspacesDeleteEmpty

    +

    +
    +
    +
    +

    +

    Delete empty workspaces

    +

    +
    +
    /sdrangel/workspace
    +

    +

    Usage and SDK Samples

    +

    + + +
    +
    +
    curl -X DELETE "http://localhost/sdrangel/workspace"
    +
    +
    +
    import SWGSDRangel.*;
    +import SWGSDRangel.auth.*;
    +import SWGSDRangel.model.*;
    +import SWGSDRangel.api.WorkspaceApi;
    +
    +import java.io.File;
    +import java.util.*;
    +
    +public class WorkspaceApiExample {
    +
    +    public static void main(String[] args) {
    +        
    +        WorkspaceApi apiInstance = new WorkspaceApi();
    +        try {
    +            SuccessResponse result = apiInstance.instanceWorkspacesDeleteEmpty();
    +            System.out.println(result);
    +        } catch (ApiException e) {
    +            System.err.println("Exception when calling WorkspaceApi#instanceWorkspacesDeleteEmpty");
    +            e.printStackTrace();
    +        }
    +    }
    +}
    +
    + +
    +
    import SWGSDRangel.api.WorkspaceApi;
    +
    +public class WorkspaceApiExample {
    +
    +    public static void main(String[] args) {
    +        WorkspaceApi apiInstance = new WorkspaceApi();
    +        try {
    +            SuccessResponse result = apiInstance.instanceWorkspacesDeleteEmpty();
    +            System.out.println(result);
    +        } catch (ApiException e) {
    +            System.err.println("Exception when calling WorkspaceApi#instanceWorkspacesDeleteEmpty");
    +            e.printStackTrace();
    +        }
    +    }
    +}
    +
    + +
    +
    
    +WorkspaceApi *apiInstance = [[WorkspaceApi alloc] init];
    +
    +[apiInstance instanceWorkspacesDeleteEmptyWithCompletionHandler: 
    +              ^(SuccessResponse output, NSError* error) {
    +                            if (output) {
    +                                NSLog(@"%@", output);
    +                            }
    +                            if (error) {
    +                                NSLog(@"Error: %@", error);
    +                            }
    +                        }];
    +
    +
    + +
    +
    var SdRangel = require('sd_rangel');
    +
    +var api = new SdRangel.WorkspaceApi()
    +
    +var callback = function(error, data, response) {
    +  if (error) {
    +    console.error(error);
    +  } else {
    +    console.log('API called successfully. Returned data: ' + data);
    +  }
    +};
    +api.instanceWorkspacesDeleteEmpty(callback);
    +
    +
    + + +
    +
    using System;
    +using System.Diagnostics;
    +using SWGSDRangel.Api;
    +using SWGSDRangel.Client;
    +using SWGSDRangel.Model;
    +
    +namespace Example
    +{
    +    public class instanceWorkspacesDeleteEmptyExample
    +    {
    +        public void main()
    +        {
    +            
    +            var apiInstance = new WorkspaceApi();
    +
    +            try
    +            {
    +                SuccessResponse result = apiInstance.instanceWorkspacesDeleteEmpty();
    +                Debug.WriteLine(result);
    +            }
    +            catch (Exception e)
    +            {
    +                Debug.Print("Exception when calling WorkspaceApi.instanceWorkspacesDeleteEmpty: " + e.Message );
    +            }
    +        }
    +    }
    +}
    +
    +
    + +
    +
    <?php
    +require_once(__DIR__ . '/vendor/autoload.php');
    +
    +$api_instance = new Swagger\Client\Api\WorkspaceApi();
    +
    +try {
    +    $result = $api_instance->instanceWorkspacesDeleteEmpty();
    +    print_r($result);
    +} catch (Exception $e) {
    +    echo 'Exception when calling WorkspaceApi->instanceWorkspacesDeleteEmpty: ', $e->getMessage(), PHP_EOL;
    +}
    +?>
    +
    + +
    +
    use Data::Dumper;
    +use SWGSDRangel::Configuration;
    +use SWGSDRangel::WorkspaceApi;
    +
    +my $api_instance = SWGSDRangel::WorkspaceApi->new();
    +
    +eval { 
    +    my $result = $api_instance->instanceWorkspacesDeleteEmpty();
    +    print Dumper($result);
    +};
    +if ($@) {
    +    warn "Exception when calling WorkspaceApi->instanceWorkspacesDeleteEmpty: $@\n";
    +}
    +
    + +
    +
    from __future__ import print_statement
    +import time
    +import swagger_sdrangel
    +from swagger_sdrangel.rest import ApiException
    +from pprint import pprint
    +
    +# create an instance of the API class
    +api_instance = swagger_sdrangel.WorkspaceApi()
    +
    +try: 
    +    api_response = api_instance.instance_workspaces_delete_empty()
    +    pprint(api_response)
    +except ApiException as e:
    +    print("Exception when calling WorkspaceApi->instanceWorkspacesDeleteEmpty: %s\n" % e)
    +
    +
    + +

    Parameters

    + + + + + + +

    Responses

    +

    Status: 202 -

    + + + +
    +
    +
    + +
    + +
    +
    + +

    Status: 500 - Error

    + + + +
    +
    +
    + +
    + +
    +
    + +

    Status: 501 - Function not implemented

    + + + +
    +
    +
    + +
    + +
    +
    + +
    +
    +
    +
    diff --git a/swagger/sdrangel/code/qt5/client/SWGWorkspaceApi.cpp b/swagger/sdrangel/code/qt5/client/SWGWorkspaceApi.cpp new file mode 100644 index 000000000..4c4333f6d --- /dev/null +++ b/swagger/sdrangel/code/qt5/client/SWGWorkspaceApi.cpp @@ -0,0 +1,136 @@ +/** + * SDRangel + * This is the web REST/JSON API of SDRangel SDR software. SDRangel is an Open Source Qt5/OpenGL 3.0+ (4.3+ in Windows) GUI and server Software Defined Radio and signal analyzer in software. It supports Airspy, BladeRF, HackRF, LimeSDR, PlutoSDR, RTL-SDR, SDRplay RSP1 and FunCube --- Limitations and specifcities: * In SDRangel GUI the first Rx device set cannot be deleted. Conversely the server starts with no device sets and its number of device sets can be reduced to zero by as many calls as necessary to /sdrangel/deviceset with DELETE method. * Preset import and export from/to file is a server only feature. * Device set focus is a GUI only feature. * The following channels are not implemented (status 501 is returned): ATV and DATV demodulators, Channel Analyzer NG, LoRa demodulator * The device settings and report structures contains only the sub-structure corresponding to the device type. The DeviceSettings and DeviceReport structures documented here shows all of them but only one will be or should be present at a time * The channel settings and report structures contains only the sub-structure corresponding to the channel type. The ChannelSettings and ChannelReport structures documented here shows all of them but only one will be or should be present at a time --- + * + * OpenAPI spec version: 6.0.0 + * Contact: f4exb06@gmail.com + * + * NOTE: This class is auto generated by the swagger code generator program. + * https://github.com/swagger-api/swagger-codegen.git + * Do not edit the class manually. + */ + +#include "SWGWorkspaceApi.h" +#include "SWGHelpers.h" +#include "SWGModelFactory.h" + +#include +#include + +namespace SWGSDRangel { + +SWGWorkspaceApi::SWGWorkspaceApi() {} + +SWGWorkspaceApi::~SWGWorkspaceApi() {} + +SWGWorkspaceApi::SWGWorkspaceApi(QString host, QString basePath) { + this->host = host; + this->basePath = basePath; +} + +void +SWGWorkspaceApi::instanceWorkspaceAdd() { + QString fullPath; + fullPath.append(this->host).append(this->basePath).append("/sdrangel/workspace"); + + + + SWGHttpRequestWorker *worker = new SWGHttpRequestWorker(); + SWGHttpRequestInput input(fullPath, "POST"); + + + + + + foreach(QString key, this->defaultHeaders.keys()) { + input.headers.insert(key, this->defaultHeaders.value(key)); + } + + connect(worker, + &SWGHttpRequestWorker::on_execution_finished, + this, + &SWGWorkspaceApi::instanceWorkspaceAddCallback); + + worker->execute(&input); +} + +void +SWGWorkspaceApi::instanceWorkspaceAddCallback(SWGHttpRequestWorker * worker) { + QString msg; + QString error_str = worker->error_str; + QNetworkReply::NetworkError error_type = worker->error_type; + + if (worker->error_type == QNetworkReply::NoError) { + msg = QString("Success! %1 bytes").arg(worker->response.length()); + } + else { + msg = "Error: " + worker->error_str; + } + + + QString json(worker->response); + SWGSuccessResponse* output = static_cast(create(json, QString("SWGSuccessResponse"))); + worker->deleteLater(); + + if (worker->error_type == QNetworkReply::NoError) { + emit instanceWorkspaceAddSignal(output); + } else { + emit instanceWorkspaceAddSignalE(output, error_type, error_str); + emit instanceWorkspaceAddSignalEFull(worker, error_type, error_str); + } +} + +void +SWGWorkspaceApi::instanceWorkspacesDeleteEmpty() { + QString fullPath; + fullPath.append(this->host).append(this->basePath).append("/sdrangel/workspace"); + + + + SWGHttpRequestWorker *worker = new SWGHttpRequestWorker(); + SWGHttpRequestInput input(fullPath, "DELETE"); + + + + + + foreach(QString key, this->defaultHeaders.keys()) { + input.headers.insert(key, this->defaultHeaders.value(key)); + } + + connect(worker, + &SWGHttpRequestWorker::on_execution_finished, + this, + &SWGWorkspaceApi::instanceWorkspacesDeleteEmptyCallback); + + worker->execute(&input); +} + +void +SWGWorkspaceApi::instanceWorkspacesDeleteEmptyCallback(SWGHttpRequestWorker * worker) { + QString msg; + QString error_str = worker->error_str; + QNetworkReply::NetworkError error_type = worker->error_type; + + if (worker->error_type == QNetworkReply::NoError) { + msg = QString("Success! %1 bytes").arg(worker->response.length()); + } + else { + msg = "Error: " + worker->error_str; + } + + + QString json(worker->response); + SWGSuccessResponse* output = static_cast(create(json, QString("SWGSuccessResponse"))); + worker->deleteLater(); + + if (worker->error_type == QNetworkReply::NoError) { + emit instanceWorkspacesDeleteEmptySignal(output); + } else { + emit instanceWorkspacesDeleteEmptySignalE(output, error_type, error_str); + emit instanceWorkspacesDeleteEmptySignalEFull(worker, error_type, error_str); + } +} + + +} diff --git a/swagger/sdrangel/code/qt5/client/SWGWorkspaceApi.h b/swagger/sdrangel/code/qt5/client/SWGWorkspaceApi.h new file mode 100644 index 000000000..5d1a5b371 --- /dev/null +++ b/swagger/sdrangel/code/qt5/client/SWGWorkspaceApi.h @@ -0,0 +1,57 @@ +/** + * SDRangel + * This is the web REST/JSON API of SDRangel SDR software. SDRangel is an Open Source Qt5/OpenGL 3.0+ (4.3+ in Windows) GUI and server Software Defined Radio and signal analyzer in software. It supports Airspy, BladeRF, HackRF, LimeSDR, PlutoSDR, RTL-SDR, SDRplay RSP1 and FunCube --- Limitations and specifcities: * In SDRangel GUI the first Rx device set cannot be deleted. Conversely the server starts with no device sets and its number of device sets can be reduced to zero by as many calls as necessary to /sdrangel/deviceset with DELETE method. * Preset import and export from/to file is a server only feature. * Device set focus is a GUI only feature. * The following channels are not implemented (status 501 is returned): ATV and DATV demodulators, Channel Analyzer NG, LoRa demodulator * The device settings and report structures contains only the sub-structure corresponding to the device type. The DeviceSettings and DeviceReport structures documented here shows all of them but only one will be or should be present at a time * The channel settings and report structures contains only the sub-structure corresponding to the channel type. The ChannelSettings and ChannelReport structures documented here shows all of them but only one will be or should be present at a time --- + * + * OpenAPI spec version: 6.0.0 + * Contact: f4exb06@gmail.com + * + * NOTE: This class is auto generated by the swagger code generator program. + * https://github.com/swagger-api/swagger-codegen.git + * Do not edit the class manually. + */ + +#ifndef _SWG_SWGWorkspaceApi_H_ +#define _SWG_SWGWorkspaceApi_H_ + +#include "SWGHttpRequest.h" + +#include "SWGErrorResponse.h" +#include "SWGSuccessResponse.h" + +#include + +namespace SWGSDRangel { + +class SWGWorkspaceApi: public QObject { + Q_OBJECT + +public: + SWGWorkspaceApi(); + SWGWorkspaceApi(QString host, QString basePath); + ~SWGWorkspaceApi(); + + QString host; + QString basePath; + QMap defaultHeaders; + + void instanceWorkspaceAdd(); + void instanceWorkspacesDeleteEmpty(); + +private: + void instanceWorkspaceAddCallback (SWGHttpRequestWorker * worker); + void instanceWorkspacesDeleteEmptyCallback (SWGHttpRequestWorker * worker); + +signals: + void instanceWorkspaceAddSignal(SWGSuccessResponse* summary); + void instanceWorkspacesDeleteEmptySignal(SWGSuccessResponse* summary); + + void instanceWorkspaceAddSignalE(SWGSuccessResponse* summary, QNetworkReply::NetworkError error_type, QString& error_str); + void instanceWorkspacesDeleteEmptySignalE(SWGSuccessResponse* summary, QNetworkReply::NetworkError error_type, QString& error_str); + + void instanceWorkspaceAddSignalEFull(SWGHttpRequestWorker* worker, QNetworkReply::NetworkError error_type, QString& error_str); + void instanceWorkspacesDeleteEmptySignalEFull(SWGHttpRequestWorker* worker, QNetworkReply::NetworkError error_type, QString& error_str); + +}; + +} +#endif From 0d7ca38ab0be4034c3b402fb3f5ad16ffe48c395 Mon Sep 17 00:00:00 2001 From: f4exb Date: Mon, 9 May 2022 21:43:30 +0200 Subject: [PATCH 109/115] Try to fix Ubuntu build --- sdrbase/webapi/webapiadapter.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/sdrbase/webapi/webapiadapter.cpp b/sdrbase/webapi/webapiadapter.cpp index d9804f000..7b5e84aba 100644 --- a/sdrbase/webapi/webapiadapter.cpp +++ b/sdrbase/webapi/webapiadapter.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include "maincore.h" #include "loggerwithfile.h" From 9e5c6294732fb469fe7fe45d7af229d33cfaaf78 Mon Sep 17 00:00:00 2001 From: f4exb Date: Fri, 13 May 2022 22:24:48 +0200 Subject: [PATCH 110/115] v7: API: added workspace handling for device, spectrum, feature and channel. Implements #1242 --- .../beamsteeringcwmod/beamsteeringcwmod.cpp | 10 + .../beamsteeringcwmod/beamsteeringcwmod.h | 4 + .../interferometer/interferometer.cpp | 10 + .../interferometer/interferometer.h | 4 + plugins/channelrx/chanalyzer/chanalyzer.cpp | 10 + plugins/channelrx/chanalyzer/chanalyzer.h | 4 + plugins/channelrx/demodadsb/adsbdemod.cpp | 10 + plugins/channelrx/demodadsb/adsbdemod.h | 4 + plugins/channelrx/demodais/aisdemod.cpp | 10 + plugins/channelrx/demodais/aisdemod.h | 4 + plugins/channelrx/demodam/amdemod.cpp | 10 + plugins/channelrx/demodam/amdemod.h | 4 + plugins/channelrx/demodapt/aptdemod.cpp | 10 + plugins/channelrx/demodapt/aptdemod.h | 4 + plugins/channelrx/demodbfm/bfmdemod.cpp | 10 + plugins/channelrx/demodbfm/bfmdemod.h | 4 + .../demodchirpchat/chirpchatdemod.cpp | 10 + .../channelrx/demodchirpchat/chirpchatdemod.h | 4 + plugins/channelrx/demoddab/dabdemod.cpp | 10 + plugins/channelrx/demoddab/dabdemod.h | 4 + plugins/channelrx/demoddatv/datvdemod.cpp | 10 + plugins/channelrx/demoddatv/datvdemod.h | 4 + plugins/channelrx/demoddsd/dsddemod.cpp | 10 + plugins/channelrx/demoddsd/dsddemod.h | 4 + plugins/channelrx/demodfreedv/freedvdemod.cpp | 10 + plugins/channelrx/demodfreedv/freedvdemod.h | 4 + plugins/channelrx/demodnfm/nfmdemod.cpp | 10 + plugins/channelrx/demodnfm/nfmdemod.h | 4 + plugins/channelrx/demodpacket/packetdemod.cpp | 10 + plugins/channelrx/demodpacket/packetdemod.h | 4 + plugins/channelrx/demodpager/pagerdemod.cpp | 10 + plugins/channelrx/demodpager/pagerdemod.h | 4 + .../demodradiosonde/radiosondedemod.cpp | 10 + .../demodradiosonde/radiosondedemod.h | 4 + plugins/channelrx/demodssb/ssbdemod.cpp | 10 + plugins/channelrx/demodssb/ssbdemod.h | 4 + plugins/channelrx/demodvor/vordemod.cpp | 10 + plugins/channelrx/demodvor/vordemod.h | 4 + plugins/channelrx/demodwfm/wfmdemod.cpp | 10 + plugins/channelrx/demodwfm/wfmdemod.h | 4 + plugins/channelrx/filesink/filesink.cpp | 10 + plugins/channelrx/filesink/filesink.h | 4 + plugins/channelrx/freqtracker/freqtracker.cpp | 10 + plugins/channelrx/freqtracker/freqtracker.h | 4 + plugins/channelrx/localsink/localsink.cpp | 10 + plugins/channelrx/localsink/localsink.h | 4 + plugins/channelrx/noisefigure/noisefigure.cpp | 10 + plugins/channelrx/noisefigure/noisefigure.h | 4 + .../radioastronomy/radioastronomy.cpp | 10 + .../channelrx/radioastronomy/radioastronomy.h | 4 + plugins/channelrx/radioclock/radioclock.cpp | 10 + plugins/channelrx/radioclock/radioclock.h | 4 + plugins/channelrx/remotesink/remotesink.cpp | 10 + plugins/channelrx/remotesink/remotesink.h | 4 + .../channelrx/sigmffilesink/sigmffilesink.cpp | 10 + .../channelrx/sigmffilesink/sigmffilesink.h | 4 + plugins/channelrx/udpsink/udpsink.cpp | 10 + plugins/channelrx/udpsink/udpsink.h | 4 + plugins/channeltx/filesource/filesource.cpp | 10 + plugins/channeltx/filesource/filesource.h | 4 + plugins/channeltx/localsource/localsource.cpp | 10 + plugins/channeltx/localsource/localsource.h | 4 + .../mod802.15.4/ieee_802_15_4_mod.cpp | 10 + .../channeltx/mod802.15.4/ieee_802_15_4_mod.h | 4 + plugins/channeltx/modais/aismod.cpp | 10 + plugins/channeltx/modais/aismod.h | 4 + plugins/channeltx/modam/ammod.cpp | 10 + plugins/channeltx/modam/ammod.h | 4 + plugins/channeltx/modatv/atvmod.cpp | 10 + plugins/channeltx/modatv/atvmod.h | 4 + .../channeltx/modchirpchat/chirpchatmod.cpp | 10 + plugins/channeltx/modchirpchat/chirpchatmod.h | 4 + plugins/channeltx/moddatv/datvmod.cpp | 10 + plugins/channeltx/moddatv/datvmod.h | 4 + plugins/channeltx/modfreedv/freedvmod.cpp | 10 + plugins/channeltx/modfreedv/freedvmod.h | 4 + plugins/channeltx/modnfm/nfmmod.cpp | 10 + plugins/channeltx/modnfm/nfmmod.h | 4 + plugins/channeltx/modpacket/packetmod.cpp | 10 + plugins/channeltx/modpacket/packetmod.h | 4 + plugins/channeltx/modssb/ssbmod.cpp | 10 + plugins/channeltx/modssb/ssbmod.h | 4 + plugins/channeltx/modwfm/wfmmod.cpp | 10 + plugins/channeltx/modwfm/wfmmod.h | 4 + .../channeltx/remotesource/remotesource.cpp | 10 + plugins/channeltx/remotesource/remotesource.h | 4 + plugins/channeltx/udpsource/udpsource.cpp | 10 + plugins/channeltx/udpsource/udpsource.h | 4 + plugins/feature/afc/afcgui.cpp | 8 + plugins/feature/afc/afcgui.h | 2 +- plugins/feature/ais/aisgui.cpp | 8 + plugins/feature/ais/aisgui.h | 2 +- .../feature/antennatools/antennatoolsgui.cpp | 8 + .../feature/antennatools/antennatoolsgui.h | 2 +- plugins/feature/aprs/aprsgui.cpp | 8 + plugins/feature/aprs/aprsgui.h | 2 +- .../demodanalyzer/demodanalyzergui.cpp | 8 + .../feature/demodanalyzer/demodanalyzergui.h | 2 +- .../gs232controller/gs232controllergui.cpp | 8 + .../gs232controller/gs232controllergui.h | 2 +- .../jogdialcontrollergui.cpp | 8 + .../jogdialcontroller/jogdialcontrollergui.h | 2 +- plugins/feature/map/mapgui.cpp | 8 + plugins/feature/map/mapgui.h | 2 +- plugins/feature/pertester/pertestergui.cpp | 8 + plugins/feature/pertester/pertestergui.h | 2 +- plugins/feature/radiosonde/radiosondegui.cpp | 8 + plugins/feature/radiosonde/radiosondegui.h | 2 +- .../feature/rigctlserver/rigctlservergui.cpp | 10 +- .../feature/rigctlserver/rigctlservergui.h | 2 +- .../satellitetracker/satellitetrackergui.cpp | 8 + .../satellitetracker/satellitetrackergui.h | 2 +- plugins/feature/simpleptt/simplepttgui.cpp | 8 + plugins/feature/simpleptt/simplepttgui.h | 2 +- .../feature/startracker/startrackergui.cpp | 8 + plugins/feature/startracker/startrackergui.h | 2 +- .../feature/vorlocalizer/vorlocalizergui.cpp | 8 + .../feature/vorlocalizer/vorlocalizergui.h | 2 +- .../bladerf2mimo/bladerf2mimogui.cpp | 2 +- .../samplemimo/bladerf2mimo/bladerf2mimogui.h | 1 - .../samplemimo/limesdrmimo/limesdrmimogui.cpp | 2 +- .../samplemimo/limesdrmimo/limesdrmimogui.h | 1 - plugins/samplemimo/metismiso/metismisogui.cpp | 2 +- plugins/samplemimo/metismiso/metismisogui.h | 1 - .../plutosdrmimo/plutosdrmimogui.cpp | 2 +- .../samplemimo/plutosdrmimo/plutosdrmimogui.h | 1 - plugins/samplemimo/testmi/testmigui.cpp | 2 +- plugins/samplemimo/testmi/testmigui.h | 1 - .../samplemimo/testmosync/testmosyncgui.cpp | 2 +- plugins/samplemimo/testmosync/testmosyncgui.h | 1 - plugins/samplemimo/xtrxmimo/xtrxmimogui.cpp | 2 +- plugins/samplemimo/xtrxmimo/xtrxmimogui.h | 1 - .../samplesink/audiooutput/audiooutputgui.cpp | 2 +- .../samplesink/audiooutput/audiooutputgui.h | 1 - .../bladerf1output/bladerf1outputgui.cpp | 2 +- .../bladerf1output/bladerf1outputgui.h | 1 - .../bladerf2output/bladerf2outputgui.cpp | 2 +- .../bladerf2output/bladerf2outputgui.h | 1 - .../samplesink/fileoutput/fileoutputgui.cpp | 2 +- plugins/samplesink/fileoutput/fileoutputgui.h | 1 - .../hackrfoutput/hackrfoutputgui.cpp | 2 +- .../samplesink/hackrfoutput/hackrfoutputgui.h | 1 - .../limesdroutput/limesdroutputgui.cpp | 2 +- .../limesdroutput/limesdroutputgui.h | 1 - .../samplesink/localoutput/localoutputgui.cpp | 2 +- .../samplesink/localoutput/localoutputgui.h | 1 - .../plutosdroutput/plutosdroutputgui.cpp | 2 +- .../plutosdroutput/plutosdroutputgui.h | 1 - .../remoteoutput/remoteoutputgui.cpp | 2 +- .../samplesink/remoteoutput/remoteoutputgui.h | 1 - .../soapysdroutput/soapysdroutputgui.cpp | 2 +- .../soapysdroutput/soapysdroutputgui.h | 1 - plugins/samplesink/testsink/testsinkgui.cpp | 2 +- plugins/samplesink/testsink/testsinkgui.h | 1 - .../samplesink/usrpoutput/usrpoutputgui.cpp | 2 +- plugins/samplesink/usrpoutput/usrpoutputgui.h | 1 - .../samplesink/xtrxoutput/xtrxoutputgui.cpp | 2 +- plugins/samplesink/xtrxoutput/xtrxoutputgui.h | 1 - plugins/samplesource/airspy/airspygui.cpp | 2 +- plugins/samplesource/airspy/airspygui.h | 1 - plugins/samplesource/airspyhf/airspyhfgui.cpp | 2 +- plugins/samplesource/airspyhf/airspyhfgui.h | 1 - .../samplesource/audioinput/audioinputgui.cpp | 2 +- .../samplesource/audioinput/audioinputgui.h | 1 - .../bladerf1input/bladerf1inputgui.cpp | 2 +- .../bladerf1input/bladerf1inputgui.h | 1 - .../bladerf2input/bladerf2inputgui.cpp | 2 +- .../bladerf2input/bladerf2inputgui.h | 1 - plugins/samplesource/fcdpro/fcdprogui.cpp | 2 +- plugins/samplesource/fcdpro/fcdprogui.h | 1 - .../samplesource/fcdproplus/fcdproplusgui.cpp | 2 +- .../samplesource/fcdproplus/fcdproplusgui.h | 1 - .../samplesource/fileinput/fileinputgui.cpp | 2 +- plugins/samplesource/fileinput/fileinputgui.h | 1 - .../hackrfinput/hackrfinputgui.cpp | 2 +- .../samplesource/hackrfinput/hackrfinputgui.h | 1 - plugins/samplesource/kiwisdr/kiwisdrgui.cpp | 2 +- plugins/samplesource/kiwisdr/kiwisdrgui.h | 1 - .../limesdrinput/limesdrinputgui.cpp | 2 +- .../limesdrinput/limesdrinputgui.h | 1 - .../samplesource/localinput/localinputgui.cpp | 2 +- .../samplesource/localinput/localinputgui.h | 1 - plugins/samplesource/perseus/perseusgui.cpp | 2 +- plugins/samplesource/perseus/perseusgui.h | 1 - .../plutosdrinput/plutosdrinputgui.cpp | 2 +- .../plutosdrinput/plutosdrinputgui.h | 1 - .../remoteinput/remoteinputgui.cpp | 2 +- .../samplesource/remoteinput/remoteinputgui.h | 1 - plugins/samplesource/rtlsdr/rtlsdrgui.cpp | 2 +- plugins/samplesource/rtlsdr/rtlsdrgui.h | 1 - plugins/samplesource/sdrplay/sdrplaygui.cpp | 2 +- plugins/samplesource/sdrplay/sdrplaygui.h | 1 - .../samplesource/sdrplayv3/sdrplayv3gui.cpp | 2 +- plugins/samplesource/sdrplayv3/sdrplayv3gui.h | 1 - .../sigmffileinput/sigmffileinputgui.cpp | 2 +- .../sigmffileinput/sigmffileinputgui.h | 1 - .../soapysdrinput/soapysdrinputgui.cpp | 2 +- .../soapysdrinput/soapysdrinputgui.h | 1 - .../samplesource/testsource/testsourcegui.cpp | 2 +- .../samplesource/testsource/testsourcegui.h | 1 - .../samplesource/usrpinput/usrpinputgui.cpp | 2 +- plugins/samplesource/usrpinput/usrpinputgui.h | 1 - .../samplesource/xtrxinput/xtrxinputgui.cpp | 2 +- plugins/samplesource/xtrxinput/xtrxinputgui.h | 1 - sdrbase/channel/channelapi.h | 12 + sdrbase/device/deviceapi.cpp | 1 + sdrbase/device/deviceapi.h | 4 + sdrbase/dsp/spectrumvis.h | 3 + sdrbase/feature/feature.h | 3 + sdrbase/maincore.cpp | 4 + sdrbase/maincore.h | 91 + sdrbase/resources/webapi/doc/html2/index.html | 3658 ++++++++++++++++- .../resources/webapi/doc/swagger/swagger.yaml | 248 +- sdrbase/webapi/webapiadapter.cpp | 251 ++ sdrbase/webapi/webapiadapter.h | 46 + sdrbase/webapi/webapiadapterinterface.cpp | 4 + sdrbase/webapi/webapiadapterinterface.h | 147 +- sdrbase/webapi/webapirequestmapper.cpp | 340 ++ sdrbase/webapi/webapirequestmapper.h | 6 +- sdrgui/device/devicegui.cpp | 12 + sdrgui/device/devicegui.h | 5 +- sdrgui/feature/featuregui.h | 2 + sdrgui/gui/glspectrum.h | 1 + sdrgui/mainspectrum/mainspectrumgui.cpp | 7 + sdrgui/mainspectrum/mainspectrumgui.h | 2 +- sdrgui/mainwindow.cpp | 60 + swagger/sdrangel/api/swagger/swagger.yaml | 248 +- swagger/sdrangel/code/html2/index.html | 3658 ++++++++++++++++- .../code/qt5/client/SWGDeviceSetApi.cpp | 337 ++ .../code/qt5/client/SWGDeviceSetApi.h | 31 + .../code/qt5/client/SWGFeatureSetApi.cpp | 111 + .../code/qt5/client/SWGFeatureSetApi.h | 11 + .../code/qt5/client/SWGModelFactory.h | 6 + .../code/qt5/client/SWGWorkspaceInfo.cpp | 108 + .../code/qt5/client/SWGWorkspaceInfo.h | 58 + 235 files changed, 10257 insertions(+), 115 deletions(-) create mode 100644 swagger/sdrangel/code/qt5/client/SWGWorkspaceInfo.cpp create mode 100644 swagger/sdrangel/code/qt5/client/SWGWorkspaceInfo.h diff --git a/plugins/channelmimo/beamsteeringcwmod/beamsteeringcwmod.cpp b/plugins/channelmimo/beamsteeringcwmod/beamsteeringcwmod.cpp index 97b7e8074..2915538d2 100644 --- a/plugins/channelmimo/beamsteeringcwmod/beamsteeringcwmod.cpp +++ b/plugins/channelmimo/beamsteeringcwmod/beamsteeringcwmod.cpp @@ -22,6 +22,7 @@ #include #include "SWGChannelSettings.h" +#include "SWGWorkspaceInfo.h" #include "device/deviceapi.h" #include "dsp/hbfilterchainconverter.h" @@ -283,6 +284,15 @@ int BeamSteeringCWMod::webapiSettingsGet( return 200; } +int BeamSteeringCWMod::webapiWorkspaceGet( + SWGSDRangel::SWGWorkspaceInfo& response, + QString& errorMessage) +{ + (void) errorMessage; + response.setIndex(m_settings.m_workspaceIndex); + return 200; +} + int BeamSteeringCWMod::webapiSettingsPutPatch( bool force, const QStringList& channelSettingsKeys, diff --git a/plugins/channelmimo/beamsteeringcwmod/beamsteeringcwmod.h b/plugins/channelmimo/beamsteeringcwmod/beamsteeringcwmod.h index 0d50058a3..90979c5c0 100644 --- a/plugins/channelmimo/beamsteeringcwmod/beamsteeringcwmod.h +++ b/plugins/channelmimo/beamsteeringcwmod/beamsteeringcwmod.h @@ -134,6 +134,10 @@ public: SWGSDRangel::SWGChannelSettings& response, QString& errorMessage); + virtual int webapiWorkspaceGet( + SWGSDRangel::SWGWorkspaceInfo& query, + QString& errorMessage); + static void webapiFormatChannelSettings( SWGSDRangel::SWGChannelSettings& response, const BeamSteeringCWModSettings& settings); diff --git a/plugins/channelmimo/interferometer/interferometer.cpp b/plugins/channelmimo/interferometer/interferometer.cpp index 47afd801d..82b1ca41a 100644 --- a/plugins/channelmimo/interferometer/interferometer.cpp +++ b/plugins/channelmimo/interferometer/interferometer.cpp @@ -22,6 +22,7 @@ #include #include "SWGChannelSettings.h" +#include "SWGWorkspaceInfo.h" #include "device/deviceapi.h" #include "dsp/hbfilterchainconverter.h" @@ -305,6 +306,15 @@ int Interferometer::webapiSettingsGet( return 200; } +int Interferometer::webapiWorkspaceGet( + SWGSDRangel::SWGWorkspaceInfo& response, + QString& errorMessage) +{ + (void) errorMessage; + response.setIndex(m_settings.m_workspaceIndex); + return 200; +} + int Interferometer::webapiSettingsPutPatch( bool force, const QStringList& channelSettingsKeys, diff --git a/plugins/channelmimo/interferometer/interferometer.h b/plugins/channelmimo/interferometer/interferometer.h index 403569e76..a6444f397 100644 --- a/plugins/channelmimo/interferometer/interferometer.h +++ b/plugins/channelmimo/interferometer/interferometer.h @@ -138,6 +138,10 @@ public: SWGSDRangel::SWGChannelSettings& response, QString& errorMessage); + virtual int webapiWorkspaceGet( + SWGSDRangel::SWGWorkspaceInfo& query, + QString& errorMessage); + static void webapiFormatChannelSettings( SWGSDRangel::SWGChannelSettings& response, const InterferometerSettings& settings); diff --git a/plugins/channelrx/chanalyzer/chanalyzer.cpp b/plugins/channelrx/chanalyzer/chanalyzer.cpp index b197902f3..59fe9de15 100644 --- a/plugins/channelrx/chanalyzer/chanalyzer.cpp +++ b/plugins/channelrx/chanalyzer/chanalyzer.cpp @@ -25,6 +25,7 @@ #include #include "SWGChannelSettings.h" +#include "SWGWorkspaceInfo.h" #include "SWGChannelAnalyzerSettings.h" #include "device/deviceapi.h" @@ -333,6 +334,15 @@ int ChannelAnalyzer::webapiSettingsGet( return 200; } +int ChannelAnalyzer::webapiWorkspaceGet( + SWGSDRangel::SWGWorkspaceInfo& response, + QString& errorMessage) +{ + (void) errorMessage; + response.setIndex(m_settings.m_workspaceIndex); + return 200; +} + int ChannelAnalyzer::webapiSettingsPutPatch( bool force, const QStringList& channelSettingsKeys, diff --git a/plugins/channelrx/chanalyzer/chanalyzer.h b/plugins/channelrx/chanalyzer/chanalyzer.h index 806594ca3..1074301f8 100644 --- a/plugins/channelrx/chanalyzer/chanalyzer.h +++ b/plugins/channelrx/chanalyzer/chanalyzer.h @@ -109,6 +109,10 @@ public: SWGSDRangel::SWGChannelSettings& response, QString& errorMessage); + virtual int webapiWorkspaceGet( + SWGSDRangel::SWGWorkspaceInfo& response, + QString& errorMessage); + virtual int webapiSettingsPutPatch( bool force, const QStringList& channelSettingsKeys, diff --git a/plugins/channelrx/demodadsb/adsbdemod.cpp b/plugins/channelrx/demodadsb/adsbdemod.cpp index 2764ff2e3..f8126e9bd 100644 --- a/plugins/channelrx/demodadsb/adsbdemod.cpp +++ b/plugins/channelrx/demodadsb/adsbdemod.cpp @@ -31,6 +31,7 @@ #include #include "SWGChannelSettings.h" +#include "SWGWorkspaceInfo.h" #include "SWGADSBDemodSettings.h" #include "SWGChannelReport.h" #include "SWGADSBDemodReport.h" @@ -374,6 +375,15 @@ int ADSBDemod::webapiSettingsGet( return 200; } +int ADSBDemod::webapiWorkspaceGet( + SWGSDRangel::SWGWorkspaceInfo& response, + QString& errorMessage) +{ + (void) errorMessage; + response.setIndex(m_settings.m_workspaceIndex); + return 200; +} + int ADSBDemod::webapiSettingsPutPatch( bool force, const QStringList& channelSettingsKeys, diff --git a/plugins/channelrx/demodadsb/adsbdemod.h b/plugins/channelrx/demodadsb/adsbdemod.h index 69214119f..c7e7b5c8a 100644 --- a/plugins/channelrx/demodadsb/adsbdemod.h +++ b/plugins/channelrx/demodadsb/adsbdemod.h @@ -98,6 +98,10 @@ public: SWGSDRangel::SWGChannelSettings& response, QString& errorMessage); + virtual int webapiWorkspaceGet( + SWGSDRangel::SWGWorkspaceInfo& response, + QString& errorMessage); + virtual int webapiSettingsPutPatch( bool force, const QStringList& channelSettingsKeys, diff --git a/plugins/channelrx/demodais/aisdemod.cpp b/plugins/channelrx/demodais/aisdemod.cpp index 67904a891..d2fb61529 100644 --- a/plugins/channelrx/demodais/aisdemod.cpp +++ b/plugins/channelrx/demodais/aisdemod.cpp @@ -29,6 +29,7 @@ #include #include "SWGChannelSettings.h" +#include "SWGWorkspaceInfo.h" #include "SWGChannelReport.h" #include "dsp/dspengine.h" @@ -418,6 +419,15 @@ int AISDemod::webapiSettingsGet( return 200; } +int AISDemod::webapiWorkspaceGet( + SWGSDRangel::SWGWorkspaceInfo& response, + QString& errorMessage) +{ + (void) errorMessage; + response.setIndex(m_settings.m_workspaceIndex); + return 200; +} + int AISDemod::webapiSettingsPutPatch( bool force, const QStringList& channelSettingsKeys, diff --git a/plugins/channelrx/demodais/aisdemod.h b/plugins/channelrx/demodais/aisdemod.h index 7e8d3017b..3fbc1d0fa 100644 --- a/plugins/channelrx/demodais/aisdemod.h +++ b/plugins/channelrx/demodais/aisdemod.h @@ -127,6 +127,10 @@ public: SWGSDRangel::SWGChannelSettings& response, QString& errorMessage); + virtual int webapiWorkspaceGet( + SWGSDRangel::SWGWorkspaceInfo& response, + QString& errorMessage); + virtual int webapiSettingsPutPatch( bool force, const QStringList& channelSettingsKeys, diff --git a/plugins/channelrx/demodam/amdemod.cpp b/plugins/channelrx/demodam/amdemod.cpp index 325aad209..dbbdda00c 100644 --- a/plugins/channelrx/demodam/amdemod.cpp +++ b/plugins/channelrx/demodam/amdemod.cpp @@ -28,6 +28,7 @@ #include #include "SWGChannelSettings.h" +#include "SWGWorkspaceInfo.h" #include "SWGAMDemodSettings.h" #include "SWGChannelReport.h" #include "SWGAMDemodReport.h" @@ -358,6 +359,15 @@ int AMDemod::webapiSettingsGet( return 200; } +int AMDemod::webapiWorkspaceGet( + SWGSDRangel::SWGWorkspaceInfo& response, + QString& errorMessage) +{ + (void) errorMessage; + response.setIndex(m_settings.m_workspaceIndex); + return 200; +} + int AMDemod::webapiSettingsPutPatch( bool force, const QStringList& channelSettingsKeys, diff --git a/plugins/channelrx/demodam/amdemod.h b/plugins/channelrx/demodam/amdemod.h index 156684802..ed1d49561 100644 --- a/plugins/channelrx/demodam/amdemod.h +++ b/plugins/channelrx/demodam/amdemod.h @@ -97,6 +97,10 @@ public: SWGSDRangel::SWGChannelSettings& response, QString& errorMessage); + virtual int webapiWorkspaceGet( + SWGSDRangel::SWGWorkspaceInfo& response, + QString& errorMessage); + virtual int webapiSettingsPutPatch( bool force, const QStringList& channelSettingsKeys, diff --git a/plugins/channelrx/demodapt/aptdemod.cpp b/plugins/channelrx/demodapt/aptdemod.cpp index 54b1a8f8f..419fa26bd 100644 --- a/plugins/channelrx/demodapt/aptdemod.cpp +++ b/plugins/channelrx/demodapt/aptdemod.cpp @@ -29,6 +29,7 @@ #include #include "SWGChannelSettings.h" +#include "SWGWorkspaceInfo.h" #include "SWGAPTDemodSettings.h" #include "SWGChannelReport.h" #include "SWGChannelActions.h" @@ -420,6 +421,15 @@ int APTDemod::webapiSettingsGet( return 200; } +int APTDemod::webapiWorkspaceGet( + SWGSDRangel::SWGWorkspaceInfo& response, + QString& errorMessage) +{ + (void) errorMessage; + response.setIndex(m_settings.m_workspaceIndex); + return 200; +} + int APTDemod::webapiSettingsPutPatch( bool force, const QStringList& channelSettingsKeys, diff --git a/plugins/channelrx/demodapt/aptdemod.h b/plugins/channelrx/demodapt/aptdemod.h index 65ef1ac13..aa580dcc5 100644 --- a/plugins/channelrx/demodapt/aptdemod.h +++ b/plugins/channelrx/demodapt/aptdemod.h @@ -232,6 +232,10 @@ public: SWGSDRangel::SWGChannelSettings& response, QString& errorMessage); + virtual int webapiWorkspaceGet( + SWGSDRangel::SWGWorkspaceInfo& response, + QString& errorMessage); + virtual int webapiSettingsPutPatch( bool force, const QStringList& channelSettingsKeys, diff --git a/plugins/channelrx/demodbfm/bfmdemod.cpp b/plugins/channelrx/demodbfm/bfmdemod.cpp index 97617df1d..ee65f9a1f 100644 --- a/plugins/channelrx/demodbfm/bfmdemod.cpp +++ b/plugins/channelrx/demodbfm/bfmdemod.cpp @@ -26,6 +26,7 @@ #include #include "SWGChannelSettings.h" +#include "SWGWorkspaceInfo.h" #include "SWGBFMDemodSettings.h" #include "SWGChannelReport.h" #include "SWGBFMDemodReport.h" @@ -307,6 +308,15 @@ int BFMDemod::webapiSettingsGet( return 200; } +int BFMDemod::webapiWorkspaceGet( + SWGSDRangel::SWGWorkspaceInfo& response, + QString& errorMessage) +{ + (void) errorMessage; + response.setIndex(m_settings.m_workspaceIndex); + return 200; +} + int BFMDemod::webapiSettingsPutPatch( bool force, const QStringList& channelSettingsKeys, diff --git a/plugins/channelrx/demodbfm/bfmdemod.h b/plugins/channelrx/demodbfm/bfmdemod.h index 49bbe0ac9..ccead9535 100644 --- a/plugins/channelrx/demodbfm/bfmdemod.h +++ b/plugins/channelrx/demodbfm/bfmdemod.h @@ -121,6 +121,10 @@ public: SWGSDRangel::SWGChannelSettings& response, QString& errorMessage); + virtual int webapiWorkspaceGet( + SWGSDRangel::SWGWorkspaceInfo& response, + QString& errorMessage); + virtual int webapiSettingsPutPatch( bool force, const QStringList& channelSettingsKeys, diff --git a/plugins/channelrx/demodchirpchat/chirpchatdemod.cpp b/plugins/channelrx/demodchirpchat/chirpchatdemod.cpp index a0f2b0ef8..36467f9b4 100644 --- a/plugins/channelrx/demodchirpchat/chirpchatdemod.cpp +++ b/plugins/channelrx/demodchirpchat/chirpchatdemod.cpp @@ -28,6 +28,7 @@ #include #include "SWGChannelSettings.h" +#include "SWGWorkspaceInfo.h" #include "SWGChannelReport.h" #include "SWGChirpChatDemodReport.h" @@ -510,6 +511,15 @@ int ChirpChatDemod::webapiSettingsGet( return 200; } +int ChirpChatDemod::webapiWorkspaceGet( + SWGSDRangel::SWGWorkspaceInfo& response, + QString& errorMessage) +{ + (void) errorMessage; + response.setIndex(m_settings.m_workspaceIndex); + return 200; +} + int ChirpChatDemod::webapiSettingsPutPatch( bool force, const QStringList& channelSettingsKeys, diff --git a/plugins/channelrx/demodchirpchat/chirpchatdemod.h b/plugins/channelrx/demodchirpchat/chirpchatdemod.h index d9dec0a35..9bb87e575 100644 --- a/plugins/channelrx/demodchirpchat/chirpchatdemod.h +++ b/plugins/channelrx/demodchirpchat/chirpchatdemod.h @@ -236,6 +236,10 @@ public: SWGSDRangel::SWGChannelSettings& response, QString& errorMessage); + virtual int webapiWorkspaceGet( + SWGSDRangel::SWGWorkspaceInfo& response, + QString& errorMessage); + virtual int webapiSettingsPutPatch( bool force, const QStringList& channelSettingsKeys, diff --git a/plugins/channelrx/demoddab/dabdemod.cpp b/plugins/channelrx/demoddab/dabdemod.cpp index 7319bc582..035eb3f35 100644 --- a/plugins/channelrx/demoddab/dabdemod.cpp +++ b/plugins/channelrx/demoddab/dabdemod.cpp @@ -28,6 +28,7 @@ #include #include "SWGChannelSettings.h" +#include "SWGWorkspaceInfo.h" #include "SWGDABDemodSettings.h" #include "SWGChannelReport.h" #include "SWGMapItem.h" @@ -422,6 +423,15 @@ int DABDemod::webapiSettingsGet( return 200; } +int DABDemod::webapiWorkspaceGet( + SWGSDRangel::SWGWorkspaceInfo& response, + QString& errorMessage) +{ + (void) errorMessage; + response.setIndex(m_settings.m_workspaceIndex); + return 200; +} + int DABDemod::webapiSettingsPutPatch( bool force, const QStringList& channelSettingsKeys, diff --git a/plugins/channelrx/demoddab/dabdemod.h b/plugins/channelrx/demoddab/dabdemod.h index a177a3872..bae6c38ee 100644 --- a/plugins/channelrx/demoddab/dabdemod.h +++ b/plugins/channelrx/demoddab/dabdemod.h @@ -347,6 +347,10 @@ public: SWGSDRangel::SWGChannelSettings& response, QString& errorMessage); + virtual int webapiWorkspaceGet( + SWGSDRangel::SWGWorkspaceInfo& response, + QString& errorMessage); + virtual int webapiSettingsPutPatch( bool force, const QStringList& channelSettingsKeys, diff --git a/plugins/channelrx/demoddatv/datvdemod.cpp b/plugins/channelrx/demoddatv/datvdemod.cpp index e50a04956..b2f7c68fb 100644 --- a/plugins/channelrx/demoddatv/datvdemod.cpp +++ b/plugins/channelrx/demoddatv/datvdemod.cpp @@ -23,6 +23,7 @@ #include #include "SWGChannelSettings.h" +#include "SWGWorkspaceInfo.h" #include "SWGDATVDemodSettings.h" #include "SWGChannelReport.h" @@ -307,6 +308,15 @@ int DATVDemod::webapiSettingsGet( return 200; } +int DATVDemod::webapiWorkspaceGet( + SWGSDRangel::SWGWorkspaceInfo& response, + QString& errorMessage) +{ + (void) errorMessage; + response.setIndex(m_settings.m_workspaceIndex); + return 200; +} + int DATVDemod::webapiReportGet( SWGSDRangel::SWGChannelReport& response, QString& errorMessage) diff --git a/plugins/channelrx/demoddatv/datvdemod.h b/plugins/channelrx/demoddatv/datvdemod.h index bd90cf38b..8c3629767 100644 --- a/plugins/channelrx/demoddatv/datvdemod.h +++ b/plugins/channelrx/demoddatv/datvdemod.h @@ -78,6 +78,10 @@ public: SWGSDRangel::SWGChannelSettings& response, QString& errorMessage); + virtual int webapiWorkspaceGet( + SWGSDRangel::SWGWorkspaceInfo& response, + QString& errorMessage); + virtual int webapiSettingsPutPatch( bool force, const QStringList& channelSettingsKeys, diff --git a/plugins/channelrx/demoddsd/dsddemod.cpp b/plugins/channelrx/demoddsd/dsddemod.cpp index 6ea650bdd..00307028b 100644 --- a/plugins/channelrx/demoddsd/dsddemod.cpp +++ b/plugins/channelrx/demoddsd/dsddemod.cpp @@ -29,6 +29,7 @@ #include #include "SWGChannelSettings.h" +#include "SWGWorkspaceInfo.h" #include "SWGDSDDemodSettings.h" #include "SWGChannelReport.h" #include "SWGDSDDemodReport.h" @@ -368,6 +369,15 @@ int DSDDemod::webapiSettingsGet( return 200; } +int DSDDemod::webapiWorkspaceGet( + SWGSDRangel::SWGWorkspaceInfo& response, + QString& errorMessage) +{ + (void) errorMessage; + response.setIndex(m_settings.m_workspaceIndex); + return 200; +} + int DSDDemod::webapiSettingsPutPatch( bool force, const QStringList& channelSettingsKeys, diff --git a/plugins/channelrx/demoddsd/dsddemod.h b/plugins/channelrx/demoddsd/dsddemod.h index 620138826..dd5584d1d 100644 --- a/plugins/channelrx/demoddsd/dsddemod.h +++ b/plugins/channelrx/demoddsd/dsddemod.h @@ -98,6 +98,10 @@ public: SWGSDRangel::SWGChannelSettings& response, QString& errorMessage); + virtual int webapiWorkspaceGet( + SWGSDRangel::SWGWorkspaceInfo& response, + QString& errorMessage); + virtual int webapiSettingsPutPatch( bool force, const QStringList& channelSettingsKeys, diff --git a/plugins/channelrx/demodfreedv/freedvdemod.cpp b/plugins/channelrx/demodfreedv/freedvdemod.cpp index dfa55ed6d..72e1fab64 100644 --- a/plugins/channelrx/demodfreedv/freedvdemod.cpp +++ b/plugins/channelrx/demodfreedv/freedvdemod.cpp @@ -22,6 +22,7 @@ #include #include "SWGChannelSettings.h" +#include "SWGWorkspaceInfo.h" #include "SWGFreeDVDemodSettings.h" #include "SWGChannelReport.h" #include "SWGFreeDVDemodReport.h" @@ -309,6 +310,15 @@ int FreeDVDemod::webapiSettingsGet( return 200; } +int FreeDVDemod::webapiWorkspaceGet( + SWGSDRangel::SWGWorkspaceInfo& response, + QString& errorMessage) +{ + (void) errorMessage; + response.setIndex(m_settings.m_workspaceIndex); + return 200; +} + int FreeDVDemod::webapiSettingsPutPatch( bool force, const QStringList& channelSettingsKeys, diff --git a/plugins/channelrx/demodfreedv/freedvdemod.h b/plugins/channelrx/demodfreedv/freedvdemod.h index 078a840c8..226b6173c 100644 --- a/plugins/channelrx/demodfreedv/freedvdemod.h +++ b/plugins/channelrx/demodfreedv/freedvdemod.h @@ -125,6 +125,10 @@ public: SWGSDRangel::SWGChannelSettings& response, QString& errorMessage); + virtual int webapiWorkspaceGet( + SWGSDRangel::SWGWorkspaceInfo& response, + QString& errorMessage); + virtual int webapiSettingsPutPatch( bool force, const QStringList& channelSettingsKeys, diff --git a/plugins/channelrx/demodnfm/nfmdemod.cpp b/plugins/channelrx/demodnfm/nfmdemod.cpp index b532c7fe7..39c323b6d 100644 --- a/plugins/channelrx/demodnfm/nfmdemod.cpp +++ b/plugins/channelrx/demodnfm/nfmdemod.cpp @@ -27,6 +27,7 @@ #include #include "SWGChannelSettings.h" +#include "SWGWorkspaceInfo.h" #include "SWGNFMDemodSettings.h" #include "SWGChannelReport.h" #include "SWGNFMDemodReport.h" @@ -358,6 +359,15 @@ int NFMDemod::webapiSettingsGet( return 200; } +int NFMDemod::webapiWorkspaceGet( + SWGSDRangel::SWGWorkspaceInfo& response, + QString& errorMessage) +{ + (void) errorMessage; + response.setIndex(m_settings.m_workspaceIndex); + return 200; +} + int NFMDemod::webapiSettingsPutPatch( bool force, const QStringList& channelSettingsKeys, diff --git a/plugins/channelrx/demodnfm/nfmdemod.h b/plugins/channelrx/demodnfm/nfmdemod.h index 546df2274..4e311d12c 100644 --- a/plugins/channelrx/demodnfm/nfmdemod.h +++ b/plugins/channelrx/demodnfm/nfmdemod.h @@ -97,6 +97,10 @@ public: SWGSDRangel::SWGChannelSettings& response, QString& errorMessage); + virtual int webapiWorkspaceGet( + SWGSDRangel::SWGWorkspaceInfo& response, + QString& errorMessage); + virtual int webapiSettingsPutPatch( bool force, const QStringList& channelSettingsKeys, diff --git a/plugins/channelrx/demodpacket/packetdemod.cpp b/plugins/channelrx/demodpacket/packetdemod.cpp index c7dfd2038..eff76d98d 100644 --- a/plugins/channelrx/demodpacket/packetdemod.cpp +++ b/plugins/channelrx/demodpacket/packetdemod.cpp @@ -29,6 +29,7 @@ #include #include "SWGChannelSettings.h" +#include "SWGWorkspaceInfo.h" #include "SWGPacketDemodSettings.h" #include "SWGChannelReport.h" #include "SWGMapItem.h" @@ -407,6 +408,15 @@ int PacketDemod::webapiSettingsGet( return 200; } +int PacketDemod::webapiWorkspaceGet( + SWGSDRangel::SWGWorkspaceInfo& response, + QString& errorMessage) +{ + (void) errorMessage; + response.setIndex(m_settings.m_workspaceIndex); + return 200; +} + int PacketDemod::webapiSettingsPutPatch( bool force, const QStringList& channelSettingsKeys, diff --git a/plugins/channelrx/demodpacket/packetdemod.h b/plugins/channelrx/demodpacket/packetdemod.h index 296f1503a..f4c8ca4d5 100644 --- a/plugins/channelrx/demodpacket/packetdemod.h +++ b/plugins/channelrx/demodpacket/packetdemod.h @@ -101,6 +101,10 @@ public: SWGSDRangel::SWGChannelSettings& response, QString& errorMessage); + virtual int webapiWorkspaceGet( + SWGSDRangel::SWGWorkspaceInfo& response, + QString& errorMessage); + virtual int webapiSettingsPutPatch( bool force, const QStringList& channelSettingsKeys, diff --git a/plugins/channelrx/demodpager/pagerdemod.cpp b/plugins/channelrx/demodpager/pagerdemod.cpp index eaa12a793..3e0f5a3de 100644 --- a/plugins/channelrx/demodpager/pagerdemod.cpp +++ b/plugins/channelrx/demodpager/pagerdemod.cpp @@ -25,6 +25,7 @@ #include #include "SWGChannelSettings.h" +#include "SWGWorkspaceInfo.h" #include "SWGChannelReport.h" #include "dsp/dspengine.h" @@ -392,6 +393,15 @@ int PagerDemod::webapiSettingsGet( return 200; } +int PagerDemod::webapiWorkspaceGet( + SWGSDRangel::SWGWorkspaceInfo& response, + QString& errorMessage) +{ + (void) errorMessage; + response.setIndex(m_settings.m_workspaceIndex); + return 200; +} + int PagerDemod::webapiSettingsPutPatch( bool force, const QStringList& channelSettingsKeys, diff --git a/plugins/channelrx/demodpager/pagerdemod.h b/plugins/channelrx/demodpager/pagerdemod.h index a283cc5d0..cc8e1e368 100644 --- a/plugins/channelrx/demodpager/pagerdemod.h +++ b/plugins/channelrx/demodpager/pagerdemod.h @@ -156,6 +156,10 @@ public: SWGSDRangel::SWGChannelSettings& response, QString& errorMessage); + virtual int webapiWorkspaceGet( + SWGSDRangel::SWGWorkspaceInfo& response, + QString& errorMessage); + virtual int webapiSettingsPutPatch( bool force, const QStringList& channelSettingsKeys, diff --git a/plugins/channelrx/demodradiosonde/radiosondedemod.cpp b/plugins/channelrx/demodradiosonde/radiosondedemod.cpp index 86944c1ea..b9e1f9e8f 100644 --- a/plugins/channelrx/demodradiosonde/radiosondedemod.cpp +++ b/plugins/channelrx/demodradiosonde/radiosondedemod.cpp @@ -29,6 +29,7 @@ #include #include "SWGChannelSettings.h" +#include "SWGWorkspaceInfo.h" #include "SWGChannelReport.h" #include "dsp/dspengine.h" @@ -439,6 +440,15 @@ int RadiosondeDemod::webapiSettingsGet( return 200; } +int RadiosondeDemod::webapiWorkspaceGet( + SWGSDRangel::SWGWorkspaceInfo& response, + QString& errorMessage) +{ + (void) errorMessage; + response.setIndex(m_settings.m_workspaceIndex); + return 200; +} + int RadiosondeDemod::webapiSettingsPutPatch( bool force, const QStringList& channelSettingsKeys, diff --git a/plugins/channelrx/demodradiosonde/radiosondedemod.h b/plugins/channelrx/demodradiosonde/radiosondedemod.h index c103794aa..bf4e91ae6 100644 --- a/plugins/channelrx/demodradiosonde/radiosondedemod.h +++ b/plugins/channelrx/demodradiosonde/radiosondedemod.h @@ -136,6 +136,10 @@ public: SWGSDRangel::SWGChannelSettings& response, QString& errorMessage); + virtual int webapiWorkspaceGet( + SWGSDRangel::SWGWorkspaceInfo& response, + QString& errorMessage); + virtual int webapiSettingsPutPatch( bool force, const QStringList& channelSettingsKeys, diff --git a/plugins/channelrx/demodssb/ssbdemod.cpp b/plugins/channelrx/demodssb/ssbdemod.cpp index 3cd877eb0..67ccc1741 100644 --- a/plugins/channelrx/demodssb/ssbdemod.cpp +++ b/plugins/channelrx/demodssb/ssbdemod.cpp @@ -28,6 +28,7 @@ #include #include "SWGChannelSettings.h" +#include "SWGWorkspaceInfo.h" #include "SWGSSBDemodSettings.h" #include "SWGChannelReport.h" #include "SWGSSBDemodReport.h" @@ -367,6 +368,15 @@ int SSBDemod::webapiSettingsGet( return 200; } +int SSBDemod::webapiWorkspaceGet( + SWGSDRangel::SWGWorkspaceInfo& response, + QString& errorMessage) +{ + (void) errorMessage; + response.setIndex(m_settings.m_workspaceIndex); + return 200; +} + int SSBDemod::webapiSettingsPutPatch( bool force, const QStringList& channelSettingsKeys, diff --git a/plugins/channelrx/demodssb/ssbdemod.h b/plugins/channelrx/demodssb/ssbdemod.h index 119dc700d..5d8489a3c 100644 --- a/plugins/channelrx/demodssb/ssbdemod.h +++ b/plugins/channelrx/demodssb/ssbdemod.h @@ -111,6 +111,10 @@ public: SWGSDRangel::SWGChannelSettings& response, QString& errorMessage); + virtual int webapiWorkspaceGet( + SWGSDRangel::SWGWorkspaceInfo& response, + QString& errorMessage); + virtual int webapiSettingsPutPatch( bool force, const QStringList& channelSettingsKeys, diff --git a/plugins/channelrx/demodvor/vordemod.cpp b/plugins/channelrx/demodvor/vordemod.cpp index 2397ac790..1ac469377 100644 --- a/plugins/channelrx/demodvor/vordemod.cpp +++ b/plugins/channelrx/demodvor/vordemod.cpp @@ -27,6 +27,7 @@ #include #include "SWGChannelSettings.h" +#include "SWGWorkspaceInfo.h" #include "SWGVORDemodSettings.h" #include "SWGChannelReport.h" #include "SWGVORDemodReport.h" @@ -352,6 +353,15 @@ int VORDemod::webapiSettingsGet( return 200; } +int VORDemod::webapiWorkspaceGet( + SWGSDRangel::SWGWorkspaceInfo& response, + QString& errorMessage) +{ + (void) errorMessage; + response.setIndex(m_settings.m_workspaceIndex); + return 200; +} + int VORDemod::webapiSettingsPutPatch( bool force, const QStringList& channelSettingsKeys, diff --git a/plugins/channelrx/demodvor/vordemod.h b/plugins/channelrx/demodvor/vordemod.h index c1bf8dcaf..ec4e51a61 100644 --- a/plugins/channelrx/demodvor/vordemod.h +++ b/plugins/channelrx/demodvor/vordemod.h @@ -99,6 +99,10 @@ public: SWGSDRangel::SWGChannelSettings& response, QString& errorMessage); + virtual int webapiWorkspaceGet( + SWGSDRangel::SWGWorkspaceInfo& response, + QString& errorMessage); + virtual int webapiSettingsPutPatch( bool force, const QStringList& channelSettingsKeys, diff --git a/plugins/channelrx/demodwfm/wfmdemod.cpp b/plugins/channelrx/demodwfm/wfmdemod.cpp index aab213164..719353708 100644 --- a/plugins/channelrx/demodwfm/wfmdemod.cpp +++ b/plugins/channelrx/demodwfm/wfmdemod.cpp @@ -28,6 +28,7 @@ #include #include "SWGChannelSettings.h" +#include "SWGWorkspaceInfo.h" #include "SWGWFMDemodSettings.h" #include "SWGChannelReport.h" #include "SWGWFMDemodReport.h" @@ -330,6 +331,15 @@ int WFMDemod::webapiSettingsGet( return 200; } +int WFMDemod::webapiWorkspaceGet( + SWGSDRangel::SWGWorkspaceInfo& response, + QString& errorMessage) +{ + (void) errorMessage; + response.setIndex(m_settings.m_workspaceIndex); + return 200; +} + int WFMDemod::webapiSettingsPutPatch( bool force, const QStringList& channelSettingsKeys, diff --git a/plugins/channelrx/demodwfm/wfmdemod.h b/plugins/channelrx/demodwfm/wfmdemod.h index 9002f6932..73f407a5b 100644 --- a/plugins/channelrx/demodwfm/wfmdemod.h +++ b/plugins/channelrx/demodwfm/wfmdemod.h @@ -102,6 +102,10 @@ public: SWGSDRangel::SWGChannelSettings& response, QString& errorMessage); + virtual int webapiWorkspaceGet( + SWGSDRangel::SWGWorkspaceInfo& response, + QString& errorMessage); + virtual int webapiSettingsPutPatch( bool force, const QStringList& channelSettingsKeys, diff --git a/plugins/channelrx/filesink/filesink.cpp b/plugins/channelrx/filesink/filesink.cpp index 96b492628..ed91febae 100644 --- a/plugins/channelrx/filesink/filesink.cpp +++ b/plugins/channelrx/filesink/filesink.cpp @@ -24,6 +24,7 @@ #include #include "SWGChannelSettings.h" +#include "SWGWorkspaceInfo.h" #include "SWGChannelReport.h" #include "SWGChannelActions.h" @@ -394,6 +395,15 @@ int FileSink::webapiSettingsGet( return 200; } +int FileSink::webapiWorkspaceGet( + SWGSDRangel::SWGWorkspaceInfo& response, + QString& errorMessage) +{ + (void) errorMessage; + response.setIndex(m_settings.m_workspaceIndex); + return 200; +} + int FileSink::webapiSettingsPutPatch( bool force, const QStringList& channelSettingsKeys, diff --git a/plugins/channelrx/filesink/filesink.h b/plugins/channelrx/filesink/filesink.h index a2b97d9a7..95ec66ffb 100644 --- a/plugins/channelrx/filesink/filesink.h +++ b/plugins/channelrx/filesink/filesink.h @@ -117,6 +117,10 @@ public: SWGSDRangel::SWGChannelSettings& response, QString& errorMessage); + virtual int webapiWorkspaceGet( + SWGSDRangel::SWGWorkspaceInfo& response, + QString& errorMessage); + virtual int webapiSettingsPutPatch( bool force, const QStringList& channelSettingsKeys, diff --git a/plugins/channelrx/freqtracker/freqtracker.cpp b/plugins/channelrx/freqtracker/freqtracker.cpp index dfc6b753f..8f0f0e07e 100644 --- a/plugins/channelrx/freqtracker/freqtracker.cpp +++ b/plugins/channelrx/freqtracker/freqtracker.cpp @@ -29,6 +29,7 @@ #include #include "SWGChannelSettings.h" +#include "SWGWorkspaceInfo.h" #include "SWGFreqTrackerSettings.h" #include "SWGChannelReport.h" #include "SWGFreqTrackerReport.h" @@ -344,6 +345,15 @@ int FreqTracker::webapiSettingsGet( return 200; } +int FreqTracker::webapiWorkspaceGet( + SWGSDRangel::SWGWorkspaceInfo& response, + QString& errorMessage) +{ + (void) errorMessage; + response.setIndex(m_settings.m_workspaceIndex); + return 200; +} + int FreqTracker::webapiSettingsPutPatch( bool force, const QStringList& channelSettingsKeys, diff --git a/plugins/channelrx/freqtracker/freqtracker.h b/plugins/channelrx/freqtracker/freqtracker.h index 628cd76cb..5a0a2e7eb 100644 --- a/plugins/channelrx/freqtracker/freqtracker.h +++ b/plugins/channelrx/freqtracker/freqtracker.h @@ -96,6 +96,10 @@ public: SWGSDRangel::SWGChannelSettings& response, QString& errorMessage); + virtual int webapiWorkspaceGet( + SWGSDRangel::SWGWorkspaceInfo& response, + QString& errorMessage); + virtual int webapiSettingsPutPatch( bool force, const QStringList& channelSettingsKeys, diff --git a/plugins/channelrx/localsink/localsink.cpp b/plugins/channelrx/localsink/localsink.cpp index 758f9e1fa..3eb87911b 100644 --- a/plugins/channelrx/localsink/localsink.cpp +++ b/plugins/channelrx/localsink/localsink.cpp @@ -24,6 +24,7 @@ #include #include "SWGChannelSettings.h" +#include "SWGWorkspaceInfo.h" #include "util/simpleserializer.h" #include "dsp/dspcommands.h" @@ -368,6 +369,15 @@ int LocalSink::webapiSettingsGet( return 200; } +int LocalSink::webapiWorkspaceGet( + SWGSDRangel::SWGWorkspaceInfo& response, + QString& errorMessage) +{ + (void) errorMessage; + response.setIndex(m_settings.m_workspaceIndex); + return 200; +} + int LocalSink::webapiSettingsPutPatch( bool force, const QStringList& channelSettingsKeys, diff --git a/plugins/channelrx/localsink/localsink.h b/plugins/channelrx/localsink/localsink.h index d25a2972a..d5ac97f47 100644 --- a/plugins/channelrx/localsink/localsink.h +++ b/plugins/channelrx/localsink/localsink.h @@ -97,6 +97,10 @@ public: SWGSDRangel::SWGChannelSettings& response, QString& errorMessage); + virtual int webapiWorkspaceGet( + SWGSDRangel::SWGWorkspaceInfo& response, + QString& errorMessage); + virtual int webapiSettingsPutPatch( bool force, const QStringList& channelSettingsKeys, diff --git a/plugins/channelrx/noisefigure/noisefigure.cpp b/plugins/channelrx/noisefigure/noisefigure.cpp index ff5f3160d..c5aa2a23f 100644 --- a/plugins/channelrx/noisefigure/noisefigure.cpp +++ b/plugins/channelrx/noisefigure/noisefigure.cpp @@ -31,6 +31,7 @@ #include #include "SWGChannelSettings.h" +#include "SWGWorkspaceInfo.h" #include "SWGChannelReport.h" #include "dsp/dspengine.h" @@ -608,6 +609,15 @@ int NoiseFigure::webapiSettingsGet( return 200; } +int NoiseFigure::webapiWorkspaceGet( + SWGSDRangel::SWGWorkspaceInfo& response, + QString& errorMessage) +{ + (void) errorMessage; + response.setIndex(m_settings.m_workspaceIndex); + return 200; +} + int NoiseFigure::webapiSettingsPutPatch( bool force, const QStringList& channelSettingsKeys, diff --git a/plugins/channelrx/noisefigure/noisefigure.h b/plugins/channelrx/noisefigure/noisefigure.h index f64437436..57f042ba8 100644 --- a/plugins/channelrx/noisefigure/noisefigure.h +++ b/plugins/channelrx/noisefigure/noisefigure.h @@ -206,6 +206,10 @@ public: SWGSDRangel::SWGChannelSettings& response, QString& errorMessage); + virtual int webapiWorkspaceGet( + SWGSDRangel::SWGWorkspaceInfo& response, + QString& errorMessage); + virtual int webapiSettingsPutPatch( bool force, const QStringList& channelSettingsKeys, diff --git a/plugins/channelrx/radioastronomy/radioastronomy.cpp b/plugins/channelrx/radioastronomy/radioastronomy.cpp index 98c8bf794..9b0ad59c2 100644 --- a/plugins/channelrx/radioastronomy/radioastronomy.cpp +++ b/plugins/channelrx/radioastronomy/radioastronomy.cpp @@ -31,6 +31,7 @@ #include #include "SWGChannelSettings.h" +#include "SWGWorkspaceInfo.h" #include "SWGChannelReport.h" #include "SWGChannelActions.h" #include "SWGRadioAstronomyActions.h" @@ -840,6 +841,15 @@ int RadioAstronomy::webapiSettingsGet( return 200; } +int RadioAstronomy::webapiWorkspaceGet( + SWGSDRangel::SWGWorkspaceInfo& response, + QString& errorMessage) +{ + (void) errorMessage; + response.setIndex(m_settings.m_workspaceIndex); + return 200; +} + int RadioAstronomy::webapiSettingsPutPatch( bool force, const QStringList& channelSettingsKeys, diff --git a/plugins/channelrx/radioastronomy/radioastronomy.h b/plugins/channelrx/radioastronomy/radioastronomy.h index aac4fc559..7a2e5ac93 100644 --- a/plugins/channelrx/radioastronomy/radioastronomy.h +++ b/plugins/channelrx/radioastronomy/radioastronomy.h @@ -380,6 +380,10 @@ public: SWGSDRangel::SWGChannelSettings& response, QString& errorMessage) override; + virtual int webapiWorkspaceGet( + SWGSDRangel::SWGWorkspaceInfo& response, + QString& errorMessage); + virtual int webapiSettingsPutPatch( bool force, const QStringList& channelSettingsKeys, diff --git a/plugins/channelrx/radioclock/radioclock.cpp b/plugins/channelrx/radioclock/radioclock.cpp index bf0859890..ad93c8dac 100644 --- a/plugins/channelrx/radioclock/radioclock.cpp +++ b/plugins/channelrx/radioclock/radioclock.cpp @@ -29,6 +29,7 @@ #include #include "SWGChannelSettings.h" +#include "SWGWorkspaceInfo.h" #include "SWGChannelReport.h" #include "dsp/dspengine.h" @@ -309,6 +310,15 @@ int RadioClock::webapiSettingsGet( return 200; } +int RadioClock::webapiWorkspaceGet( + SWGSDRangel::SWGWorkspaceInfo& response, + QString& errorMessage) +{ + (void) errorMessage; + response.setIndex(m_settings.m_workspaceIndex); + return 200; +} + int RadioClock::webapiSettingsPutPatch( bool force, const QStringList& channelSettingsKeys, diff --git a/plugins/channelrx/radioclock/radioclock.h b/plugins/channelrx/radioclock/radioclock.h index af0558895..9ed1b3b50 100644 --- a/plugins/channelrx/radioclock/radioclock.h +++ b/plugins/channelrx/radioclock/radioclock.h @@ -146,6 +146,10 @@ public: SWGSDRangel::SWGChannelSettings& response, QString& errorMessage); + virtual int webapiWorkspaceGet( + SWGSDRangel::SWGWorkspaceInfo& response, + QString& errorMessage); + virtual int webapiSettingsPutPatch( bool force, const QStringList& channelSettingsKeys, diff --git a/plugins/channelrx/remotesink/remotesink.cpp b/plugins/channelrx/remotesink/remotesink.cpp index bbba6c7f4..d5eaf261c 100644 --- a/plugins/channelrx/remotesink/remotesink.cpp +++ b/plugins/channelrx/remotesink/remotesink.cpp @@ -29,6 +29,7 @@ #include #include "SWGChannelSettings.h" +#include "SWGWorkspaceInfo.h" #include "util/simpleserializer.h" #include "dsp/dspcommands.h" @@ -338,6 +339,15 @@ int RemoteSink::webapiSettingsGet( return 200; } +int RemoteSink::webapiWorkspaceGet( + SWGSDRangel::SWGWorkspaceInfo& response, + QString& errorMessage) +{ + (void) errorMessage; + response.setIndex(m_settings.m_workspaceIndex); + return 200; +} + int RemoteSink::webapiSettingsPutPatch( bool force, const QStringList& channelSettingsKeys, diff --git a/plugins/channelrx/remotesink/remotesink.h b/plugins/channelrx/remotesink/remotesink.h index 476ed6677..13debfd79 100644 --- a/plugins/channelrx/remotesink/remotesink.h +++ b/plugins/channelrx/remotesink/remotesink.h @@ -98,6 +98,10 @@ public: SWGSDRangel::SWGChannelSettings& response, QString& errorMessage); + virtual int webapiWorkspaceGet( + SWGSDRangel::SWGWorkspaceInfo& response, + QString& errorMessage); + virtual int webapiSettingsPutPatch( bool force, const QStringList& channelSettingsKeys, diff --git a/plugins/channelrx/sigmffilesink/sigmffilesink.cpp b/plugins/channelrx/sigmffilesink/sigmffilesink.cpp index a2879bead..6057e16f2 100644 --- a/plugins/channelrx/sigmffilesink/sigmffilesink.cpp +++ b/plugins/channelrx/sigmffilesink/sigmffilesink.cpp @@ -24,6 +24,7 @@ #include #include "SWGChannelSettings.h" +#include "SWGWorkspaceInfo.h" #include "SWGChannelReport.h" #include "SWGChannelActions.h" @@ -394,6 +395,15 @@ int SigMFFileSink::webapiSettingsGet( return 200; } +int SigMFFileSink::webapiWorkspaceGet( + SWGSDRangel::SWGWorkspaceInfo& response, + QString& errorMessage) +{ + (void) errorMessage; + response.setIndex(m_settings.m_workspaceIndex); + return 200; +} + int SigMFFileSink::webapiSettingsPutPatch( bool force, const QStringList& channelSettingsKeys, diff --git a/plugins/channelrx/sigmffilesink/sigmffilesink.h b/plugins/channelrx/sigmffilesink/sigmffilesink.h index 4923c9fc4..75b1785ee 100644 --- a/plugins/channelrx/sigmffilesink/sigmffilesink.h +++ b/plugins/channelrx/sigmffilesink/sigmffilesink.h @@ -117,6 +117,10 @@ public: SWGSDRangel::SWGChannelSettings& response, QString& errorMessage); + virtual int webapiWorkspaceGet( + SWGSDRangel::SWGWorkspaceInfo& response, + QString& errorMessage); + virtual int webapiSettingsPutPatch( bool force, const QStringList& channelSettingsKeys, diff --git a/plugins/channelrx/udpsink/udpsink.cpp b/plugins/channelrx/udpsink/udpsink.cpp index 81a90f9c3..86b7df320 100644 --- a/plugins/channelrx/udpsink/udpsink.cpp +++ b/plugins/channelrx/udpsink/udpsink.cpp @@ -24,6 +24,7 @@ #include #include "SWGChannelSettings.h" +#include "SWGWorkspaceInfo.h" #include "SWGUDPSinkSettings.h" #include "SWGChannelReport.h" #include "SWGUDPSinkReport.h" @@ -326,6 +327,15 @@ int UDPSink::webapiSettingsGet( return 200; } +int UDPSink::webapiWorkspaceGet( + SWGSDRangel::SWGWorkspaceInfo& response, + QString& errorMessage) +{ + (void) errorMessage; + response.setIndex(m_settings.m_workspaceIndex); + return 200; +} + int UDPSink::webapiSettingsPutPatch( bool force, const QStringList& channelSettingsKeys, diff --git a/plugins/channelrx/udpsink/udpsink.h b/plugins/channelrx/udpsink/udpsink.h index 6a5dac3db..14b09b877 100644 --- a/plugins/channelrx/udpsink/udpsink.h +++ b/plugins/channelrx/udpsink/udpsink.h @@ -103,6 +103,10 @@ public: SWGSDRangel::SWGChannelSettings& response, QString& errorMessage); + virtual int webapiWorkspaceGet( + SWGSDRangel::SWGWorkspaceInfo& response, + QString& errorMessage); + virtual int webapiSettingsPutPatch( bool force, const QStringList& channelSettingsKeys, diff --git a/plugins/channeltx/filesource/filesource.cpp b/plugins/channeltx/filesource/filesource.cpp index fcce597f2..9830cb0f9 100644 --- a/plugins/channeltx/filesource/filesource.cpp +++ b/plugins/channeltx/filesource/filesource.cpp @@ -24,6 +24,7 @@ #include #include "SWGChannelSettings.h" +#include "SWGWorkspaceInfo.h" #include "SWGChannelReport.h" #include "SWGChannelActions.h" #include "SWGFileSourceReport.h" @@ -322,6 +323,15 @@ int FileSource::webapiSettingsGet( return 200; } +int FileSource::webapiWorkspaceGet( + SWGSDRangel::SWGWorkspaceInfo& response, + QString& errorMessage) +{ + (void) errorMessage; + response.setIndex(m_settings.m_workspaceIndex); + return 200; +} + int FileSource::webapiSettingsPutPatch( bool force, const QStringList& channelSettingsKeys, diff --git a/plugins/channeltx/filesource/filesource.h b/plugins/channeltx/filesource/filesource.h index 6a7de9be0..b3e101122 100644 --- a/plugins/channeltx/filesource/filesource.h +++ b/plugins/channeltx/filesource/filesource.h @@ -179,6 +179,10 @@ public: SWGSDRangel::SWGChannelSettings& response, QString& errorMessage); + virtual int webapiWorkspaceGet( + SWGSDRangel::SWGWorkspaceInfo& response, + QString& errorMessage); + virtual int webapiSettingsPutPatch( bool force, const QStringList& channelSettingsKeys, diff --git a/plugins/channeltx/localsource/localsource.cpp b/plugins/channeltx/localsource/localsource.cpp index 05c3d795f..84b5bcaec 100644 --- a/plugins/channeltx/localsource/localsource.cpp +++ b/plugins/channeltx/localsource/localsource.cpp @@ -23,6 +23,7 @@ #include #include "SWGChannelSettings.h" +#include "SWGWorkspaceInfo.h" #include "util/simpleserializer.h" #include "dsp/dspcommands.h" @@ -365,6 +366,15 @@ int LocalSource::webapiSettingsGet( return 200; } +int LocalSource::webapiWorkspaceGet( + SWGSDRangel::SWGWorkspaceInfo& response, + QString& errorMessage) +{ + (void) errorMessage; + response.setIndex(m_settings.m_workspaceIndex); + return 200; +} + int LocalSource::webapiSettingsPutPatch( bool force, const QStringList& channelSettingsKeys, diff --git a/plugins/channeltx/localsource/localsource.h b/plugins/channeltx/localsource/localsource.h index 37963a533..9ee878cc6 100644 --- a/plugins/channeltx/localsource/localsource.h +++ b/plugins/channeltx/localsource/localsource.h @@ -95,6 +95,10 @@ public: SWGSDRangel::SWGChannelSettings& response, QString& errorMessage); + virtual int webapiWorkspaceGet( + SWGSDRangel::SWGWorkspaceInfo& response, + QString& errorMessage); + virtual int webapiSettingsPutPatch( bool force, const QStringList& channelSettingsKeys, diff --git a/plugins/channeltx/mod802.15.4/ieee_802_15_4_mod.cpp b/plugins/channeltx/mod802.15.4/ieee_802_15_4_mod.cpp index eb18df7ef..5bd158a84 100644 --- a/plugins/channeltx/mod802.15.4/ieee_802_15_4_mod.cpp +++ b/plugins/channeltx/mod802.15.4/ieee_802_15_4_mod.cpp @@ -27,6 +27,7 @@ #include #include "SWGChannelSettings.h" +#include "SWGWorkspaceInfo.h" #include "SWGChannelReport.h" #include "SWGChannelActions.h" #include "SWGIEEE_802_15_4_ModReport.h" @@ -437,6 +438,15 @@ int IEEE_802_15_4_Mod::webapiSettingsGet( return 200; } +int IEEE_802_15_4_Mod::webapiWorkspaceGet( + SWGSDRangel::SWGWorkspaceInfo& response, + QString& errorMessage) +{ + (void) errorMessage; + response.setIndex(m_settings.m_workspaceIndex); + return 200; +} + int IEEE_802_15_4_Mod::webapiSettingsPutPatch( bool force, const QStringList& channelSettingsKeys, diff --git a/plugins/channeltx/mod802.15.4/ieee_802_15_4_mod.h b/plugins/channeltx/mod802.15.4/ieee_802_15_4_mod.h index 6dd3bd28a..0c168aadd 100644 --- a/plugins/channeltx/mod802.15.4/ieee_802_15_4_mod.h +++ b/plugins/channeltx/mod802.15.4/ieee_802_15_4_mod.h @@ -123,6 +123,10 @@ public: SWGSDRangel::SWGChannelSettings& response, QString& errorMessage); + virtual int webapiWorkspaceGet( + SWGSDRangel::SWGWorkspaceInfo& response, + QString& errorMessage); + virtual int webapiSettingsPutPatch( bool force, const QStringList& channelSettingsKeys, diff --git a/plugins/channeltx/modais/aismod.cpp b/plugins/channeltx/modais/aismod.cpp index 2de16546e..46dafffc2 100644 --- a/plugins/channeltx/modais/aismod.cpp +++ b/plugins/channeltx/modais/aismod.cpp @@ -27,6 +27,7 @@ #include #include "SWGChannelSettings.h" +#include "SWGWorkspaceInfo.h" #include "SWGChannelReport.h" #include "SWGChannelActions.h" @@ -543,6 +544,15 @@ int AISMod::webapiSettingsGet( return 200; } +int AISMod::webapiWorkspaceGet( + SWGSDRangel::SWGWorkspaceInfo& response, + QString& errorMessage) +{ + (void) errorMessage; + response.setIndex(m_settings.m_workspaceIndex); + return 200; +} + int AISMod::webapiSettingsPutPatch( bool force, const QStringList& channelSettingsKeys, diff --git a/plugins/channeltx/modais/aismod.h b/plugins/channeltx/modais/aismod.h index 3492ec4dc..7275fffb8 100644 --- a/plugins/channeltx/modais/aismod.h +++ b/plugins/channeltx/modais/aismod.h @@ -184,6 +184,10 @@ public: SWGSDRangel::SWGChannelSettings& response, QString& errorMessage); + virtual int webapiWorkspaceGet( + SWGSDRangel::SWGWorkspaceInfo& response, + QString& errorMessage); + virtual int webapiSettingsPutPatch( bool force, const QStringList& channelSettingsKeys, diff --git a/plugins/channeltx/modam/ammod.cpp b/plugins/channeltx/modam/ammod.cpp index c1742d396..287176ebb 100644 --- a/plugins/channeltx/modam/ammod.cpp +++ b/plugins/channeltx/modam/ammod.cpp @@ -27,6 +27,7 @@ #include #include "SWGChannelSettings.h" +#include "SWGWorkspaceInfo.h" #include "SWGChannelReport.h" #include "SWGAMModReport.h" @@ -416,6 +417,15 @@ int AMMod::webapiSettingsGet( return 200; } +int AMMod::webapiWorkspaceGet( + SWGSDRangel::SWGWorkspaceInfo& response, + QString& errorMessage) +{ + (void) errorMessage; + response.setIndex(m_settings.m_workspaceIndex); + return 200; +} + int AMMod::webapiSettingsPutPatch( bool force, const QStringList& channelSettingsKeys, diff --git a/plugins/channeltx/modam/ammod.h b/plugins/channeltx/modam/ammod.h index 66658c438..1d2839eae 100644 --- a/plugins/channeltx/modam/ammod.h +++ b/plugins/channeltx/modam/ammod.h @@ -206,6 +206,10 @@ public: SWGSDRangel::SWGChannelSettings& response, QString& errorMessage); + virtual int webapiWorkspaceGet( + SWGSDRangel::SWGWorkspaceInfo& response, + QString& errorMessage); + virtual int webapiSettingsPutPatch( bool force, const QStringList& channelSettingsKeys, diff --git a/plugins/channeltx/modatv/atvmod.cpp b/plugins/channeltx/modatv/atvmod.cpp index 0eb8dad6d..705b55120 100644 --- a/plugins/channeltx/modatv/atvmod.cpp +++ b/plugins/channeltx/modatv/atvmod.cpp @@ -27,6 +27,7 @@ #include #include "SWGChannelSettings.h" +#include "SWGWorkspaceInfo.h" #include "SWGChannelReport.h" #include "SWGATVModReport.h" @@ -406,6 +407,15 @@ int ATVMod::webapiSettingsGet( return 200; } +int ATVMod::webapiWorkspaceGet( + SWGSDRangel::SWGWorkspaceInfo& response, + QString& errorMessage) +{ + (void) errorMessage; + response.setIndex(m_settings.m_workspaceIndex); + return 200; +} + int ATVMod::webapiSettingsPutPatch( bool force, const QStringList& channelSettingsKeys, diff --git a/plugins/channeltx/modatv/atvmod.h b/plugins/channeltx/modatv/atvmod.h index 185e11fd0..4577c0586 100644 --- a/plugins/channeltx/modatv/atvmod.h +++ b/plugins/channeltx/modatv/atvmod.h @@ -278,6 +278,10 @@ public: SWGSDRangel::SWGChannelSettings& response, QString& errorMessage); + virtual int webapiWorkspaceGet( + SWGSDRangel::SWGWorkspaceInfo& response, + QString& errorMessage); + virtual int webapiSettingsPutPatch( bool force, const QStringList& channelSettingsKeys, diff --git a/plugins/channeltx/modchirpchat/chirpchatmod.cpp b/plugins/channeltx/modchirpchat/chirpchatmod.cpp index c2d3412fa..37f7fd8ec 100644 --- a/plugins/channeltx/modchirpchat/chirpchatmod.cpp +++ b/plugins/channeltx/modchirpchat/chirpchatmod.cpp @@ -26,6 +26,7 @@ #include #include "SWGChannelSettings.h" +#include "SWGWorkspaceInfo.h" #include "SWGChannelReport.h" #include "SWGChirpChatModReport.h" @@ -458,6 +459,15 @@ int ChirpChatMod::webapiSettingsGet( return 200; } +int ChirpChatMod::webapiWorkspaceGet( + SWGSDRangel::SWGWorkspaceInfo& response, + QString& errorMessage) +{ + (void) errorMessage; + response.setIndex(m_settings.m_workspaceIndex); + return 200; +} + int ChirpChatMod::webapiSettingsPutPatch( bool force, const QStringList& channelSettingsKeys, diff --git a/plugins/channeltx/modchirpchat/chirpchatmod.h b/plugins/channeltx/modchirpchat/chirpchatmod.h index cfbc82d89..18214d66e 100644 --- a/plugins/channeltx/modchirpchat/chirpchatmod.h +++ b/plugins/channeltx/modchirpchat/chirpchatmod.h @@ -121,6 +121,10 @@ public: SWGSDRangel::SWGChannelSettings& response, QString& errorMessage); + virtual int webapiWorkspaceGet( + SWGSDRangel::SWGWorkspaceInfo& response, + QString& errorMessage); + virtual int webapiSettingsPutPatch( bool force, const QStringList& channelSettingsKeys, diff --git a/plugins/channeltx/moddatv/datvmod.cpp b/plugins/channeltx/moddatv/datvmod.cpp index 83b429850..196ecd85c 100644 --- a/plugins/channeltx/moddatv/datvmod.cpp +++ b/plugins/channeltx/moddatv/datvmod.cpp @@ -31,6 +31,7 @@ #include #include "SWGChannelSettings.h" +#include "SWGWorkspaceInfo.h" #include "SWGChannelReport.h" //#include "SWGDATVModReport.h" @@ -372,6 +373,15 @@ int DATVMod::webapiSettingsGet( return 200; } +int DATVMod::webapiWorkspaceGet( + SWGSDRangel::SWGWorkspaceInfo& response, + QString& errorMessage) +{ + (void) errorMessage; + response.setIndex(m_settings.m_workspaceIndex); + return 200; +} + int DATVMod::webapiSettingsPutPatch( bool force, const QStringList& channelSettingsKeys, diff --git a/plugins/channeltx/moddatv/datvmod.h b/plugins/channeltx/moddatv/datvmod.h index c4d1a3764..98781f861 100644 --- a/plugins/channeltx/moddatv/datvmod.h +++ b/plugins/channeltx/moddatv/datvmod.h @@ -234,6 +234,10 @@ public: SWGSDRangel::SWGChannelSettings& response, QString& errorMessage); + virtual int webapiWorkspaceGet( + SWGSDRangel::SWGWorkspaceInfo& response, + QString& errorMessage); + virtual int webapiSettingsPutPatch( bool force, const QStringList& channelSettingsKeys, diff --git a/plugins/channeltx/modfreedv/freedvmod.cpp b/plugins/channeltx/modfreedv/freedvmod.cpp index 9667d4e8b..8ffa17f3b 100644 --- a/plugins/channeltx/modfreedv/freedvmod.cpp +++ b/plugins/channeltx/modfreedv/freedvmod.cpp @@ -28,6 +28,7 @@ #include #include "SWGChannelSettings.h" +#include "SWGWorkspaceInfo.h" #include "SWGChannelReport.h" #include "SWGFreeDVModReport.h" @@ -391,6 +392,15 @@ int FreeDVMod::webapiSettingsGet( return 200; } +int FreeDVMod::webapiWorkspaceGet( + SWGSDRangel::SWGWorkspaceInfo& response, + QString& errorMessage) +{ + (void) errorMessage; + response.setIndex(m_settings.m_workspaceIndex); + return 200; +} + int FreeDVMod::webapiSettingsPutPatch( bool force, const QStringList& channelSettingsKeys, diff --git a/plugins/channeltx/modfreedv/freedvmod.h b/plugins/channeltx/modfreedv/freedvmod.h index 41660950f..0ed5c8feb 100644 --- a/plugins/channeltx/modfreedv/freedvmod.h +++ b/plugins/channeltx/modfreedv/freedvmod.h @@ -210,6 +210,10 @@ public: SWGSDRangel::SWGChannelSettings& response, QString& errorMessage); + virtual int webapiWorkspaceGet( + SWGSDRangel::SWGWorkspaceInfo& response, + QString& errorMessage); + virtual int webapiSettingsPutPatch( bool force, const QStringList& channelSettingsKeys, diff --git a/plugins/channeltx/modnfm/nfmmod.cpp b/plugins/channeltx/modnfm/nfmmod.cpp index faaf386da..62c7b5d18 100644 --- a/plugins/channeltx/modnfm/nfmmod.cpp +++ b/plugins/channeltx/modnfm/nfmmod.cpp @@ -24,6 +24,7 @@ #include #include "SWGChannelSettings.h" +#include "SWGWorkspaceInfo.h" #include "SWGCWKeyerSettings.h" #include "SWGChannelReport.h" #include "SWGNFMModReport.h" @@ -463,6 +464,15 @@ int NFMMod::webapiSettingsGet( return 200; } +int NFMMod::webapiWorkspaceGet( + SWGSDRangel::SWGWorkspaceInfo& response, + QString& errorMessage) +{ + (void) errorMessage; + response.setIndex(m_settings.m_workspaceIndex); + return 200; +} + int NFMMod::webapiSettingsPutPatch( bool force, const QStringList& channelSettingsKeys, diff --git a/plugins/channeltx/modnfm/nfmmod.h b/plugins/channeltx/modnfm/nfmmod.h index 90a4ce786..43f610168 100644 --- a/plugins/channeltx/modnfm/nfmmod.h +++ b/plugins/channeltx/modnfm/nfmmod.h @@ -206,6 +206,10 @@ public: SWGSDRangel::SWGChannelSettings& response, QString& errorMessage); + virtual int webapiWorkspaceGet( + SWGSDRangel::SWGWorkspaceInfo& response, + QString& errorMessage); + virtual int webapiSettingsPutPatch( bool force, const QStringList& channelSettingsKeys, diff --git a/plugins/channeltx/modpacket/packetmod.cpp b/plugins/channeltx/modpacket/packetmod.cpp index c11add032..b9f3ca8b5 100644 --- a/plugins/channeltx/modpacket/packetmod.cpp +++ b/plugins/channeltx/modpacket/packetmod.cpp @@ -27,6 +27,7 @@ #include #include "SWGChannelSettings.h" +#include "SWGWorkspaceInfo.h" #include "SWGChannelReport.h" #include "SWGChannelActions.h" #include "SWGPacketModReport.h" @@ -492,6 +493,15 @@ int PacketMod::webapiSettingsGet( return 200; } +int PacketMod::webapiWorkspaceGet( + SWGSDRangel::SWGWorkspaceInfo& response, + QString& errorMessage) +{ + (void) errorMessage; + response.setIndex(m_settings.m_workspaceIndex); + return 200; +} + int PacketMod::webapiSettingsPutPatch( bool force, const QStringList& channelSettingsKeys, diff --git a/plugins/channeltx/modpacket/packetmod.h b/plugins/channeltx/modpacket/packetmod.h index ebb7ebf78..afd992f2b 100644 --- a/plugins/channeltx/modpacket/packetmod.h +++ b/plugins/channeltx/modpacket/packetmod.h @@ -174,6 +174,10 @@ public: SWGSDRangel::SWGChannelSettings& response, QString& errorMessage); + virtual int webapiWorkspaceGet( + SWGSDRangel::SWGWorkspaceInfo& response, + QString& errorMessage); + virtual int webapiSettingsPutPatch( bool force, const QStringList& channelSettingsKeys, diff --git a/plugins/channeltx/modssb/ssbmod.cpp b/plugins/channeltx/modssb/ssbmod.cpp index 51860e1fe..f9e997c92 100644 --- a/plugins/channeltx/modssb/ssbmod.cpp +++ b/plugins/channeltx/modssb/ssbmod.cpp @@ -28,6 +28,7 @@ #include #include "SWGChannelSettings.h" +#include "SWGWorkspaceInfo.h" #include "SWGChannelReport.h" #include "SWGSSBModReport.h" @@ -440,6 +441,15 @@ int SSBMod::webapiSettingsGet( return 200; } +int SSBMod::webapiWorkspaceGet( + SWGSDRangel::SWGWorkspaceInfo& response, + QString& errorMessage) +{ + (void) errorMessage; + response.setIndex(m_settings.m_workspaceIndex); + return 200; +} + int SSBMod::webapiSettingsPutPatch( bool force, const QStringList& channelSettingsKeys, diff --git a/plugins/channeltx/modssb/ssbmod.h b/plugins/channeltx/modssb/ssbmod.h index 6603f9ac0..d09f97ed2 100644 --- a/plugins/channeltx/modssb/ssbmod.h +++ b/plugins/channeltx/modssb/ssbmod.h @@ -208,6 +208,10 @@ public: SWGSDRangel::SWGChannelSettings& response, QString& errorMessage); + virtual int webapiWorkspaceGet( + SWGSDRangel::SWGWorkspaceInfo& response, + QString& errorMessage); + virtual int webapiSettingsPutPatch( bool force, const QStringList& channelSettingsKeys, diff --git a/plugins/channeltx/modwfm/wfmmod.cpp b/plugins/channeltx/modwfm/wfmmod.cpp index c0859f4f5..e95a01a3e 100644 --- a/plugins/channeltx/modwfm/wfmmod.cpp +++ b/plugins/channeltx/modwfm/wfmmod.cpp @@ -28,6 +28,7 @@ #include #include "SWGChannelSettings.h" +#include "SWGWorkspaceInfo.h" #include "SWGChannelReport.h" #include "SWGAMModReport.h" @@ -402,6 +403,15 @@ int WFMMod::webapiSettingsGet( return 200; } +int WFMMod::webapiWorkspaceGet( + SWGSDRangel::SWGWorkspaceInfo& response, + QString& errorMessage) +{ + (void) errorMessage; + response.setIndex(m_settings.m_workspaceIndex); + return 200; +} + int WFMMod::webapiSettingsPutPatch( bool force, const QStringList& channelSettingsKeys, diff --git a/plugins/channeltx/modwfm/wfmmod.h b/plugins/channeltx/modwfm/wfmmod.h index 625164ef9..d209ef708 100644 --- a/plugins/channeltx/modwfm/wfmmod.h +++ b/plugins/channeltx/modwfm/wfmmod.h @@ -206,6 +206,10 @@ public: SWGSDRangel::SWGChannelSettings& response, QString& errorMessage); + virtual int webapiWorkspaceGet( + SWGSDRangel::SWGWorkspaceInfo& response, + QString& errorMessage); + virtual int webapiSettingsPutPatch( bool force, const QStringList& channelSettingsKeys, diff --git a/plugins/channeltx/remotesource/remotesource.cpp b/plugins/channeltx/remotesource/remotesource.cpp index 5458f7294..35ef6f583 100644 --- a/plugins/channeltx/remotesource/remotesource.cpp +++ b/plugins/channeltx/remotesource/remotesource.cpp @@ -24,6 +24,7 @@ #include #include "SWGChannelSettings.h" +#include "SWGWorkspaceInfo.h" #include "SWGChannelReport.h" #include "SWGRemoteSourceReport.h" @@ -300,6 +301,15 @@ int RemoteSource::webapiSettingsGet( return 200; } +int RemoteSource::webapiWorkspaceGet( + SWGSDRangel::SWGWorkspaceInfo& response, + QString& errorMessage) +{ + (void) errorMessage; + response.setIndex(m_settings.m_workspaceIndex); + return 200; +} + int RemoteSource::webapiSettingsPutPatch( bool force, const QStringList& channelSettingsKeys, diff --git a/plugins/channeltx/remotesource/remotesource.h b/plugins/channeltx/remotesource/remotesource.h index 6b3f831f5..0ada09b3c 100644 --- a/plugins/channeltx/remotesource/remotesource.h +++ b/plugins/channeltx/remotesource/remotesource.h @@ -188,6 +188,10 @@ public: SWGSDRangel::SWGChannelSettings& response, QString& errorMessage); + virtual int webapiWorkspaceGet( + SWGSDRangel::SWGWorkspaceInfo& response, + QString& errorMessage); + virtual int webapiSettingsPutPatch( bool force, const QStringList& channelSettingsKeys, diff --git a/plugins/channeltx/udpsource/udpsource.cpp b/plugins/channeltx/udpsource/udpsource.cpp index f9a96d86f..9068c6526 100644 --- a/plugins/channeltx/udpsource/udpsource.cpp +++ b/plugins/channeltx/udpsource/udpsource.cpp @@ -22,6 +22,7 @@ #include #include "SWGChannelSettings.h" +#include "SWGWorkspaceInfo.h" #include "SWGChannelReport.h" #include "SWGUDPSourceReport.h" @@ -340,6 +341,15 @@ int UDPSource::webapiSettingsGet( return 200; } +int UDPSource::webapiWorkspaceGet( + SWGSDRangel::SWGWorkspaceInfo& response, + QString& errorMessage) +{ + (void) errorMessage; + response.setIndex(m_settings.m_workspaceIndex); + return 200; +} + int UDPSource::webapiSettingsPutPatch( bool force, const QStringList& channelSettingsKeys, diff --git a/plugins/channeltx/udpsource/udpsource.h b/plugins/channeltx/udpsource/udpsource.h index 4a4fe6caf..6bab5cd1e 100644 --- a/plugins/channeltx/udpsource/udpsource.h +++ b/plugins/channeltx/udpsource/udpsource.h @@ -127,6 +127,10 @@ public: SWGSDRangel::SWGChannelSettings& response, QString& errorMessage); + virtual int webapiWorkspaceGet( + SWGSDRangel::SWGWorkspaceInfo& response, + QString& errorMessage); + virtual int webapiSettingsPutPatch( bool force, const QStringList& channelSettingsKeys, diff --git a/plugins/feature/afc/afcgui.cpp b/plugins/feature/afc/afcgui.cpp index 413ebc6c4..6d8a1b0de 100644 --- a/plugins/feature/afc/afcgui.cpp +++ b/plugins/feature/afc/afcgui.cpp @@ -55,6 +55,7 @@ bool AFCGUI::deserialize(const QByteArray& data) { if (m_settings.deserialize(data)) { + m_feature->setWorkspaceIndex(m_settings.m_workspaceIndex); displaySettings(); applySettings(true); return true; @@ -139,6 +140,7 @@ AFCGUI::AFCGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISet, Feature *featur m_doApplySettings(true), m_lastFeatureState(0) { + m_feature = feature; setAttribute(Qt::WA_DeleteOnClose, true); m_helpURL = "plugins/feature/afc/readme.md"; RollupContents *rollupContents = getRollupContents(); @@ -179,6 +181,12 @@ AFCGUI::~AFCGUI() delete ui; } +void AFCGUI::setWorkspaceIndex(int index) +{ + m_settings.m_workspaceIndex = index; + m_feature->setWorkspaceIndex(index); +} + void AFCGUI::blockApplySettings(bool block) { m_doApplySettings = !block; diff --git a/plugins/feature/afc/afcgui.h b/plugins/feature/afc/afcgui.h index 74e1f8bff..fa210aa41 100644 --- a/plugins/feature/afc/afcgui.h +++ b/plugins/feature/afc/afcgui.h @@ -43,7 +43,7 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } - virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; } + virtual void setWorkspaceIndex(int index); virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; } virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; } virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; } diff --git a/plugins/feature/ais/aisgui.cpp b/plugins/feature/ais/aisgui.cpp index b021ccacb..eaa6749c6 100644 --- a/plugins/feature/ais/aisgui.cpp +++ b/plugins/feature/ais/aisgui.cpp @@ -112,6 +112,7 @@ bool AISGUI::deserialize(const QByteArray& data) { if (m_settings.deserialize(data)) { + m_feature->setWorkspaceIndex(m_settings.m_workspaceIndex); displaySettings(); applySettings(true); return true; @@ -189,6 +190,7 @@ AISGUI::AISGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISet, Feature *featur m_doApplySettings(true), m_lastFeatureState(0) { + m_feature = feature; setAttribute(Qt::WA_DeleteOnClose, true); m_helpURL = "plugins/feature/ais/readme.md"; RollupContents *rollupContents = getRollupContents(); @@ -241,6 +243,12 @@ AISGUI::~AISGUI() delete ui; } +void AISGUI::setWorkspaceIndex(int index) +{ + m_settings.m_workspaceIndex = index; + m_feature->setWorkspaceIndex(index); +} + void AISGUI::blockApplySettings(bool block) { m_doApplySettings = !block; diff --git a/plugins/feature/ais/aisgui.h b/plugins/feature/ais/aisgui.h index 107276ffb..4e30f7d15 100644 --- a/plugins/feature/ais/aisgui.h +++ b/plugins/feature/ais/aisgui.h @@ -57,7 +57,7 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } - virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; } + virtual void setWorkspaceIndex(int index); virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; } virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; } virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; } diff --git a/plugins/feature/antennatools/antennatoolsgui.cpp b/plugins/feature/antennatools/antennatoolsgui.cpp index ff316198c..1b2141062 100644 --- a/plugins/feature/antennatools/antennatoolsgui.cpp +++ b/plugins/feature/antennatools/antennatoolsgui.cpp @@ -59,6 +59,7 @@ bool AntennaToolsGUI::deserialize(const QByteArray& data) { if (m_settings.deserialize(data)) { + m_feature->setWorkspaceIndex(m_settings.m_workspaceIndex); displaySettings(); applySettings(true); return true; @@ -133,6 +134,7 @@ AntennaToolsGUI::AntennaToolsGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISe m_doApplySettings(true), m_deviceSets(0) { + m_feature = feature; setAttribute(Qt::WA_DeleteOnClose, true); m_helpURL = "plugins/feature/antennatools/readme.md"; RollupContents *rollupContents = getRollupContents(); @@ -163,6 +165,12 @@ AntennaToolsGUI::~AntennaToolsGUI() delete ui; } +void AntennaToolsGUI::setWorkspaceIndex(int index) +{ + m_settings.m_workspaceIndex = index; + m_feature->setWorkspaceIndex(index); +} + void AntennaToolsGUI::blockApplySettings(bool block) { m_doApplySettings = !block; diff --git a/plugins/feature/antennatools/antennatoolsgui.h b/plugins/feature/antennatools/antennatoolsgui.h index 72c22905c..431d95785 100644 --- a/plugins/feature/antennatools/antennatoolsgui.h +++ b/plugins/feature/antennatools/antennatoolsgui.h @@ -46,7 +46,7 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } - virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; } + virtual void setWorkspaceIndex(int index); virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; } virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; } virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; } diff --git a/plugins/feature/aprs/aprsgui.cpp b/plugins/feature/aprs/aprsgui.cpp index 906cf23b4..bd2dd5013 100644 --- a/plugins/feature/aprs/aprsgui.cpp +++ b/plugins/feature/aprs/aprsgui.cpp @@ -132,6 +132,7 @@ bool APRSGUI::deserialize(const QByteArray& data) { if (m_settings.deserialize(data)) { + m_feature->setWorkspaceIndex(m_settings.m_workspaceIndex); displaySettings(); applySettings(true); return true; @@ -430,6 +431,7 @@ APRSGUI::APRSGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISet, Feature *feat m_doApplySettings(true), m_lastFeatureState(0) { + m_feature = feature; setAttribute(Qt::WA_DeleteOnClose, true); m_helpURL = "plugins/feature/aprs/readme.md"; RollupContents *rollupContents = getRollupContents(); @@ -572,6 +574,12 @@ APRSGUI::~APRSGUI() delete ui; } +void APRSGUI::setWorkspaceIndex(int index) +{ + m_settings.m_workspaceIndex = index; + m_feature->setWorkspaceIndex(index); +} + void APRSGUI::blockApplySettings(bool block) { m_doApplySettings = !block; diff --git a/plugins/feature/aprs/aprsgui.h b/plugins/feature/aprs/aprsgui.h index 4b9c67442..fdb730a91 100644 --- a/plugins/feature/aprs/aprsgui.h +++ b/plugins/feature/aprs/aprsgui.h @@ -105,7 +105,7 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } - virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; } + virtual void setWorkspaceIndex(int index); virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; } virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; } virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; } diff --git a/plugins/feature/demodanalyzer/demodanalyzergui.cpp b/plugins/feature/demodanalyzer/demodanalyzergui.cpp index 006176c59..fd6d39bcf 100644 --- a/plugins/feature/demodanalyzer/demodanalyzergui.cpp +++ b/plugins/feature/demodanalyzer/demodanalyzergui.cpp @@ -57,6 +57,7 @@ bool DemodAnalyzerGUI::deserialize(const QByteArray& data) { if (m_settings.deserialize(data)) { + m_feature->setWorkspaceIndex(m_settings.m_workspaceIndex); displaySettings(); applySettings(true); return true; @@ -150,6 +151,7 @@ DemodAnalyzerGUI::DemodAnalyzerGUI(PluginAPI* pluginAPI, FeatureUISet *featureUI m_lastFeatureState(0), m_selectedChannel(nullptr) { + m_feature = feature; setAttribute(Qt::WA_DeleteOnClose, true); m_helpURL = "plugins/feature/demodanalyzer/readme.md"; RollupContents *rollupContents = getRollupContents(); @@ -201,6 +203,12 @@ void DemodAnalyzerGUI::blockApplySettings(bool block) m_doApplySettings = !block; } +void DemodAnalyzerGUI::setWorkspaceIndex(int index) +{ + m_settings.m_workspaceIndex = index; + m_feature->setWorkspaceIndex(index); +} + void DemodAnalyzerGUI::displaySettings() { setTitleColor(m_settings.m_rgbColor); diff --git a/plugins/feature/demodanalyzer/demodanalyzergui.h b/plugins/feature/demodanalyzer/demodanalyzergui.h index d21c0b1c3..84483f668 100644 --- a/plugins/feature/demodanalyzer/demodanalyzergui.h +++ b/plugins/feature/demodanalyzer/demodanalyzergui.h @@ -49,7 +49,7 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } - virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; } + virtual void setWorkspaceIndex(int index); virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; } virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; } virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; } diff --git a/plugins/feature/gs232controller/gs232controllergui.cpp b/plugins/feature/gs232controller/gs232controllergui.cpp index 695083ee2..d9adff147 100644 --- a/plugins/feature/gs232controller/gs232controllergui.cpp +++ b/plugins/feature/gs232controller/gs232controllergui.cpp @@ -60,6 +60,7 @@ bool GS232ControllerGUI::deserialize(const QByteArray& data) { if (m_settings.deserialize(data)) { + m_feature->setWorkspaceIndex(m_settings.m_workspaceIndex); displaySettings(); applySettings(true); return true; @@ -149,6 +150,7 @@ GS232ControllerGUI::GS232ControllerGUI(PluginAPI* pluginAPI, FeatureUISet *featu m_lastFeatureState(0), m_lastOnTarget(false) { + m_feature = feature; setAttribute(Qt::WA_DeleteOnClose, true); m_helpURL = "plugins/feature/gs232controller/readme.md"; RollupContents *rollupContents = getRollupContents(); @@ -183,6 +185,12 @@ GS232ControllerGUI::~GS232ControllerGUI() delete ui; } +void GS232ControllerGUI::setWorkspaceIndex(int index) +{ + m_settings.m_workspaceIndex = index; + m_feature->setWorkspaceIndex(index); +} + void GS232ControllerGUI::blockApplySettings(bool block) { m_doApplySettings = !block; diff --git a/plugins/feature/gs232controller/gs232controllergui.h b/plugins/feature/gs232controller/gs232controllergui.h index f3e2f5b1f..ce3dd8466 100644 --- a/plugins/feature/gs232controller/gs232controllergui.h +++ b/plugins/feature/gs232controller/gs232controllergui.h @@ -45,7 +45,7 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } - virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; } + virtual void setWorkspaceIndex(int index); virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; } virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; } virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; } diff --git a/plugins/feature/jogdialcontroller/jogdialcontrollergui.cpp b/plugins/feature/jogdialcontroller/jogdialcontrollergui.cpp index 1ff5381c6..ece4480c9 100644 --- a/plugins/feature/jogdialcontroller/jogdialcontrollergui.cpp +++ b/plugins/feature/jogdialcontroller/jogdialcontrollergui.cpp @@ -56,6 +56,7 @@ bool JogdialControllerGUI::deserialize(const QByteArray& data) { if (m_settings.deserialize(data)) { + m_feature->setWorkspaceIndex(m_settings.m_workspaceIndex); displaySettings(); applySettings(true); return true; @@ -154,6 +155,7 @@ JogdialControllerGUI::JogdialControllerGUI(PluginAPI* pluginAPI, FeatureUISet *f m_lastFeatureState(0), m_selectedChannel(nullptr) { + m_feature = feature; setAttribute(Qt::WA_DeleteOnClose, true); m_helpURL = "plugins/feature/jogdialcontroller/readme.md"; RollupContents *rollupContents = getRollupContents(); @@ -186,6 +188,12 @@ JogdialControllerGUI::~JogdialControllerGUI() delete ui; } +void JogdialControllerGUI::setWorkspaceIndex(int index) +{ + m_settings.m_workspaceIndex = index; + m_feature->setWorkspaceIndex(index); +} + void JogdialControllerGUI::blockApplySettings(bool block) { m_doApplySettings = !block; diff --git a/plugins/feature/jogdialcontroller/jogdialcontrollergui.h b/plugins/feature/jogdialcontroller/jogdialcontrollergui.h index a93fcf72e..8569190f0 100644 --- a/plugins/feature/jogdialcontroller/jogdialcontrollergui.h +++ b/plugins/feature/jogdialcontroller/jogdialcontrollergui.h @@ -47,7 +47,7 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } - virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; } + virtual void setWorkspaceIndex(int index); virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; } virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; } virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; } diff --git a/plugins/feature/map/mapgui.cpp b/plugins/feature/map/mapgui.cpp index 33aa3073b..56ad80f9e 100644 --- a/plugins/feature/map/mapgui.cpp +++ b/plugins/feature/map/mapgui.cpp @@ -72,6 +72,7 @@ bool MapGUI::deserialize(const QByteArray& data) { if (m_settings.deserialize(data)) { + m_feature->setWorkspaceIndex(m_settings.m_workspaceIndex); displaySettings(); applySettings(true); return true; @@ -190,6 +191,7 @@ MapGUI::MapGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISet, Feature *featur m_radioTimeDialog(this), m_cesium(nullptr) { + m_feature = feature; setAttribute(Qt::WA_DeleteOnClose, true); m_helpURL = "plugins/feature/map/readme.md"; RollupContents *rollupContents = getRollupContents(); @@ -303,6 +305,12 @@ MapGUI::~MapGUI() delete ui; } +void MapGUI::setWorkspaceIndex(int index) +{ + m_settings.m_workspaceIndex = index; + m_feature->setWorkspaceIndex(index); +} + // Update a map item or image void MapGUI::update(const QObject *source, SWGSDRangel::SWGMapItem *swgMapItem, const QString &group) { diff --git a/plugins/feature/map/mapgui.h b/plugins/feature/map/mapgui.h index 2c5d7f625..4a2d29a2c 100644 --- a/plugins/feature/map/mapgui.h +++ b/plugins/feature/map/mapgui.h @@ -72,7 +72,7 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } - virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; } + virtual void setWorkspaceIndex(int index); virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; } virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; } virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; } diff --git a/plugins/feature/pertester/pertestergui.cpp b/plugins/feature/pertester/pertestergui.cpp index 5458e46bd..3f24c5ca0 100644 --- a/plugins/feature/pertester/pertestergui.cpp +++ b/plugins/feature/pertester/pertestergui.cpp @@ -58,6 +58,7 @@ bool PERTesterGUI::deserialize(const QByteArray& data) { if (m_settings.deserialize(data)) { + m_feature->setWorkspaceIndex(m_settings.m_workspaceIndex); displaySettings(); applySettings(true); return true; @@ -135,6 +136,7 @@ PERTesterGUI::PERTesterGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISet, Fea m_doApplySettings(true), m_lastFeatureState(0) { + m_feature = feature; setAttribute(Qt::WA_DeleteOnClose, true); m_helpURL = "plugins/feature/pertester/readme.md"; RollupContents *rollupContents = getRollupContents(); @@ -164,6 +166,12 @@ PERTesterGUI::~PERTesterGUI() delete ui; } +void PERTesterGUI::setWorkspaceIndex(int index) +{ + m_settings.m_workspaceIndex = index; + m_feature->setWorkspaceIndex(index); +} + void PERTesterGUI::blockApplySettings(bool block) { m_doApplySettings = !block; diff --git a/plugins/feature/pertester/pertestergui.h b/plugins/feature/pertester/pertestergui.h index 8fc751d79..af3a67bb0 100644 --- a/plugins/feature/pertester/pertestergui.h +++ b/plugins/feature/pertester/pertestergui.h @@ -45,7 +45,7 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } - virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; } + virtual void setWorkspaceIndex(int index); virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; } virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; } virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; } diff --git a/plugins/feature/radiosonde/radiosondegui.cpp b/plugins/feature/radiosonde/radiosondegui.cpp index 8a60dedd2..c4b1af7a6 100644 --- a/plugins/feature/radiosonde/radiosondegui.cpp +++ b/plugins/feature/radiosonde/radiosondegui.cpp @@ -63,6 +63,7 @@ bool RadiosondeGUI::deserialize(const QByteArray& data) { if (m_settings.deserialize(data)) { + m_feature->setWorkspaceIndex(m_settings.m_workspaceIndex); displaySettings(); applySettings(true); return true; @@ -140,6 +141,7 @@ RadiosondeGUI::RadiosondeGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISet, F m_doApplySettings(true), m_lastFeatureState(0) { + m_feature = feature; setAttribute(Qt::WA_DeleteOnClose, true); m_helpURL = "plugins/feature/radiosonde/readme.md"; RollupContents *rollupContents = getRollupContents(); @@ -203,6 +205,12 @@ RadiosondeGUI::~RadiosondeGUI() delete ui; } +void RadiosondeGUI::setWorkspaceIndex(int index) +{ + m_settings.m_workspaceIndex = index; + m_feature->setWorkspaceIndex(index); +} + void RadiosondeGUI::blockApplySettings(bool block) { m_doApplySettings = !block; diff --git a/plugins/feature/radiosonde/radiosondegui.h b/plugins/feature/radiosonde/radiosondegui.h index c42bd655c..355a9b76d 100644 --- a/plugins/feature/radiosonde/radiosondegui.h +++ b/plugins/feature/radiosonde/radiosondegui.h @@ -74,7 +74,7 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } - virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; } + virtual void setWorkspaceIndex(int index); virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; } virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; } virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; } diff --git a/plugins/feature/rigctlserver/rigctlservergui.cpp b/plugins/feature/rigctlserver/rigctlservergui.cpp index a9f5eef5e..017093c89 100644 --- a/plugins/feature/rigctlserver/rigctlservergui.cpp +++ b/plugins/feature/rigctlserver/rigctlservergui.cpp @@ -48,7 +48,6 @@ void RigCtlServerGUI::resetToDefaults() QByteArray RigCtlServerGUI::serialize() const { - qDebug("RigCtlServerGUI::serialize: %d", m_settings.m_channelIndex); return m_settings.serialize(); } @@ -56,7 +55,7 @@ bool RigCtlServerGUI::deserialize(const QByteArray& data) { if (m_settings.deserialize(data)) { - qDebug("RigCtlServerGUI::deserialize: %d", m_settings.m_channelIndex); + m_feature->setWorkspaceIndex(m_settings.m_workspaceIndex); updateDeviceSetList(); displaySettings(); applySettings(true); @@ -135,6 +134,7 @@ RigCtlServerGUI::RigCtlServerGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISe m_doApplySettings(true), m_lastFeatureState(0) { + m_feature = feature; setAttribute(Qt::WA_DeleteOnClose, true); m_helpURL = "plugins/feature/rigctlserver/readme.md"; RollupContents *rollupContents = getRollupContents(); @@ -165,6 +165,12 @@ RigCtlServerGUI::~RigCtlServerGUI() delete ui; } +void RigCtlServerGUI::setWorkspaceIndex(int index) +{ + m_settings.m_workspaceIndex = index; + m_feature->setWorkspaceIndex(index); +} + void RigCtlServerGUI::blockApplySettings(bool block) { m_doApplySettings = !block; diff --git a/plugins/feature/rigctlserver/rigctlservergui.h b/plugins/feature/rigctlserver/rigctlservergui.h index 922425093..e56c0e941 100644 --- a/plugins/feature/rigctlserver/rigctlservergui.h +++ b/plugins/feature/rigctlserver/rigctlservergui.h @@ -45,7 +45,7 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } - virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; } + virtual void setWorkspaceIndex(int index); virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; } virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; } virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; } diff --git a/plugins/feature/satellitetracker/satellitetrackergui.cpp b/plugins/feature/satellitetracker/satellitetrackergui.cpp index 7be65d894..701490b4f 100644 --- a/plugins/feature/satellitetracker/satellitetrackergui.cpp +++ b/plugins/feature/satellitetracker/satellitetrackergui.cpp @@ -73,6 +73,7 @@ bool SatelliteTrackerGUI::deserialize(const QByteArray& data) { if (m_settings.deserialize(data)) { + m_feature->setWorkspaceIndex(m_settings.m_workspaceIndex); updateSelectedSats(); displaySettings(); qDebug() << " deserialize " << m_settings.m_satellites; @@ -259,6 +260,7 @@ SatelliteTrackerGUI::SatelliteTrackerGUI(PluginAPI* pluginAPI, FeatureUISet *fea m_polarChart(nullptr), m_geostationarySatVisible(false) { + m_feature = feature; setAttribute(Qt::WA_DeleteOnClose, true); m_helpURL = "plugins/feature/satellitetracker/readme.md"; RollupContents *rollupContents = getRollupContents(); @@ -327,6 +329,12 @@ SatelliteTrackerGUI::~SatelliteTrackerGUI() delete ui; } +void SatelliteTrackerGUI::setWorkspaceIndex(int index) +{ + m_settings.m_workspaceIndex = index; + m_feature->setWorkspaceIndex(index); +} + void SatelliteTrackerGUI::blockApplySettings(bool block) { m_doApplySettings = !block; diff --git a/plugins/feature/satellitetracker/satellitetrackergui.h b/plugins/feature/satellitetracker/satellitetrackergui.h index 301333216..f5a058789 100644 --- a/plugins/feature/satellitetracker/satellitetrackergui.h +++ b/plugins/feature/satellitetracker/satellitetrackergui.h @@ -52,7 +52,7 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } - virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; } + virtual void setWorkspaceIndex(int index); virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; } virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; } virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; } diff --git a/plugins/feature/simpleptt/simplepttgui.cpp b/plugins/feature/simpleptt/simplepttgui.cpp index 981662ece..c7bc256b0 100644 --- a/plugins/feature/simpleptt/simplepttgui.cpp +++ b/plugins/feature/simpleptt/simplepttgui.cpp @@ -59,6 +59,7 @@ bool SimplePTTGUI::deserialize(const QByteArray& data) { if (m_settings.deserialize(data)) { + m_feature->setWorkspaceIndex(m_settings.m_workspaceIndex); displaySettings(); applySettings(true); return true; @@ -159,6 +160,7 @@ SimplePTTGUI::SimplePTTGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISet, Fea m_doApplySettings(true), m_lastFeatureState(0) { + m_feature = feature; setAttribute(Qt::WA_DeleteOnClose, true); m_helpURL = "plugins/feature/simpleptt/readme.md"; RollupContents *rollupContents = getRollupContents(); @@ -199,6 +201,12 @@ SimplePTTGUI::~SimplePTTGUI() delete ui; } +void SimplePTTGUI::setWorkspaceIndex(int index) +{ + m_settings.m_workspaceIndex = index; + m_feature->setWorkspaceIndex(index); +} + void SimplePTTGUI::blockApplySettings(bool block) { m_doApplySettings = !block; diff --git a/plugins/feature/simpleptt/simplepttgui.h b/plugins/feature/simpleptt/simplepttgui.h index bfd10af3f..55a744bf1 100644 --- a/plugins/feature/simpleptt/simplepttgui.h +++ b/plugins/feature/simpleptt/simplepttgui.h @@ -44,7 +44,7 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } - virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; } + virtual void setWorkspaceIndex(int index); virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; } virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; } virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; } diff --git a/plugins/feature/startracker/startrackergui.cpp b/plugins/feature/startracker/startrackergui.cpp index 2807b1c83..b7ee738b4 100644 --- a/plugins/feature/startracker/startrackergui.cpp +++ b/plugins/feature/startracker/startrackergui.cpp @@ -79,6 +79,7 @@ bool StarTrackerGUI::deserialize(const QByteArray& data) { if (m_settings.deserialize(data)) { + m_feature->setWorkspaceIndex(m_settings.m_workspaceIndex); displaySettings(); applySettings(true); return true; @@ -265,6 +266,7 @@ StarTrackerGUI::StarTrackerGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISet, m_moonRA(0.0), m_moonDec(0.0) { + m_feature = feature; setAttribute(Qt::WA_DeleteOnClose, true); m_helpURL = "plugins/feature/startracker/readme.md"; RollupContents *rollupContents = getRollupContents(); @@ -397,6 +399,12 @@ StarTrackerGUI::~StarTrackerGUI() delete ui; } +void StarTrackerGUI::setWorkspaceIndex(int index) +{ + m_settings.m_workspaceIndex = index; + m_feature->setWorkspaceIndex(index); +} + void StarTrackerGUI::blockApplySettings(bool block) { m_doApplySettings = !block; diff --git a/plugins/feature/startracker/startrackergui.h b/plugins/feature/startracker/startrackergui.h index 689b00b39..c0acf6015 100644 --- a/plugins/feature/startracker/startrackergui.h +++ b/plugins/feature/startracker/startrackergui.h @@ -66,7 +66,7 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } - virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; } + virtual void setWorkspaceIndex(int index); virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; } virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; } virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; } diff --git a/plugins/feature/vorlocalizer/vorlocalizergui.cpp b/plugins/feature/vorlocalizer/vorlocalizergui.cpp index 79a2895e0..7f8811f4a 100644 --- a/plugins/feature/vorlocalizer/vorlocalizergui.cpp +++ b/plugins/feature/vorlocalizer/vorlocalizergui.cpp @@ -515,6 +515,7 @@ bool VORLocalizerGUI::deserialize(const QByteArray& data) { if (m_settings.deserialize(data)) { + m_feature->setWorkspaceIndex(m_settings.m_workspaceIndex); displaySettings(); applySettings(true); return true; @@ -856,6 +857,7 @@ VORLocalizerGUI::VORLocalizerGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISe m_lastFeatureState(0), m_rrSecondsCount(0) { + m_feature = feature; setAttribute(Qt::WA_DeleteOnClose, true); m_helpURL = "plugins/feature/vorlocalizer/readme.md"; RollupContents *rollupContents = getRollupContents(); @@ -975,6 +977,12 @@ VORLocalizerGUI::~VORLocalizerGUI() qDeleteAll(m_vors); } +void VORLocalizerGUI::setWorkspaceIndex(int index) +{ + m_settings.m_workspaceIndex = index; + m_feature->setWorkspaceIndex(index); +} + void VORLocalizerGUI::blockApplySettings(bool block) { m_doApplySettings = !block; diff --git a/plugins/feature/vorlocalizer/vorlocalizergui.h b/plugins/feature/vorlocalizer/vorlocalizergui.h index 20c953f31..d0785a077 100644 --- a/plugins/feature/vorlocalizer/vorlocalizergui.h +++ b/plugins/feature/vorlocalizer/vorlocalizergui.h @@ -213,7 +213,7 @@ public: bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } void selectVOR(VORGUI *vorGUI, bool selected); - virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; } + virtual void setWorkspaceIndex(int index); virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; } virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; } virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; } diff --git a/plugins/samplemimo/bladerf2mimo/bladerf2mimogui.cpp b/plugins/samplemimo/bladerf2mimo/bladerf2mimogui.cpp index 4513b2bda..4b174c8ce 100644 --- a/plugins/samplemimo/bladerf2mimo/bladerf2mimogui.cpp +++ b/plugins/samplemimo/bladerf2mimo/bladerf2mimogui.cpp @@ -46,7 +46,6 @@ BladeRF2MIMOGui::BladeRF2MIMOGui(DeviceUISet *deviceUISet, QWidget* parent) : DeviceGUI(parent), ui(new Ui::BladeRF2MIMOGui), - m_deviceUISet(deviceUISet), m_settings(), m_rxElseTx(true), m_streamIndex(0), @@ -66,6 +65,7 @@ BladeRF2MIMOGui::BladeRF2MIMOGui(DeviceUISet *deviceUISet, QWidget* parent) : m_sampleRateMode(true) { qDebug("BladeRF2MIMOGui::BladeRF2MIMOGui"); + m_deviceUISet = deviceUISet; setAttribute(Qt::WA_DeleteOnClose, true); ui->setupUi(getContents()); setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); diff --git a/plugins/samplemimo/bladerf2mimo/bladerf2mimogui.h b/plugins/samplemimo/bladerf2mimo/bladerf2mimogui.h index 66627d824..ad0b150be 100644 --- a/plugins/samplemimo/bladerf2mimo/bladerf2mimogui.h +++ b/plugins/samplemimo/bladerf2mimo/bladerf2mimogui.h @@ -51,7 +51,6 @@ protected: private: Ui::BladeRF2MIMOGui* ui; - DeviceUISet* m_deviceUISet; BladeRF2MIMOSettings m_settings; bool m_rxElseTx; //!< Which side is being dealt with int m_streamIndex; //!< Current stream index being dealt with diff --git a/plugins/samplemimo/limesdrmimo/limesdrmimogui.cpp b/plugins/samplemimo/limesdrmimo/limesdrmimogui.cpp index f8c9d1d79..a41cd7d5f 100644 --- a/plugins/samplemimo/limesdrmimo/limesdrmimogui.cpp +++ b/plugins/samplemimo/limesdrmimo/limesdrmimogui.cpp @@ -46,7 +46,6 @@ LimeSDRMIMOGUI::LimeSDRMIMOGUI(DeviceUISet *deviceUISet, QWidget* parent) : DeviceGUI(parent), ui(new Ui::LimeSDRMIMOGUI), - m_deviceUISet(deviceUISet), m_settings(), m_rxElseTx(true), m_streamIndex(0), @@ -68,6 +67,7 @@ LimeSDRMIMOGUI::LimeSDRMIMOGUI(DeviceUISet *deviceUISet, QWidget* parent) : m_sampleRateMode(true) { qDebug("LimeSDRMIMOGUI::LimeSDRMIMOGUI"); + m_deviceUISet = deviceUISet; setAttribute(Qt::WA_DeleteOnClose, true); ui->setupUi(getContents()); setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); diff --git a/plugins/samplemimo/limesdrmimo/limesdrmimogui.h b/plugins/samplemimo/limesdrmimo/limesdrmimogui.h index 42f0a7845..3b50812d0 100644 --- a/plugins/samplemimo/limesdrmimo/limesdrmimogui.h +++ b/plugins/samplemimo/limesdrmimo/limesdrmimogui.h @@ -51,7 +51,6 @@ protected: private: Ui::LimeSDRMIMOGUI* ui; - DeviceUISet* m_deviceUISet; LimeSDRMIMOSettings m_settings; bool m_rxElseTx; //!< Which side is being dealt with int m_streamIndex; //!< Current stream index being dealt with diff --git a/plugins/samplemimo/metismiso/metismisogui.cpp b/plugins/samplemimo/metismiso/metismisogui.cpp index 1dc7aaf60..b3836f1d7 100644 --- a/plugins/samplemimo/metismiso/metismisogui.cpp +++ b/plugins/samplemimo/metismiso/metismisogui.cpp @@ -43,7 +43,6 @@ MetisMISOGui::MetisMISOGui(DeviceUISet *deviceUISet, QWidget* parent) : DeviceGUI(parent), ui(new Ui::MetisMISOGui), - m_deviceUISet(deviceUISet), m_settings(), m_doApplySettings(true), m_forceSettings(true), @@ -52,6 +51,7 @@ MetisMISOGui::MetisMISOGui(DeviceUISet *deviceUISet, QWidget* parent) : m_lastEngineState(DeviceAPI::StNotStarted) { qDebug("MetisMISOGui::MetisMISOGui"); + m_deviceUISet = deviceUISet; setAttribute(Qt::WA_DeleteOnClose, true); m_sampleMIMO = m_deviceUISet->m_deviceAPI->getSampleMIMO(); m_rxSampleRate = 48000; diff --git a/plugins/samplemimo/metismiso/metismisogui.h b/plugins/samplemimo/metismiso/metismisogui.h index 5a79f96f2..f22c53ebf 100644 --- a/plugins/samplemimo/metismiso/metismisogui.h +++ b/plugins/samplemimo/metismiso/metismisogui.h @@ -52,7 +52,6 @@ protected: private: Ui::MetisMISOGui* ui; - DeviceUISet* m_deviceUISet; MetisMISOSettings m_settings; int m_rxSampleRate; int m_txSampleRate; diff --git a/plugins/samplemimo/plutosdrmimo/plutosdrmimogui.cpp b/plugins/samplemimo/plutosdrmimo/plutosdrmimogui.cpp index e95a61e65..c47ee554c 100644 --- a/plugins/samplemimo/plutosdrmimo/plutosdrmimogui.cpp +++ b/plugins/samplemimo/plutosdrmimo/plutosdrmimogui.cpp @@ -47,7 +47,6 @@ PlutoSDRMIMOGUI::PlutoSDRMIMOGUI(DeviceUISet *deviceUISet, QWidget* parent) : DeviceGUI(parent), ui(new Ui::PlutoSDRMIMOGUI), - m_deviceUISet(deviceUISet), m_settings(), m_rxElseTx(true), m_streamIndex(0), @@ -68,6 +67,7 @@ PlutoSDRMIMOGUI::PlutoSDRMIMOGUI(DeviceUISet *deviceUISet, QWidget* parent) : m_sampleRateMode(true) { qDebug("PlutoSDRMIMOGui::PlutoSDRMIMOGui"); + m_deviceUISet = deviceUISet; setAttribute(Qt::WA_DeleteOnClose, true); ui->setupUi(getContents()); setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); diff --git a/plugins/samplemimo/plutosdrmimo/plutosdrmimogui.h b/plugins/samplemimo/plutosdrmimo/plutosdrmimogui.h index 0c32f2c07..e7993c1bb 100644 --- a/plugins/samplemimo/plutosdrmimo/plutosdrmimogui.h +++ b/plugins/samplemimo/plutosdrmimo/plutosdrmimogui.h @@ -51,7 +51,6 @@ protected: private: Ui::PlutoSDRMIMOGUI* ui; - DeviceUISet* m_deviceUISet; PlutoSDRMIMOSettings m_settings; bool m_rxElseTx; //!< Which side is being dealt with int m_streamIndex; //!< Current stream index being dealt with diff --git a/plugins/samplemimo/testmi/testmigui.cpp b/plugins/samplemimo/testmi/testmigui.cpp index 99752212a..cc0d6a4cf 100644 --- a/plugins/samplemimo/testmi/testmigui.cpp +++ b/plugins/samplemimo/testmi/testmigui.cpp @@ -43,7 +43,6 @@ TestMIGui::TestMIGui(DeviceUISet *deviceUISet, QWidget* parent) : DeviceGUI(parent), ui(new Ui::TestMIGui), - m_deviceUISet(deviceUISet), m_settings(), m_streamIndex(0), m_spectrumStreamIndex(0), @@ -54,6 +53,7 @@ TestMIGui::TestMIGui(DeviceUISet *deviceUISet, QWidget* parent) : m_lastEngineState(DeviceAPI::StNotStarted) { qDebug("TestMIGui::TestMIGui"); + m_deviceUISet = deviceUISet; setAttribute(Qt::WA_DeleteOnClose, true); m_helpURL = "plugins/samplemimo/testmi/readme.md"; ui->setupUi(getContents()); diff --git a/plugins/samplemimo/testmi/testmigui.h b/plugins/samplemimo/testmi/testmigui.h index 0fff5a4f9..41a949014 100644 --- a/plugins/samplemimo/testmi/testmigui.h +++ b/plugins/samplemimo/testmi/testmigui.h @@ -52,7 +52,6 @@ protected: private: Ui::TestMIGui* ui; - DeviceUISet* m_deviceUISet; TestMISettings m_settings; int m_streamIndex; //!< Current stream index being dealt with int m_spectrumStreamIndex; //!< Index of the stream displayed on main spectrum diff --git a/plugins/samplemimo/testmosync/testmosyncgui.cpp b/plugins/samplemimo/testmosync/testmosyncgui.cpp index 764414e2d..1b53e70fe 100644 --- a/plugins/samplemimo/testmosync/testmosyncgui.cpp +++ b/plugins/samplemimo/testmosync/testmosyncgui.cpp @@ -39,7 +39,6 @@ TestMOSyncGui::TestMOSyncGui(DeviceUISet *deviceUISet, QWidget* parent) : DeviceGUI(parent), ui(new Ui::TestMOSyncGui), - m_deviceUISet(deviceUISet), m_doApplySettings(true), m_forceSettings(true), m_settings(), @@ -50,6 +49,7 @@ TestMOSyncGui::TestMOSyncGui(DeviceUISet *deviceUISet, QWidget* parent) : m_tickCount(0), m_lastEngineState(DeviceAPI::StNotStarted) { + m_deviceUISet = deviceUISet; setAttribute(Qt::WA_DeleteOnClose, true); m_helpURL = "plugins/samplemimo/testmosync/readme.md"; QWidget *contents = getContents(); diff --git a/plugins/samplemimo/testmosync/testmosyncgui.h b/plugins/samplemimo/testmosync/testmosyncgui.h index 7c712e1af..7815d10ba 100644 --- a/plugins/samplemimo/testmosync/testmosyncgui.h +++ b/plugins/samplemimo/testmosync/testmosyncgui.h @@ -51,7 +51,6 @@ public: private: Ui::TestMOSyncGui* ui; - DeviceUISet* m_deviceUISet; bool m_doApplySettings; bool m_forceSettings; TestMOSyncSettings m_settings; diff --git a/plugins/samplemimo/xtrxmimo/xtrxmimogui.cpp b/plugins/samplemimo/xtrxmimo/xtrxmimogui.cpp index d31c970ea..1f66fa5d0 100644 --- a/plugins/samplemimo/xtrxmimo/xtrxmimogui.cpp +++ b/plugins/samplemimo/xtrxmimo/xtrxmimogui.cpp @@ -44,7 +44,6 @@ XTRXMIMOGUI::XTRXMIMOGUI(DeviceUISet *deviceUISet, QWidget* parent) : DeviceGUI(parent), ui(new Ui::XTRXMIMOGUI), - m_deviceUISet(deviceUISet), m_settings(), m_rxElseTx(true), m_streamIndex(0), @@ -65,6 +64,7 @@ XTRXMIMOGUI::XTRXMIMOGUI(DeviceUISet *deviceUISet, QWidget* parent) : m_sampleRateMode(true) { qDebug("XTRXMIMOGUI::XTRXMIMOGUI"); + m_deviceUISet = deviceUISet; setAttribute(Qt::WA_DeleteOnClose, true); ui->setupUi(getContents()); setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); diff --git a/plugins/samplemimo/xtrxmimo/xtrxmimogui.h b/plugins/samplemimo/xtrxmimo/xtrxmimogui.h index 9b72ab2a9..913195065 100644 --- a/plugins/samplemimo/xtrxmimo/xtrxmimogui.h +++ b/plugins/samplemimo/xtrxmimo/xtrxmimogui.h @@ -51,7 +51,6 @@ protected: private: Ui::XTRXMIMOGUI* ui; - DeviceUISet* m_deviceUISet; XTRXMIMOSettings m_settings; bool m_rxElseTx; //!< Which side is being dealt with int m_streamIndex; //!< Current stream index being dealt with diff --git a/plugins/samplesink/audiooutput/audiooutputgui.cpp b/plugins/samplesink/audiooutput/audiooutputgui.cpp index cd8c5caf8..f20cfaf19 100644 --- a/plugins/samplesink/audiooutput/audiooutputgui.cpp +++ b/plugins/samplesink/audiooutput/audiooutputgui.cpp @@ -34,12 +34,12 @@ AudioOutputGui::AudioOutputGui(DeviceUISet *deviceUISet, QWidget* parent) : DeviceGUI(parent), ui(new Ui::AudioOutputGui), - m_deviceUISet(deviceUISet), m_doApplySettings(true), m_forceSettings(true), m_settings(), m_centerFrequency(0) { + m_deviceUISet = deviceUISet; setAttribute(Qt::WA_DeleteOnClose, true); m_audioOutput = (AudioOutput*) m_deviceUISet->m_deviceAPI->getSampleSink(); diff --git a/plugins/samplesink/audiooutput/audiooutputgui.h b/plugins/samplesink/audiooutput/audiooutputgui.h index 91d74861f..ae38f6dbb 100644 --- a/plugins/samplesink/audiooutput/audiooutputgui.h +++ b/plugins/samplesink/audiooutput/audiooutputgui.h @@ -52,7 +52,6 @@ protected: private: Ui::AudioOutputGui* ui; - DeviceUISet* m_deviceUISet; AudioOutput* m_audioOutput; bool m_doApplySettings; bool m_forceSettings; diff --git a/plugins/samplesink/bladerf1output/bladerf1outputgui.cpp b/plugins/samplesink/bladerf1output/bladerf1outputgui.cpp index bc88fa575..efd1bcfb7 100644 --- a/plugins/samplesink/bladerf1output/bladerf1outputgui.cpp +++ b/plugins/samplesink/bladerf1output/bladerf1outputgui.cpp @@ -34,7 +34,6 @@ Bladerf1OutputGui::Bladerf1OutputGui(DeviceUISet *deviceUISet, QWidget* parent) : DeviceGUI(parent), ui(new Ui::Bladerf1OutputGui), - m_deviceUISet(deviceUISet), m_doApplySettings(true), m_forceSettings(true), m_settings(), @@ -43,6 +42,7 @@ Bladerf1OutputGui::Bladerf1OutputGui(DeviceUISet *deviceUISet, QWidget* parent) m_sampleRate(0), m_lastEngineState(DeviceAPI::StNotStarted) { + m_deviceUISet = deviceUISet; setAttribute(Qt::WA_DeleteOnClose, true); m_deviceSampleSink = (Bladerf1Output*) m_deviceUISet->m_deviceAPI->getSampleSink(); diff --git a/plugins/samplesink/bladerf1output/bladerf1outputgui.h b/plugins/samplesink/bladerf1output/bladerf1outputgui.h index 4e0bbecc7..b7689f3f3 100644 --- a/plugins/samplesink/bladerf1output/bladerf1outputgui.h +++ b/plugins/samplesink/bladerf1output/bladerf1outputgui.h @@ -52,7 +52,6 @@ protected: private: Ui::Bladerf1OutputGui* ui; - DeviceUISet* m_deviceUISet; bool m_doApplySettings; bool m_forceSettings; BladeRF1OutputSettings m_settings; diff --git a/plugins/samplesink/bladerf2output/bladerf2outputgui.cpp b/plugins/samplesink/bladerf2output/bladerf2outputgui.cpp index 19377ef2b..8b2fc5bfd 100644 --- a/plugins/samplesink/bladerf2output/bladerf2outputgui.cpp +++ b/plugins/samplesink/bladerf2output/bladerf2outputgui.cpp @@ -35,7 +35,6 @@ BladeRF2OutputGui::BladeRF2OutputGui(DeviceUISet *deviceUISet, QWidget* parent) : DeviceGUI(parent), ui(new Ui::BladeRF2OutputGui), - m_deviceUISet(deviceUISet), m_doApplySettings(true), m_forceSettings(true), m_settings(), @@ -43,6 +42,7 @@ BladeRF2OutputGui::BladeRF2OutputGui(DeviceUISet *deviceUISet, QWidget* parent) m_sampleRate(0), m_lastEngineState(DeviceAPI::StNotStarted) { + m_deviceUISet = deviceUISet; setAttribute(Qt::WA_DeleteOnClose, true); m_sampleSink = (BladeRF2Output*) m_deviceUISet->m_deviceAPI->getSampleSink(); int max, min, step; diff --git a/plugins/samplesink/bladerf2output/bladerf2outputgui.h b/plugins/samplesink/bladerf2output/bladerf2outputgui.h index 4f6e6ce21..345754dc3 100644 --- a/plugins/samplesink/bladerf2output/bladerf2outputgui.h +++ b/plugins/samplesink/bladerf2output/bladerf2outputgui.h @@ -53,7 +53,6 @@ protected: private: Ui::BladeRF2OutputGui* ui; - DeviceUISet* m_deviceUISet; bool m_doApplySettings; bool m_forceSettings; BladeRF2OutputSettings m_settings; diff --git a/plugins/samplesink/fileoutput/fileoutputgui.cpp b/plugins/samplesink/fileoutput/fileoutputgui.cpp index 37c443db6..48a93316f 100644 --- a/plugins/samplesink/fileoutput/fileoutputgui.cpp +++ b/plugins/samplesink/fileoutput/fileoutputgui.cpp @@ -41,7 +41,6 @@ FileOutputGui::FileOutputGui(DeviceUISet *deviceUISet, QWidget* parent) : DeviceGUI(parent), ui(new Ui::FileOutputGui), - m_deviceUISet(deviceUISet), m_doApplySettings(true), m_forceSettings(true), m_settings(), @@ -53,6 +52,7 @@ FileOutputGui::FileOutputGui(DeviceUISet *deviceUISet, QWidget* parent) : m_tickCount(0), m_lastEngineState(DeviceAPI::StNotStarted) { + m_deviceUISet = deviceUISet; setAttribute(Qt::WA_DeleteOnClose, true); ui->setupUi(getContents()); setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); diff --git a/plugins/samplesink/fileoutput/fileoutputgui.h b/plugins/samplesink/fileoutput/fileoutputgui.h index 1ec881108..f7ca4da87 100644 --- a/plugins/samplesink/fileoutput/fileoutputgui.h +++ b/plugins/samplesink/fileoutput/fileoutputgui.h @@ -54,7 +54,6 @@ protected: private: Ui::FileOutputGui* ui; - DeviceUISet* m_deviceUISet; bool m_doApplySettings; bool m_forceSettings; FileOutputSettings m_settings; diff --git a/plugins/samplesink/hackrfoutput/hackrfoutputgui.cpp b/plugins/samplesink/hackrfoutput/hackrfoutputgui.cpp index 1bd613033..b4be2bdb8 100644 --- a/plugins/samplesink/hackrfoutput/hackrfoutputgui.cpp +++ b/plugins/samplesink/hackrfoutput/hackrfoutputgui.cpp @@ -37,7 +37,6 @@ HackRFOutputGui::HackRFOutputGui(DeviceUISet *deviceUISet, QWidget* parent) : DeviceGUI(parent), ui(new Ui::HackRFOutputGui), - m_deviceUISet(deviceUISet), m_forceSettings(true), m_settings(), m_sampleRateMode(true), @@ -45,6 +44,7 @@ HackRFOutputGui::HackRFOutputGui(DeviceUISet *deviceUISet, QWidget* parent) : m_lastEngineState(DeviceAPI::StNotStarted), m_doApplySettings(true) { + m_deviceUISet = deviceUISet; setAttribute(Qt::WA_DeleteOnClose, true); m_deviceSampleSink = (HackRFOutput*) m_deviceUISet->m_deviceAPI->getSampleSink(); diff --git a/plugins/samplesink/hackrfoutput/hackrfoutputgui.h b/plugins/samplesink/hackrfoutput/hackrfoutputgui.h index b41fd3f24..0541ff791 100644 --- a/plugins/samplesink/hackrfoutput/hackrfoutputgui.h +++ b/plugins/samplesink/hackrfoutput/hackrfoutputgui.h @@ -62,7 +62,6 @@ protected: private: Ui::HackRFOutputGui* ui; - DeviceUISet* m_deviceUISet; bool m_forceSettings; HackRFOutputSettings m_settings; bool m_sampleRateMode; //!< true: device, false: base band sample rate update mode diff --git a/plugins/samplesink/limesdroutput/limesdroutputgui.cpp b/plugins/samplesink/limesdroutput/limesdroutputgui.cpp index 77c709329..77135e5ae 100644 --- a/plugins/samplesink/limesdroutput/limesdroutputgui.cpp +++ b/plugins/samplesink/limesdroutput/limesdroutputgui.cpp @@ -32,7 +32,6 @@ LimeSDROutputGUI::LimeSDROutputGUI(DeviceUISet *deviceUISet, QWidget* parent) : DeviceGUI(parent), ui(new Ui::LimeSDROutputGUI), - m_deviceUISet(deviceUISet), m_settings(), m_sampleRateMode(true), m_sampleRate(0), @@ -42,6 +41,7 @@ LimeSDROutputGUI::LimeSDROutputGUI(DeviceUISet *deviceUISet, QWidget* parent) : m_statusCounter(0), m_deviceStatusCounter(0) { + m_deviceUISet = deviceUISet; setAttribute(Qt::WA_DeleteOnClose, true); m_limeSDROutput = (LimeSDROutput*) m_deviceUISet->m_deviceAPI->getSampleSink(); diff --git a/plugins/samplesink/limesdroutput/limesdroutputgui.h b/plugins/samplesink/limesdroutput/limesdroutputgui.h index c67e8e110..71831b414 100644 --- a/plugins/samplesink/limesdroutput/limesdroutputgui.h +++ b/plugins/samplesink/limesdroutput/limesdroutputgui.h @@ -52,7 +52,6 @@ protected: private: Ui::LimeSDROutputGUI* ui; - DeviceUISet* m_deviceUISet; LimeSDROutput* m_limeSDROutput; //!< Same object as above but gives easy access to LimeSDROutput methods and attributes that are used intensively LimeSDROutputSettings m_settings; bool m_sampleRateMode; //!< true: device, false: base band sample rate update mode diff --git a/plugins/samplesink/localoutput/localoutputgui.cpp b/plugins/samplesink/localoutput/localoutputgui.cpp index 7eb1035d8..054c2a1fc 100644 --- a/plugins/samplesink/localoutput/localoutputgui.cpp +++ b/plugins/samplesink/localoutput/localoutputgui.cpp @@ -46,7 +46,6 @@ LocalOutputGui::LocalOutputGui(DeviceUISet *deviceUISet, QWidget* parent) : DeviceGUI(parent), ui(new Ui::LocalOutputGui), - m_deviceUISet(deviceUISet), m_settings(), m_sampleSink(0), m_acquisition(false), @@ -58,6 +57,7 @@ LocalOutputGui::LocalOutputGui(DeviceUISet *deviceUISet, QWidget* parent) : m_doApplySettings(true), m_forceSettings(true) { + m_deviceUISet = deviceUISet; setAttribute(Qt::WA_DeleteOnClose, true); m_paletteGreenText.setColor(QPalette::WindowText, Qt::green); m_paletteWhiteText.setColor(QPalette::WindowText, Qt::white); diff --git a/plugins/samplesink/localoutput/localoutputgui.h b/plugins/samplesink/localoutput/localoutputgui.h index d6ec64ede..9c2e46252 100644 --- a/plugins/samplesink/localoutput/localoutputgui.h +++ b/plugins/samplesink/localoutput/localoutputgui.h @@ -53,7 +53,6 @@ protected: private: Ui::LocalOutputGui* ui; - DeviceUISet* m_deviceUISet; LocalOutputSettings m_settings; //!< current settings LocalOutput* m_sampleSink; bool m_acquisition; diff --git a/plugins/samplesink/plutosdroutput/plutosdroutputgui.cpp b/plugins/samplesink/plutosdroutput/plutosdroutputgui.cpp index fff366918..29a4d85b9 100644 --- a/plugins/samplesink/plutosdroutput/plutosdroutputgui.cpp +++ b/plugins/samplesink/plutosdroutput/plutosdroutputgui.cpp @@ -34,7 +34,6 @@ PlutoSDROutputGUI::PlutoSDROutputGUI(DeviceUISet *deviceUISet, QWidget* parent) : DeviceGUI(parent), ui(new Ui::PlutoSDROutputGUI), - m_deviceUISet(deviceUISet), m_settings(), m_sampleRateMode(true), m_forceSettings(true), @@ -45,6 +44,7 @@ PlutoSDROutputGUI::PlutoSDROutputGUI(DeviceUISet *deviceUISet, QWidget* parent) m_doApplySettings(true), m_statusCounter(0) { + m_deviceUISet = deviceUISet; setAttribute(Qt::WA_DeleteOnClose, true); m_sampleSink = (PlutoSDROutput*) m_deviceUISet->m_deviceAPI->getSampleSink(); diff --git a/plugins/samplesink/plutosdroutput/plutosdroutputgui.h b/plugins/samplesink/plutosdroutput/plutosdroutputgui.h index 50c74d13c..0032780a9 100644 --- a/plugins/samplesink/plutosdroutput/plutosdroutputgui.h +++ b/plugins/samplesink/plutosdroutput/plutosdroutputgui.h @@ -53,7 +53,6 @@ protected: private: Ui::PlutoSDROutputGUI* ui; - DeviceUISet* m_deviceUISet; PlutoSDROutputSettings m_settings; bool m_sampleRateMode; //!< true: device, false: base band sample rate update mode bool m_forceSettings; diff --git a/plugins/samplesink/remoteoutput/remoteoutputgui.cpp b/plugins/samplesink/remoteoutput/remoteoutputgui.cpp index 13c3b809d..8d494378e 100644 --- a/plugins/samplesink/remoteoutput/remoteoutputgui.cpp +++ b/plugins/samplesink/remoteoutput/remoteoutputgui.cpp @@ -44,7 +44,6 @@ RemoteOutputSinkGui::RemoteOutputSinkGui(DeviceUISet *deviceUISet, QWidget* parent) : DeviceGUI(parent), ui(new Ui::RemoteOutputGui), - m_deviceUISet(deviceUISet), m_settings(), m_remoteOutput(0), m_deviceCenterFrequency(0), @@ -56,6 +55,7 @@ RemoteOutputSinkGui::RemoteOutputSinkGui(DeviceUISet *deviceUISet, QWidget* pare m_forceSettings(true), m_remoteAPIConnected(false) { + m_deviceUISet = deviceUISet; setAttribute(Qt::WA_DeleteOnClose, true); m_countUnrecoverable = 0; m_countRecovered = 0; diff --git a/plugins/samplesink/remoteoutput/remoteoutputgui.h b/plugins/samplesink/remoteoutput/remoteoutputgui.h index cbf0d522f..77aab4905 100644 --- a/plugins/samplesink/remoteoutput/remoteoutputgui.h +++ b/plugins/samplesink/remoteoutput/remoteoutputgui.h @@ -84,7 +84,6 @@ protected: private: Ui::RemoteOutputGui* ui; - DeviceUISet* m_deviceUISet; RemoteOutputSettings m_settings; //!< current settings RemoteOutputSettings m_controlSettings; //!< settings last sent to device via control port QTimer m_updateTimer; diff --git a/plugins/samplesink/soapysdroutput/soapysdroutputgui.cpp b/plugins/samplesink/soapysdroutput/soapysdroutputgui.cpp index c664ac5bc..ae98737d9 100644 --- a/plugins/samplesink/soapysdroutput/soapysdroutputgui.cpp +++ b/plugins/samplesink/soapysdroutput/soapysdroutputgui.cpp @@ -41,7 +41,6 @@ SoapySDROutputGui::SoapySDROutputGui(DeviceUISet *deviceUISet, QWidget* parent) : DeviceGUI(parent), ui(new Ui::SoapySDROutputGui), - m_deviceUISet(deviceUISet), m_forceSettings(true), m_doApplySettings(true), m_sampleSink(0), @@ -57,6 +56,7 @@ SoapySDROutputGui::SoapySDROutputGui(DeviceUISet *deviceUISet, QWidget* parent) m_autoDCCorrection(0), m_autoIQCorrection(0) { + m_deviceUISet = deviceUISet; setAttribute(Qt::WA_DeleteOnClose, true); m_sampleSink = (SoapySDROutput*) m_deviceUISet->m_deviceAPI->getSampleSink(); ui->setupUi(getContents()); diff --git a/plugins/samplesink/soapysdroutput/soapysdroutputgui.h b/plugins/samplesink/soapysdroutput/soapysdroutputgui.h index 1021006cd..258df8362 100644 --- a/plugins/samplesink/soapysdroutput/soapysdroutputgui.h +++ b/plugins/samplesink/soapysdroutput/soapysdroutputgui.h @@ -73,7 +73,6 @@ private: Ui::SoapySDROutputGui* ui; - DeviceUISet* m_deviceUISet; bool m_forceSettings; bool m_doApplySettings; SoapySDROutputSettings m_settings; diff --git a/plugins/samplesink/testsink/testsinkgui.cpp b/plugins/samplesink/testsink/testsinkgui.cpp index ecccbf97c..04b5ffa6f 100644 --- a/plugins/samplesink/testsink/testsinkgui.cpp +++ b/plugins/samplesink/testsink/testsinkgui.cpp @@ -39,7 +39,6 @@ TestSinkGui::TestSinkGui(DeviceUISet *deviceUISet, QWidget* parent) : DeviceGUI(parent), ui(new Ui::TestSinkGui), - m_deviceUISet(deviceUISet), m_doApplySettings(true), m_forceSettings(true), m_settings(), @@ -49,6 +48,7 @@ TestSinkGui::TestSinkGui(DeviceUISet *deviceUISet, QWidget* parent) : m_tickCount(0), m_lastEngineState(DeviceAPI::StNotStarted) { + m_deviceUISet = deviceUISet; setAttribute(Qt::WA_DeleteOnClose, true); m_helpURL = "plugins/samplesink/testsink/readme.md"; QWidget *contents = getContents(); diff --git a/plugins/samplesink/testsink/testsinkgui.h b/plugins/samplesink/testsink/testsinkgui.h index 76d7e0a95..8d8b5f1da 100644 --- a/plugins/samplesink/testsink/testsinkgui.h +++ b/plugins/samplesink/testsink/testsinkgui.h @@ -52,7 +52,6 @@ public: private: Ui::TestSinkGui* ui; - DeviceUISet* m_deviceUISet; bool m_doApplySettings; bool m_forceSettings; TestSinkSettings m_settings; diff --git a/plugins/samplesink/usrpoutput/usrpoutputgui.cpp b/plugins/samplesink/usrpoutput/usrpoutputgui.cpp index a67c3eaaa..64bb7fb97 100644 --- a/plugins/samplesink/usrpoutput/usrpoutputgui.cpp +++ b/plugins/samplesink/usrpoutput/usrpoutputgui.cpp @@ -33,7 +33,6 @@ USRPOutputGUI::USRPOutputGUI(DeviceUISet *deviceUISet, QWidget* parent) : DeviceGUI(parent), ui(new Ui::USRPOutputGUI), - m_deviceUISet(deviceUISet), m_settings(), m_sampleRateMode(true), m_sampleRate(0), @@ -43,6 +42,7 @@ USRPOutputGUI::USRPOutputGUI(DeviceUISet *deviceUISet, QWidget* parent) : m_statusCounter(0), m_deviceStatusCounter(0) { + m_deviceUISet = deviceUISet; setAttribute(Qt::WA_DeleteOnClose, true); m_usrpOutput = (USRPOutput*) m_deviceUISet->m_deviceAPI->getSampleSink(); diff --git a/plugins/samplesink/usrpoutput/usrpoutputgui.h b/plugins/samplesink/usrpoutput/usrpoutputgui.h index 481fd841f..1a7ce6ba6 100644 --- a/plugins/samplesink/usrpoutput/usrpoutputgui.h +++ b/plugins/samplesink/usrpoutput/usrpoutputgui.h @@ -59,7 +59,6 @@ protected: private: Ui::USRPOutputGUI* ui; - DeviceUISet* m_deviceUISet; USRPOutput* m_usrpOutput; //!< Same object as above but gives easy access to USRPOutput methods and attributes that are used intensively USRPOutputSettings m_settings; bool m_sampleRateMode; //!< true: device, false: base band sample rate update mode diff --git a/plugins/samplesink/xtrxoutput/xtrxoutputgui.cpp b/plugins/samplesink/xtrxoutput/xtrxoutputgui.cpp index f5b8a79c2..01ba67f2b 100644 --- a/plugins/samplesink/xtrxoutput/xtrxoutputgui.cpp +++ b/plugins/samplesink/xtrxoutput/xtrxoutputgui.cpp @@ -34,7 +34,6 @@ XTRXOutputGUI::XTRXOutputGUI(DeviceUISet *deviceUISet, QWidget* parent) : DeviceGUI(parent), ui(new Ui::XTRXOutputGUI), - m_deviceUISet(deviceUISet), m_settings(), m_sampleRateMode(true), m_sampleRate(0), @@ -44,6 +43,7 @@ XTRXOutputGUI::XTRXOutputGUI(DeviceUISet *deviceUISet, QWidget* parent) : m_statusCounter(0), m_deviceStatusCounter(0) { + m_deviceUISet = deviceUISet; setAttribute(Qt::WA_DeleteOnClose, true); m_XTRXOutput = (XTRXOutput*) m_deviceUISet->m_deviceAPI->getSampleSink(); diff --git a/plugins/samplesink/xtrxoutput/xtrxoutputgui.h b/plugins/samplesink/xtrxoutput/xtrxoutputgui.h index 73ff03d7b..cfec0e87e 100644 --- a/plugins/samplesink/xtrxoutput/xtrxoutputgui.h +++ b/plugins/samplesink/xtrxoutput/xtrxoutputgui.h @@ -51,7 +51,6 @@ protected: private: Ui::XTRXOutputGUI* ui; - DeviceUISet* m_deviceUISet; XTRXOutput* m_XTRXOutput; //!< Same object as above but gives easy access to XTRXInput methods and attributes that are used intensively XTRXOutputSettings m_settings; bool m_sampleRateMode; //!< true: device, false: base band sample rate update mode diff --git a/plugins/samplesource/airspy/airspygui.cpp b/plugins/samplesource/airspy/airspygui.cpp index 291563abd..b5a8b7e8b 100644 --- a/plugins/samplesource/airspy/airspygui.cpp +++ b/plugins/samplesource/airspy/airspygui.cpp @@ -37,13 +37,13 @@ AirspyGui::AirspyGui(DeviceUISet *deviceUISet, QWidget* parent) : DeviceGUI(parent), ui(new Ui::AirspyGui), - m_deviceUISet(deviceUISet), m_doApplySettings(true), m_forceSettings(true), m_settings(), m_sampleSource(0), m_lastEngineState(DeviceAPI::StNotStarted) { + m_deviceUISet = deviceUISet; setAttribute(Qt::WA_DeleteOnClose, true); m_sampleSource = (AirspyInput*) m_deviceUISet->m_deviceAPI->getSampleSource(); diff --git a/plugins/samplesource/airspy/airspygui.h b/plugins/samplesource/airspy/airspygui.h index cc8151b50..2f7de1f48 100644 --- a/plugins/samplesource/airspy/airspygui.h +++ b/plugins/samplesource/airspy/airspygui.h @@ -54,7 +54,6 @@ protected: private: Ui::AirspyGui* ui; - DeviceUISet* m_deviceUISet; bool m_doApplySettings; bool m_forceSettings; AirspySettings m_settings; diff --git a/plugins/samplesource/airspyhf/airspyhfgui.cpp b/plugins/samplesource/airspyhf/airspyhfgui.cpp index 6a0803b75..a056e7ae0 100644 --- a/plugins/samplesource/airspyhf/airspyhfgui.cpp +++ b/plugins/samplesource/airspyhf/airspyhfgui.cpp @@ -36,13 +36,13 @@ AirspyHFGui::AirspyHFGui(DeviceUISet *deviceUISet, QWidget* parent) : DeviceGUI(parent), ui(new Ui::AirspyHFGui), - m_deviceUISet(deviceUISet), m_doApplySettings(true), m_forceSettings(true), m_settings(), m_sampleSource(0), m_lastEngineState(DeviceAPI::StNotStarted) { + m_deviceUISet = deviceUISet; setAttribute(Qt::WA_DeleteOnClose, true); m_sampleSource = (AirspyHFInput*) m_deviceUISet->m_deviceAPI->getSampleSource(); diff --git a/plugins/samplesource/airspyhf/airspyhfgui.h b/plugins/samplesource/airspyhf/airspyhfgui.h index 238003e29..d02356f5a 100644 --- a/plugins/samplesource/airspyhf/airspyhfgui.h +++ b/plugins/samplesource/airspyhf/airspyhfgui.h @@ -55,7 +55,6 @@ protected: private: Ui::AirspyHFGui* ui; - DeviceUISet* m_deviceUISet; bool m_doApplySettings; bool m_forceSettings; AirspyHFSettings m_settings; diff --git a/plugins/samplesource/audioinput/audioinputgui.cpp b/plugins/samplesource/audioinput/audioinputgui.cpp index 273a5050e..71544fe44 100644 --- a/plugins/samplesource/audioinput/audioinputgui.cpp +++ b/plugins/samplesource/audioinput/audioinputgui.cpp @@ -34,12 +34,12 @@ AudioInputGui::AudioInputGui(DeviceUISet *deviceUISet, QWidget* parent) : DeviceGUI(parent), ui(new Ui::AudioInputGui), - m_deviceUISet(deviceUISet), m_forceSettings(true), m_settings(), m_sampleSource(nullptr), m_centerFrequency(0) { + m_deviceUISet = deviceUISet; setAttribute(Qt::WA_DeleteOnClose, true); m_sampleSource = (AudioInput*) m_deviceUISet->m_deviceAPI->getSampleSource(); diff --git a/plugins/samplesource/audioinput/audioinputgui.h b/plugins/samplesource/audioinput/audioinputgui.h index a11d90cae..fd50166f4 100644 --- a/plugins/samplesource/audioinput/audioinputgui.h +++ b/plugins/samplesource/audioinput/audioinputgui.h @@ -53,7 +53,6 @@ protected: private: Ui::AudioInputGui* ui; - DeviceUISet* m_deviceUISet; bool m_doApplySettings; bool m_forceSettings; AudioInputSettings m_settings; diff --git a/plugins/samplesource/bladerf1input/bladerf1inputgui.cpp b/plugins/samplesource/bladerf1input/bladerf1inputgui.cpp index 17c948c0d..e5fadcdbd 100644 --- a/plugins/samplesource/bladerf1input/bladerf1inputgui.cpp +++ b/plugins/samplesource/bladerf1input/bladerf1inputgui.cpp @@ -36,7 +36,6 @@ Bladerf1InputGui::Bladerf1InputGui(DeviceUISet *deviceUISet, QWidget* parent) : DeviceGUI(parent), ui(new Ui::Bladerf1InputGui), - m_deviceUISet(deviceUISet), m_forceSettings(true), m_doApplySettings(true), m_settings(), @@ -45,6 +44,7 @@ Bladerf1InputGui::Bladerf1InputGui(DeviceUISet *deviceUISet, QWidget* parent) : m_sampleRate(0), m_lastEngineState(DeviceAPI::StNotStarted) { + m_deviceUISet = deviceUISet; setAttribute(Qt::WA_DeleteOnClose, true); m_sampleSource = (Bladerf1Input*) m_deviceUISet->m_deviceAPI->getSampleSource(); diff --git a/plugins/samplesource/bladerf1input/bladerf1inputgui.h b/plugins/samplesource/bladerf1input/bladerf1inputgui.h index 9202e42f2..106afe231 100644 --- a/plugins/samplesource/bladerf1input/bladerf1inputgui.h +++ b/plugins/samplesource/bladerf1input/bladerf1inputgui.h @@ -48,7 +48,6 @@ public: private: Ui::Bladerf1InputGui* ui; - DeviceUISet* m_deviceUISet; bool m_forceSettings; bool m_doApplySettings; BladeRF1InputSettings m_settings; diff --git a/plugins/samplesource/bladerf2input/bladerf2inputgui.cpp b/plugins/samplesource/bladerf2input/bladerf2inputgui.cpp index fea4b1505..6511b955f 100644 --- a/plugins/samplesource/bladerf2input/bladerf2inputgui.cpp +++ b/plugins/samplesource/bladerf2input/bladerf2inputgui.cpp @@ -36,7 +36,6 @@ BladeRF2InputGui::BladeRF2InputGui(DeviceUISet *deviceUISet, QWidget* parent) : DeviceGUI(parent), ui(new Ui::Bladerf2InputGui), - m_deviceUISet(deviceUISet), m_forceSettings(true), m_doApplySettings(true), m_settings(), @@ -45,6 +44,7 @@ BladeRF2InputGui::BladeRF2InputGui(DeviceUISet *deviceUISet, QWidget* parent) : m_sampleRate(0), m_lastEngineState(DeviceAPI::StNotStarted) { + m_deviceUISet = deviceUISet; setAttribute(Qt::WA_DeleteOnClose, true); m_sampleSource = (BladeRF2Input*) m_deviceUISet->m_deviceAPI->getSampleSource(); int max, min, step; diff --git a/plugins/samplesource/bladerf2input/bladerf2inputgui.h b/plugins/samplesource/bladerf2input/bladerf2inputgui.h index 45c842676..517e21f3e 100644 --- a/plugins/samplesource/bladerf2input/bladerf2inputgui.h +++ b/plugins/samplesource/bladerf2input/bladerf2inputgui.h @@ -48,7 +48,6 @@ public: private: Ui::Bladerf2InputGui* ui; - DeviceUISet* m_deviceUISet; bool m_forceSettings; bool m_doApplySettings; BladeRF2InputSettings m_settings; diff --git a/plugins/samplesource/fcdpro/fcdprogui.cpp b/plugins/samplesource/fcdpro/fcdprogui.cpp index 692165e14..5aa11fd48 100644 --- a/plugins/samplesource/fcdpro/fcdprogui.cpp +++ b/plugins/samplesource/fcdpro/fcdprogui.cpp @@ -35,12 +35,12 @@ FCDProGui::FCDProGui(DeviceUISet *deviceUISet, QWidget* parent) : DeviceGUI(parent), ui(new Ui::FCDProGui), - m_deviceUISet(deviceUISet), m_forceSettings(true), m_settings(), m_sampleSource(NULL), m_lastEngineState(DeviceAPI::StNotStarted) { + m_deviceUISet = deviceUISet; setAttribute(Qt::WA_DeleteOnClose, true); m_sampleSource = (FCDProInput*) m_deviceUISet->m_deviceAPI->getSampleSource(); diff --git a/plugins/samplesource/fcdpro/fcdprogui.h b/plugins/samplesource/fcdpro/fcdprogui.h index 717d0a588..83ebe7b7f 100644 --- a/plugins/samplesource/fcdpro/fcdprogui.h +++ b/plugins/samplesource/fcdpro/fcdprogui.h @@ -49,7 +49,6 @@ public: private: Ui::FCDProGui* ui; - DeviceUISet* m_deviceUISet; bool m_doApplySettings; bool m_forceSettings; FCDProSettings m_settings; diff --git a/plugins/samplesource/fcdproplus/fcdproplusgui.cpp b/plugins/samplesource/fcdproplus/fcdproplusgui.cpp index 76d3f6737..8040b546e 100644 --- a/plugins/samplesource/fcdproplus/fcdproplusgui.cpp +++ b/plugins/samplesource/fcdproplus/fcdproplusgui.cpp @@ -36,12 +36,12 @@ FCDProPlusGui::FCDProPlusGui(DeviceUISet *deviceUISet, QWidget* parent) : DeviceGUI(parent), ui(new Ui::FCDProPlusGui), - m_deviceUISet(deviceUISet), m_forceSettings(true), m_settings(), m_sampleSource(NULL), m_lastEngineState(DeviceAPI::StNotStarted) { + m_deviceUISet = deviceUISet; setAttribute(Qt::WA_DeleteOnClose, true); m_sampleSource = (FCDProPlusInput*) m_deviceUISet->m_deviceAPI->getSampleSource(); diff --git a/plugins/samplesource/fcdproplus/fcdproplusgui.h b/plugins/samplesource/fcdproplus/fcdproplusgui.h index 317c0ab0b..9cbae0f75 100644 --- a/plugins/samplesource/fcdproplus/fcdproplusgui.h +++ b/plugins/samplesource/fcdproplus/fcdproplusgui.h @@ -48,7 +48,6 @@ public: private: Ui::FCDProPlusGui* ui; - DeviceUISet* m_deviceUISet; bool m_doApplySettings; bool m_forceSettings; FCDProPlusSettings m_settings; diff --git a/plugins/samplesource/fileinput/fileinputgui.cpp b/plugins/samplesource/fileinput/fileinputgui.cpp index e59153aeb..4e0d95d7e 100644 --- a/plugins/samplesource/fileinput/fileinputgui.cpp +++ b/plugins/samplesource/fileinput/fileinputgui.cpp @@ -41,7 +41,6 @@ FileInputGUI::FileInputGUI(DeviceUISet *deviceUISet, QWidget* parent) : DeviceGUI(parent), ui(new Ui::FileInputGUI), - m_deviceUISet(deviceUISet), m_settings(), m_doApplySettings(true), m_sampleSource(0), @@ -55,6 +54,7 @@ FileInputGUI::FileInputGUI(DeviceUISet *deviceUISet, QWidget* parent) : m_enableNavTime(false), m_lastEngineState(DeviceAPI::StNotStarted) { + m_deviceUISet = deviceUISet; setAttribute(Qt::WA_DeleteOnClose, true); ui->setupUi(getContents()); setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); diff --git a/plugins/samplesource/fileinput/fileinputgui.h b/plugins/samplesource/fileinput/fileinputgui.h index 43a7d49ac..78dc0b338 100644 --- a/plugins/samplesource/fileinput/fileinputgui.h +++ b/plugins/samplesource/fileinput/fileinputgui.h @@ -53,7 +53,6 @@ protected: private: Ui::FileInputGUI* ui; - DeviceUISet* m_deviceUISet; FileInputSettings m_settings; bool m_doApplySettings; QTimer m_statusTimer; diff --git a/plugins/samplesource/hackrfinput/hackrfinputgui.cpp b/plugins/samplesource/hackrfinput/hackrfinputgui.cpp index a692cbce5..9f0c04c44 100644 --- a/plugins/samplesource/hackrfinput/hackrfinputgui.cpp +++ b/plugins/samplesource/hackrfinput/hackrfinputgui.cpp @@ -38,7 +38,6 @@ HackRFInputGui::HackRFInputGui(DeviceUISet *deviceUISet, QWidget* parent) : DeviceGUI(parent), ui(new Ui::HackRFInputGui), - m_deviceUISet(deviceUISet), m_settings(), m_sampleRateMode(true), m_forceSettings(true), @@ -46,6 +45,7 @@ HackRFInputGui::HackRFInputGui(DeviceUISet *deviceUISet, QWidget* parent) : m_sampleSource(NULL), m_lastEngineState(DeviceAPI::StNotStarted) { + m_deviceUISet = deviceUISet; setAttribute(Qt::WA_DeleteOnClose, true); m_sampleSource = (HackRFInput*) m_deviceUISet->m_deviceAPI->getSampleSource(); diff --git a/plugins/samplesource/hackrfinput/hackrfinputgui.h b/plugins/samplesource/hackrfinput/hackrfinputgui.h index c9840a3b5..74e293a0f 100644 --- a/plugins/samplesource/hackrfinput/hackrfinputgui.h +++ b/plugins/samplesource/hackrfinput/hackrfinputgui.h @@ -61,7 +61,6 @@ protected: private: Ui::HackRFInputGui* ui; - DeviceUISet* m_deviceUISet; HackRFInputSettings m_settings; bool m_sampleRateMode; //!< true: device, false: base band sample rate update mode bool m_forceSettings; diff --git a/plugins/samplesource/kiwisdr/kiwisdrgui.cpp b/plugins/samplesource/kiwisdr/kiwisdrgui.cpp index 2ca20b58f..231e11c1b 100644 --- a/plugins/samplesource/kiwisdr/kiwisdrgui.cpp +++ b/plugins/samplesource/kiwisdr/kiwisdrgui.cpp @@ -43,7 +43,6 @@ KiwiSDRGui::KiwiSDRGui(DeviceUISet *deviceUISet, QWidget* parent) : DeviceGUI(parent), ui(new Ui::KiwiSDRGui), - m_deviceUISet(deviceUISet), m_settings(), m_doApplySettings(true), m_forceSettings(true), @@ -52,6 +51,7 @@ KiwiSDRGui::KiwiSDRGui(DeviceUISet *deviceUISet, QWidget* parent) : m_lastEngineState(DeviceAPI::StNotStarted) { qDebug("KiwiSDRGui::KiwiSDRGui"); + m_deviceUISet = deviceUISet; setAttribute(Qt::WA_DeleteOnClose, true); m_sampleSource = m_deviceUISet->m_deviceAPI->getSampleSource(); diff --git a/plugins/samplesource/kiwisdr/kiwisdrgui.h b/plugins/samplesource/kiwisdr/kiwisdrgui.h index 90ebe736a..385426c7c 100644 --- a/plugins/samplesource/kiwisdr/kiwisdrgui.h +++ b/plugins/samplesource/kiwisdr/kiwisdrgui.h @@ -53,7 +53,6 @@ protected: private: Ui::KiwiSDRGui* ui; - DeviceUISet* m_deviceUISet; KiwiSDRSettings m_settings; QTimer m_updateTimer; QTimer m_statusTimer; diff --git a/plugins/samplesource/limesdrinput/limesdrinputgui.cpp b/plugins/samplesource/limesdrinput/limesdrinputgui.cpp index 0a8c5ea4e..7d71754ef 100644 --- a/plugins/samplesource/limesdrinput/limesdrinputgui.cpp +++ b/plugins/samplesource/limesdrinput/limesdrinputgui.cpp @@ -36,7 +36,6 @@ LimeSDRInputGUI::LimeSDRInputGUI(DeviceUISet *deviceUISet, QWidget* parent) : DeviceGUI(parent), ui(new Ui::LimeSDRInputGUI), - m_deviceUISet(deviceUISet), m_settings(), m_sampleRateMode(true), m_sampleRate(0), @@ -46,6 +45,7 @@ LimeSDRInputGUI::LimeSDRInputGUI(DeviceUISet *deviceUISet, QWidget* parent) : m_statusCounter(0), m_deviceStatusCounter(0) { + m_deviceUISet = deviceUISet; setAttribute(Qt::WA_DeleteOnClose, true); m_limeSDRInput = (LimeSDRInput*) m_deviceUISet->m_deviceAPI->getSampleSource(); diff --git a/plugins/samplesource/limesdrinput/limesdrinputgui.h b/plugins/samplesource/limesdrinput/limesdrinputgui.h index eac7ce41e..401f334cb 100644 --- a/plugins/samplesource/limesdrinput/limesdrinputgui.h +++ b/plugins/samplesource/limesdrinput/limesdrinputgui.h @@ -51,7 +51,6 @@ protected: private: Ui::LimeSDRInputGUI* ui; - DeviceUISet* m_deviceUISet; LimeSDRInput* m_limeSDRInput; //!< Same object as above but gives easy access to LimeSDRInput methods and attributes that are used intensively LimeSDRInputSettings m_settings; bool m_sampleRateMode; //!< true: device, false: base band sample rate update mode diff --git a/plugins/samplesource/localinput/localinputgui.cpp b/plugins/samplesource/localinput/localinputgui.cpp index c7d087c6d..9eb721982 100644 --- a/plugins/samplesource/localinput/localinputgui.cpp +++ b/plugins/samplesource/localinput/localinputgui.cpp @@ -47,7 +47,6 @@ LocalInputGui::LocalInputGui(DeviceUISet *deviceUISet, QWidget* parent) : DeviceGUI(parent), ui(new Ui::LocalInputGui), - m_deviceUISet(deviceUISet), m_settings(), m_sampleSource(0), m_acquisition(false), @@ -70,6 +69,7 @@ LocalInputGui::LocalInputGui(DeviceUISet *deviceUISet, QWidget* parent) : m_doApplySettings(true), m_forceSettings(true) { + m_deviceUISet = deviceUISet; setAttribute(Qt::WA_DeleteOnClose, true); m_paletteGreenText.setColor(QPalette::WindowText, Qt::green); m_paletteWhiteText.setColor(QPalette::WindowText, Qt::white); diff --git a/plugins/samplesource/localinput/localinputgui.h b/plugins/samplesource/localinput/localinputgui.h index dfa8a25bd..11ef79cc8 100644 --- a/plugins/samplesource/localinput/localinputgui.h +++ b/plugins/samplesource/localinput/localinputgui.h @@ -53,7 +53,6 @@ protected: private: Ui::LocalInputGui* ui; - DeviceUISet* m_deviceUISet; LocalInputSettings m_settings; //!< current settings LocalInput* m_sampleSource; bool m_acquisition; diff --git a/plugins/samplesource/perseus/perseusgui.cpp b/plugins/samplesource/perseus/perseusgui.cpp index 917d12a76..d4d951ee8 100644 --- a/plugins/samplesource/perseus/perseusgui.cpp +++ b/plugins/samplesource/perseus/perseusgui.cpp @@ -34,13 +34,13 @@ PerseusGui::PerseusGui(DeviceUISet *deviceUISet, QWidget* parent) : DeviceGUI(parent), ui(new Ui::PerseusGui), - m_deviceUISet(deviceUISet), m_doApplySettings(true), m_forceSettings(true), m_settings(), m_sampleSource(0), m_lastEngineState(DeviceAPI::StNotStarted) { + m_deviceUISet = deviceUISet; setAttribute(Qt::WA_DeleteOnClose, true); m_sampleSource = (PerseusInput*) m_deviceUISet->m_deviceAPI->getSampleSource(); diff --git a/plugins/samplesource/perseus/perseusgui.h b/plugins/samplesource/perseus/perseusgui.h index dc3273258..4298b6992 100644 --- a/plugins/samplesource/perseus/perseusgui.h +++ b/plugins/samplesource/perseus/perseusgui.h @@ -54,7 +54,6 @@ protected: private: Ui::PerseusGui* ui; - DeviceUISet* m_deviceUISet; bool m_doApplySettings; bool m_forceSettings; PerseusSettings m_settings; diff --git a/plugins/samplesource/plutosdrinput/plutosdrinputgui.cpp b/plugins/samplesource/plutosdrinput/plutosdrinputgui.cpp index 01cc68dda..8ea5458c8 100644 --- a/plugins/samplesource/plutosdrinput/plutosdrinputgui.cpp +++ b/plugins/samplesource/plutosdrinput/plutosdrinputgui.cpp @@ -35,7 +35,6 @@ PlutoSDRInputGui::PlutoSDRInputGui(DeviceUISet *deviceUISet, QWidget* parent) : DeviceGUI(parent), ui(new Ui::PlutoSDRInputGUI), - m_deviceUISet(deviceUISet), m_settings(), m_sampleRateMode(true), m_forceSettings(true), @@ -46,6 +45,7 @@ PlutoSDRInputGui::PlutoSDRInputGui(DeviceUISet *deviceUISet, QWidget* parent) : m_doApplySettings(true), m_statusCounter(0) { + m_deviceUISet = deviceUISet; setAttribute(Qt::WA_DeleteOnClose, true); m_sampleSource = (PlutoSDRInput*) m_deviceUISet->m_deviceAPI->getSampleSource(); diff --git a/plugins/samplesource/plutosdrinput/plutosdrinputgui.h b/plugins/samplesource/plutosdrinput/plutosdrinputgui.h index 878b30db4..6a0841295 100644 --- a/plugins/samplesource/plutosdrinput/plutosdrinputgui.h +++ b/plugins/samplesource/plutosdrinput/plutosdrinputgui.h @@ -53,7 +53,6 @@ protected: private: Ui::PlutoSDRInputGUI* ui; - DeviceUISet* m_deviceUISet; PlutoSDRInputSettings m_settings; bool m_sampleRateMode; //!< true: device, false: base band sample rate update mode bool m_forceSettings; diff --git a/plugins/samplesource/remoteinput/remoteinputgui.cpp b/plugins/samplesource/remoteinput/remoteinputgui.cpp index 99022c146..289427346 100644 --- a/plugins/samplesource/remoteinput/remoteinputgui.cpp +++ b/plugins/samplesource/remoteinput/remoteinputgui.cpp @@ -44,7 +44,6 @@ RemoteInputGui::RemoteInputGui(DeviceUISet *deviceUISet, QWidget* parent) : DeviceGUI(parent), ui(new Ui::RemoteInputGui), - m_deviceUISet(deviceUISet), m_settings(), m_sampleSource(0), m_acquisition(false), @@ -67,6 +66,7 @@ RemoteInputGui::RemoteInputGui(DeviceUISet *deviceUISet, QWidget* parent) : m_doApplySettings(true), m_forceSettings(true) { + m_deviceUISet = deviceUISet; setAttribute(Qt::WA_DeleteOnClose, true); m_paletteGreenText.setColor(QPalette::WindowText, Qt::green); m_paletteWhiteText.setColor(QPalette::WindowText, Qt::white); diff --git a/plugins/samplesource/remoteinput/remoteinputgui.h b/plugins/samplesource/remoteinput/remoteinputgui.h index e9f8013c5..d6266ceda 100644 --- a/plugins/samplesource/remoteinput/remoteinputgui.h +++ b/plugins/samplesource/remoteinput/remoteinputgui.h @@ -55,7 +55,6 @@ protected: private: Ui::RemoteInputGui* ui; - DeviceUISet* m_deviceUISet; RemoteInputSettings m_settings; //!< current settings RemoteInput::RemoteChannelSettings m_remoteChannelSettings; double m_remoteShiftFrequencyFactor; //!< Remote channel frequency shift factor diff --git a/plugins/samplesource/rtlsdr/rtlsdrgui.cpp b/plugins/samplesource/rtlsdr/rtlsdrgui.cpp index 37b273e2b..5118993f9 100644 --- a/plugins/samplesource/rtlsdr/rtlsdrgui.cpp +++ b/plugins/samplesource/rtlsdr/rtlsdrgui.cpp @@ -35,7 +35,6 @@ RTLSDRGui::RTLSDRGui(DeviceUISet *deviceUISet, QWidget* parent) : DeviceGUI(parent), ui(new Ui::RTLSDRGui), - m_deviceUISet(deviceUISet), m_doApplySettings(true), m_forceSettings(true), m_settings(), @@ -43,6 +42,7 @@ RTLSDRGui::RTLSDRGui(DeviceUISet *deviceUISet, QWidget* parent) : m_sampleSource(0), m_lastEngineState(DeviceAPI::StNotStarted) { + m_deviceUISet = deviceUISet; setAttribute(Qt::WA_DeleteOnClose, true); m_sampleSource = (RTLSDRInput*) m_deviceUISet->m_deviceAPI->getSampleSource(); diff --git a/plugins/samplesource/rtlsdr/rtlsdrgui.h b/plugins/samplesource/rtlsdr/rtlsdrgui.h index 29cf1470c..09a26f3cd 100644 --- a/plugins/samplesource/rtlsdr/rtlsdrgui.h +++ b/plugins/samplesource/rtlsdr/rtlsdrgui.h @@ -53,7 +53,6 @@ protected: private: Ui::RTLSDRGui* ui; - DeviceUISet* m_deviceUISet; bool m_doApplySettings; bool m_forceSettings; RTLSDRSettings m_settings; diff --git a/plugins/samplesource/sdrplay/sdrplaygui.cpp b/plugins/samplesource/sdrplay/sdrplaygui.cpp index dd8ce5172..df6ec645a 100644 --- a/plugins/samplesource/sdrplay/sdrplaygui.cpp +++ b/plugins/samplesource/sdrplay/sdrplaygui.cpp @@ -36,10 +36,10 @@ SDRPlayGui::SDRPlayGui(DeviceUISet *deviceUISet, QWidget* parent) : DeviceGUI(parent), ui(new Ui::SDRPlayGui), - m_deviceUISet(deviceUISet), m_doApplySettings(true), m_forceSettings(true) { + m_deviceUISet = deviceUISet; setAttribute(Qt::WA_DeleteOnClose, true); m_sampleSource = (SDRPlayInput*) m_deviceUISet->m_deviceAPI->getSampleSource(); diff --git a/plugins/samplesource/sdrplay/sdrplaygui.h b/plugins/samplesource/sdrplay/sdrplaygui.h index 6e9196c22..0ffa73f46 100644 --- a/plugins/samplesource/sdrplay/sdrplaygui.h +++ b/plugins/samplesource/sdrplay/sdrplaygui.h @@ -53,7 +53,6 @@ protected: private: Ui::SDRPlayGui* ui; - DeviceUISet* m_deviceUISet; bool m_doApplySettings; bool m_forceSettings; SDRPlaySettings m_settings; diff --git a/plugins/samplesource/sdrplayv3/sdrplayv3gui.cpp b/plugins/samplesource/sdrplayv3/sdrplayv3gui.cpp index a66991a7b..c057ab815 100644 --- a/plugins/samplesource/sdrplayv3/sdrplayv3gui.cpp +++ b/plugins/samplesource/sdrplayv3/sdrplayv3gui.cpp @@ -36,10 +36,10 @@ SDRPlayV3Gui::SDRPlayV3Gui(DeviceUISet *deviceUISet, QWidget* parent) : DeviceGUI(parent), ui(new Ui::SDRPlayV3Gui), - m_deviceUISet(deviceUISet), m_doApplySettings(true), m_forceSettings(true) { + m_deviceUISet = deviceUISet; setAttribute(Qt::WA_DeleteOnClose, true); m_sdrPlayV3Input = (SDRPlayV3Input*) m_deviceUISet->m_deviceAPI->getSampleSource(); diff --git a/plugins/samplesource/sdrplayv3/sdrplayv3gui.h b/plugins/samplesource/sdrplayv3/sdrplayv3gui.h index 59fe97011..fc16fd029 100644 --- a/plugins/samplesource/sdrplayv3/sdrplayv3gui.h +++ b/plugins/samplesource/sdrplayv3/sdrplayv3gui.h @@ -53,7 +53,6 @@ protected: private: Ui::SDRPlayV3Gui* ui; - DeviceUISet* m_deviceUISet; bool m_doApplySettings; bool m_forceSettings; SDRPlayV3Settings m_settings; diff --git a/plugins/samplesource/sigmffileinput/sigmffileinputgui.cpp b/plugins/samplesource/sigmffileinput/sigmffileinputgui.cpp index 343585106..2970f6227 100644 --- a/plugins/samplesource/sigmffileinput/sigmffileinputgui.cpp +++ b/plugins/samplesource/sigmffileinput/sigmffileinputgui.cpp @@ -43,7 +43,6 @@ SigMFFileInputGUI::SigMFFileInputGUI(DeviceUISet *deviceUISet, QWidget* parent) : DeviceGUI(parent), ui(new Ui::SigMFFileInputGUI), - m_deviceUISet(deviceUISet), m_settings(), m_currentTrackIndex(0), m_doApplySettings(true), @@ -61,6 +60,7 @@ SigMFFileInputGUI::SigMFFileInputGUI(DeviceUISet *deviceUISet, QWidget* parent) m_enableFullNavTime(false), m_lastEngineState(DeviceAPI::StNotStarted) { + m_deviceUISet = deviceUISet; setAttribute(Qt::WA_DeleteOnClose, true); m_helpURL = "plugins/samplesource/sigmffileinput/readme.md"; QWidget *contents = getContents(); diff --git a/plugins/samplesource/sigmffileinput/sigmffileinputgui.h b/plugins/samplesource/sigmffileinput/sigmffileinputgui.h index 381ad5ea9..b2a72848e 100644 --- a/plugins/samplesource/sigmffileinput/sigmffileinputgui.h +++ b/plugins/samplesource/sigmffileinput/sigmffileinputgui.h @@ -50,7 +50,6 @@ public: private: Ui::SigMFFileInputGUI* ui; - DeviceUISet* m_deviceUISet; SigMFFileInputSettings m_settings; int m_currentTrackIndex; bool m_doApplySettings; diff --git a/plugins/samplesource/soapysdrinput/soapysdrinputgui.cpp b/plugins/samplesource/soapysdrinput/soapysdrinputgui.cpp index ecade848c..d8f074d48 100644 --- a/plugins/samplesource/soapysdrinput/soapysdrinputgui.cpp +++ b/plugins/samplesource/soapysdrinput/soapysdrinputgui.cpp @@ -42,7 +42,6 @@ SoapySDRInputGui::SoapySDRInputGui(DeviceUISet *deviceUISet, QWidget* parent) : DeviceGUI(parent), ui(new Ui::SoapySDRInputGui), - m_deviceUISet(deviceUISet), m_forceSettings(true), m_doApplySettings(true), m_sampleSource(0), @@ -59,6 +58,7 @@ SoapySDRInputGui::SoapySDRInputGui(DeviceUISet *deviceUISet, QWidget* parent) : m_autoDCCorrection(0), m_autoIQCorrection(0) { + m_deviceUISet = deviceUISet; setAttribute(Qt::WA_DeleteOnClose, true); m_sampleSource = (SoapySDRInput*) m_deviceUISet->m_deviceAPI->getSampleSource(); ui->setupUi(getContents()); diff --git a/plugins/samplesource/soapysdrinput/soapysdrinputgui.h b/plugins/samplesource/soapysdrinput/soapysdrinputgui.h index 0f896a921..b21db63b7 100644 --- a/plugins/samplesource/soapysdrinput/soapysdrinputgui.h +++ b/plugins/samplesource/soapysdrinput/soapysdrinputgui.h @@ -71,7 +71,6 @@ private: Ui::SoapySDRInputGui* ui; - DeviceUISet* m_deviceUISet; bool m_forceSettings; bool m_doApplySettings; SoapySDRInputSettings m_settings; diff --git a/plugins/samplesource/testsource/testsourcegui.cpp b/plugins/samplesource/testsource/testsourcegui.cpp index dda83fc7d..404546566 100644 --- a/plugins/samplesource/testsource/testsourcegui.cpp +++ b/plugins/samplesource/testsource/testsourcegui.cpp @@ -42,7 +42,6 @@ TestSourceGui::TestSourceGui(DeviceUISet *deviceUISet, QWidget* parent) : DeviceGUI(parent), ui(new Ui::TestSourceGui), - m_deviceUISet(deviceUISet), m_settings(), m_doApplySettings(true), m_forceSettings(true), @@ -51,6 +50,7 @@ TestSourceGui::TestSourceGui(DeviceUISet *deviceUISet, QWidget* parent) : m_lastEngineState(DeviceAPI::StNotStarted) { qDebug("TestSourceGui::TestSourceGui"); + m_deviceUISet = deviceUISet; setAttribute(Qt::WA_DeleteOnClose, true); m_sampleSource = m_deviceUISet->m_deviceAPI->getSampleSource(); diff --git a/plugins/samplesource/testsource/testsourcegui.h b/plugins/samplesource/testsource/testsourcegui.h index 01473a90b..ead209b5d 100644 --- a/plugins/samplesource/testsource/testsourcegui.h +++ b/plugins/samplesource/testsource/testsourcegui.h @@ -52,7 +52,6 @@ protected: private: Ui::TestSourceGui* ui; - DeviceUISet* m_deviceUISet; TestSourceSettings m_settings; QTimer m_updateTimer; QTimer m_statusTimer; diff --git a/plugins/samplesource/usrpinput/usrpinputgui.cpp b/plugins/samplesource/usrpinput/usrpinputgui.cpp index 5b7830843..39a085e8d 100644 --- a/plugins/samplesource/usrpinput/usrpinputgui.cpp +++ b/plugins/samplesource/usrpinput/usrpinputgui.cpp @@ -37,7 +37,6 @@ USRPInputGUI::USRPInputGUI(DeviceUISet *deviceUISet, QWidget* parent) : DeviceGUI(parent), ui(new Ui::USRPInputGUI), - m_deviceUISet(deviceUISet), m_settings(), m_sampleRateMode(true), m_sampleRate(0), @@ -47,6 +46,7 @@ USRPInputGUI::USRPInputGUI(DeviceUISet *deviceUISet, QWidget* parent) : m_statusCounter(0), m_deviceStatusCounter(0) { + m_deviceUISet = deviceUISet; setAttribute(Qt::WA_DeleteOnClose, true); m_usrpInput = (USRPInput*) m_deviceUISet->m_deviceAPI->getSampleSource(); diff --git a/plugins/samplesource/usrpinput/usrpinputgui.h b/plugins/samplesource/usrpinput/usrpinputgui.h index 9749ccd9c..84aa4e271 100644 --- a/plugins/samplesource/usrpinput/usrpinputgui.h +++ b/plugins/samplesource/usrpinput/usrpinputgui.h @@ -57,7 +57,6 @@ protected: private: Ui::USRPInputGUI* ui; - DeviceUISet* m_deviceUISet; USRPInput* m_usrpInput; //!< Same object as above but gives easy access to USRPInput methods and attributes that are used intensively USRPInputSettings m_settings; bool m_sampleRateMode; //!< true: device, false: base band sample rate update mode diff --git a/plugins/samplesource/xtrxinput/xtrxinputgui.cpp b/plugins/samplesource/xtrxinput/xtrxinputgui.cpp index 2a137e6db..35ad4668d 100644 --- a/plugins/samplesource/xtrxinput/xtrxinputgui.cpp +++ b/plugins/samplesource/xtrxinput/xtrxinputgui.cpp @@ -37,7 +37,6 @@ XTRXInputGUI::XTRXInputGUI(DeviceUISet *deviceUISet, QWidget* parent) : DeviceGUI(parent), ui(new Ui::XTRXInputGUI), - m_deviceUISet(deviceUISet), m_settings(), m_sampleRateMode(true), m_sampleRate(0), @@ -47,6 +46,7 @@ XTRXInputGUI::XTRXInputGUI(DeviceUISet *deviceUISet, QWidget* parent) : m_statusCounter(0), m_deviceStatusCounter(0) { + m_deviceUISet = deviceUISet; setAttribute(Qt::WA_DeleteOnClose, true); m_XTRXInput = (XTRXInput*) m_deviceUISet->m_deviceAPI->getSampleSource(); diff --git a/plugins/samplesource/xtrxinput/xtrxinputgui.h b/plugins/samplesource/xtrxinput/xtrxinputgui.h index 72862c5c6..5973d816c 100644 --- a/plugins/samplesource/xtrxinput/xtrxinputgui.h +++ b/plugins/samplesource/xtrxinput/xtrxinputgui.h @@ -52,7 +52,6 @@ protected: private: Ui::XTRXInputGUI* ui; - DeviceUISet* m_deviceUISet; XTRXInput* m_XTRXInput; //!< Same object as above but gives easy access to XTRXInput methods and attributes that are used intensively XTRXInputSettings m_settings; bool m_sampleRateMode; //!< true: device, false: base band sample rate update mode diff --git a/sdrbase/channel/channelapi.h b/sdrbase/channel/channelapi.h index b7168d1b8..8bdb4a9d0 100644 --- a/sdrbase/channel/channelapi.h +++ b/sdrbase/channel/channelapi.h @@ -37,6 +37,7 @@ namespace SWGSDRangel class SWGChannelSettings; class SWGChannelReport; class SWGChannelActions; + class SWGWorkspaceInfo; } class SDRBASE_API ChannelAPI : public QObject { @@ -121,6 +122,17 @@ public: errorMessage = "Not implemented"; return 501; } + /** + * API adapter for the channel UI workspace GET requests + */ + virtual int webapiWorkspaceGet( + SWGSDRangel::SWGWorkspaceInfo& query, + QString& errorMessage) + { + (void) query; + errorMessage = "Not implemented"; return 501; + } + int getIndexInDeviceSet() const { return m_indexInDeviceSet; } void setIndexInDeviceSet(int indexInDeviceSet) diff --git a/sdrbase/device/deviceapi.cpp b/sdrbase/device/deviceapi.cpp index 813d243d3..e6da31bae 100644 --- a/sdrbase/device/deviceapi.cpp +++ b/sdrbase/device/deviceapi.cpp @@ -44,6 +44,7 @@ DeviceAPI::DeviceAPI( m_pluginInterface(nullptr), m_masterTimer(DSPEngine::instance()->getMasterTimer()), m_samplingDeviceSequence(0), + m_workspaceIndex(0), m_buddySharedPtr(nullptr), m_isBuddyLeader(false), m_deviceSourceEngine(deviceSourceEngine), diff --git a/sdrbase/device/deviceapi.h b/sdrbase/device/deviceapi.h index 630ee0ee0..f2477b10b 100644 --- a/sdrbase/device/deviceapi.h +++ b/sdrbase/device/deviceapi.h @@ -117,6 +117,9 @@ public: uint32_t getSamplingDeviceSequence() const { return m_samplingDeviceSequence; } const QString& getHardwareUserArguments() const { return m_hardwareUserArguments; } + void setWorkspaceIndex(int index) { m_workspaceIndex = index; } + int getWorkspaceIndex() const { return m_workspaceIndex; } + void setDeviceNbItems(uint32_t nbItems); void setDeviceItemIndex(uint32_t index); uint32_t getDeviceNbItems() const { return m_deviceNbItems; } @@ -183,6 +186,7 @@ protected: QString m_samplingDeviceDisplayName; //!< The human readable name identifying this instance uint32_t m_samplingDeviceSequence; //!< The device sequence. >0 when more than one device of the same type is connected QString m_hardwareUserArguments; //!< User given arguments to be used at hardware level i.e. for the hardware device and device sequence + int m_workspaceIndex; //!< Used only by the GUI but accessible via web API // Buddies (single Rx or single Tx) diff --git a/sdrbase/dsp/spectrumvis.h b/sdrbase/dsp/spectrumvis.h index 9fab52eab..e214062a3 100644 --- a/sdrbase/dsp/spectrumvis.h +++ b/sdrbase/dsp/spectrumvis.h @@ -139,6 +139,8 @@ public: virtual ~SpectrumVis(); void setGLSpectrum(GLSpectrumInterface* glSpectrum) { m_glSpectrum = glSpectrum; } + void setWorkspaceIndex(int index) { m_workspaceIndex = index; } + int getWorkspaceIndex() const { return m_workspaceIndex; } void setScalef(Real scalef); void configureWSSpectrum(const QString& address, uint16_t port); @@ -211,6 +213,7 @@ private: FFTEngine* m_fft; FFTWindow m_window; unsigned int m_fftEngineSequence; + int m_workspaceIndex; std::vector m_fftBuffer; std::vector m_powerSpectrum; //!< displayable power spectrum diff --git a/sdrbase/feature/feature.h b/sdrbase/feature/feature.h index 7f04633cc..0529b3846 100644 --- a/sdrbase/feature/feature.h +++ b/sdrbase/feature/feature.h @@ -150,6 +150,8 @@ public: MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } //!< Get the queue for asynchronous inbound communication void setMessageQueueToGUI(MessageQueue *queue) { m_guiMessageQueue = queue; } MessageQueue *getMessageQueueToGUI() { return m_guiMessageQueue; } + void setWorkspaceIndex(int index) { m_workspaceIndex = index; } + int getWorkspaceIndex() const { return m_workspaceIndex; } protected: MessageQueue m_inputMessageQueue; @@ -169,6 +171,7 @@ private: QString m_uri; //!< Unique non modifiable identifier attached to channel type uint64_t m_uid; int m_indexInFeatureSet; + int m_workspaceIndex; signals: void indexInFeatureSetChanged(int index); diff --git a/sdrbase/maincore.cpp b/sdrbase/maincore.cpp index 47db34a50..242faa779 100644 --- a/sdrbase/maincore.cpp +++ b/sdrbase/maincore.cpp @@ -57,6 +57,10 @@ MESSAGE_CLASS_DEFINITION(MainCore::MsgChannelReport, Message) MESSAGE_CLASS_DEFINITION(MainCore::MsgChannelSettings, Message) MESSAGE_CLASS_DEFINITION(MainCore::MsgChannelDemodReport, Message) MESSAGE_CLASS_DEFINITION(MainCore::MsgChannelDemodQuery, Message) +MESSAGE_CLASS_DEFINITION(MainCore::MsgMoveDeviceUIToWorkspace, Message) +MESSAGE_CLASS_DEFINITION(MainCore::MsgMoveMainSpectrumUIToWorkspace, Message) +MESSAGE_CLASS_DEFINITION(MainCore::MsgMoveFeatureUIToWorkspace, Message) +MESSAGE_CLASS_DEFINITION(MainCore::MsgMoveChannelUIToWorkspace, Message) MESSAGE_CLASS_DEFINITION(MainCore::MsgMapItem, Message) MESSAGE_CLASS_DEFINITION(MainCore::MsgPacket, Message) MESSAGE_CLASS_DEFINITION(MainCore::MsgTargetAzimuthElevation, Message) diff --git a/sdrbase/maincore.h b/sdrbase/maincore.h index 0bf3324cc..18b29e7a0 100644 --- a/sdrbase/maincore.h +++ b/sdrbase/maincore.h @@ -640,6 +640,97 @@ public: { } }; + class SDRBASE_API MsgMoveDeviceUIToWorkspace : public Message { + MESSAGE_CLASS_DECLARATION + + public: + int getDeviceSetIndex() const { return m_deviceSetIndex; } + int getWorkspaceIndex() const { return m_workspaceIndex; } + + static MsgMoveDeviceUIToWorkspace* create(int deviceSetIndex, int workspceIndex) { + return new MsgMoveDeviceUIToWorkspace(deviceSetIndex, workspceIndex); + } + + private: + int m_deviceSetIndex; + int m_workspaceIndex; + + MsgMoveDeviceUIToWorkspace(int deviceSetIndex, int workspceIndex) : + Message(), + m_deviceSetIndex(deviceSetIndex), + m_workspaceIndex(workspceIndex) + { } + }; + + class SDRBASE_API MsgMoveMainSpectrumUIToWorkspace : public Message { + MESSAGE_CLASS_DECLARATION + + public: + int getDeviceSetIndex() const { return m_deviceSetIndex; } + int getWorkspaceIndex() const { return m_workspaceIndex; } + + static MsgMoveMainSpectrumUIToWorkspace* create(int deviceSetIndex, int workspceIndex) { + return new MsgMoveMainSpectrumUIToWorkspace(deviceSetIndex, workspceIndex); + } + + private: + int m_deviceSetIndex; + int m_workspaceIndex; + + MsgMoveMainSpectrumUIToWorkspace(int deviceSetIndex, int workspceIndex) : + Message(), + m_deviceSetIndex(deviceSetIndex), + m_workspaceIndex(workspceIndex) + { } + }; + + class SDRBASE_API MsgMoveFeatureUIToWorkspace : public Message { + MESSAGE_CLASS_DECLARATION + + public: + int getFeatureIndex() const { return m_featureIndex; } + int getWorkspaceIndex() const { return m_workspaceIndex; } + + static MsgMoveFeatureUIToWorkspace* create(int featureIndex, int workspceIndex) { + return new MsgMoveFeatureUIToWorkspace(featureIndex, workspceIndex); + } + + private: + int m_featureIndex; + int m_workspaceIndex; + + MsgMoveFeatureUIToWorkspace(int featureIndex, int workspceIndex) : + Message(), + m_featureIndex(featureIndex), + m_workspaceIndex(workspceIndex) + { } + }; + + class SDRBASE_API MsgMoveChannelUIToWorkspace : public Message { + MESSAGE_CLASS_DECLARATION + + public: + int getDeviceSetIndex() const { return m_deviceSetIndex; } + int getChannelIndex() const { return m_channelIndex; } + int getWorkspaceIndex() const { return m_workspaceIndex; } + + static MsgMoveChannelUIToWorkspace* create(int deviceSetIndex, int channelIndex, int workspceIndex) { + return new MsgMoveChannelUIToWorkspace(deviceSetIndex, channelIndex, workspceIndex); + } + + private: + int m_deviceSetIndex; + int m_channelIndex; + int m_workspaceIndex; + + MsgMoveChannelUIToWorkspace(int deviceSetIndex, int channelIndex, int workspceIndex) : + Message(), + m_deviceSetIndex(deviceSetIndex), + m_channelIndex(channelIndex), + m_workspaceIndex(workspceIndex) + { } + }; + // Message to Map feature to display an item on the map class SDRBASE_API MsgMapItem : public Message { MESSAGE_CLASS_DECLARATION diff --git a/sdrbase/resources/webapi/doc/html2/index.html b/sdrbase/resources/webapi/doc/html2/index.html index b685023c3..ef261f77e 100644 --- a/sdrbase/resources/webapi/doc/html2/index.html +++ b/sdrbase/resources/webapi/doc/html2/index.html @@ -14039,6 +14039,16 @@ margin-bottom: 20px; } }, "description" : "WFMMod" +}; + defs.WorkspaceInfo = { + "required" : [ "index" ], + "properties" : { + "index" : { + "type" : "integer", + "description" : "index of workspace in the list of workspaces" + } + }, + "description" : "Workspace information" }; defs.XtrxInputReport = { "properties" : { @@ -14491,6 +14501,12 @@ margin-bottom: 20px;
  • devicesetChannelSettingsPut
  • +
  • + devicesetChannelWorkspaceGet +
  • +
  • + devicesetChannelWorkspacePut +
  • devicesetChannelsReportGet
  • @@ -14530,6 +14546,12 @@ margin-bottom: 20px;
  • devicesetDeviceSubsystemRunPost
  • +
  • + devicesetDeviceWorkspaceGet +
  • +
  • + devicesetDeviceWorkspacePut +
  • devicesetFocusPatch
  • @@ -14554,6 +14576,12 @@ margin-bottom: 20px;
  • devicesetSpectrumSettingsPut
  • +
  • + devicesetSpectrumWorkspaceGet +
  • +
  • + devicesetSpectrumWorkspacePut +
  • instanceDeviceSetDelete
  • @@ -14591,6 +14619,12 @@ margin-bottom: 20px;
  • featuresetFeatureSettingsPatch
  • +
  • + featuresetFeatureWorkspaceGet +
  • +
  • + featuresetFeatureWorkspacePut +
  • featuresetGet
  • @@ -18427,6 +18461,960 @@ $(document).ready(function() {
    +
    +
    +
    +

    devicesetChannelWorkspaceGet

    +

    +
    +
    +
    +

    +

    get channel UI workspace information (GUI)

    +

    +
    +
    /sdrangel/deviceset/{deviceSetIndex}/channel/{channelIndex}/workspace
    +

    +

    Usage and SDK Samples

    +

    + + +
    +
    +
    curl -X GET "http://localhost/sdrangel/deviceset/{deviceSetIndex}/channel/{channelIndex}/workspace"
    +
    +
    +
    import SWGSDRangel.*;
    +import SWGSDRangel.auth.*;
    +import SWGSDRangel.model.*;
    +import SWGSDRangel.api.DeviceSetApi;
    +
    +import java.io.File;
    +import java.util.*;
    +
    +public class DeviceSetApiExample {
    +
    +    public static void main(String[] args) {
    +        
    +        DeviceSetApi apiInstance = new DeviceSetApi();
    +        Integer deviceSetIndex = 56; // Integer | Index of device set in the device set list
    +        Integer channelIndex = 56; // Integer | Index of the channel in the channels list for this device set
    +        try {
    +            WorkspaceInfo result = apiInstance.devicesetChannelWorkspaceGet(deviceSetIndex, channelIndex);
    +            System.out.println(result);
    +        } catch (ApiException e) {
    +            System.err.println("Exception when calling DeviceSetApi#devicesetChannelWorkspaceGet");
    +            e.printStackTrace();
    +        }
    +    }
    +}
    +
    + +
    +
    import SWGSDRangel.api.DeviceSetApi;
    +
    +public class DeviceSetApiExample {
    +
    +    public static void main(String[] args) {
    +        DeviceSetApi apiInstance = new DeviceSetApi();
    +        Integer deviceSetIndex = 56; // Integer | Index of device set in the device set list
    +        Integer channelIndex = 56; // Integer | Index of the channel in the channels list for this device set
    +        try {
    +            WorkspaceInfo result = apiInstance.devicesetChannelWorkspaceGet(deviceSetIndex, channelIndex);
    +            System.out.println(result);
    +        } catch (ApiException e) {
    +            System.err.println("Exception when calling DeviceSetApi#devicesetChannelWorkspaceGet");
    +            e.printStackTrace();
    +        }
    +    }
    +}
    +
    + +
    +
    Integer *deviceSetIndex = 56; // Index of device set in the device set list
    +Integer *channelIndex = 56; // Index of the channel in the channels list for this device set
    +
    +DeviceSetApi *apiInstance = [[DeviceSetApi alloc] init];
    +
    +[apiInstance devicesetChannelWorkspaceGetWith:deviceSetIndex
    +    channelIndex:channelIndex
    +              completionHandler: ^(WorkspaceInfo output, NSError* error) {
    +                            if (output) {
    +                                NSLog(@"%@", output);
    +                            }
    +                            if (error) {
    +                                NSLog(@"Error: %@", error);
    +                            }
    +                        }];
    +
    +
    + +
    +
    var SdRangel = require('sd_rangel');
    +
    +var api = new SdRangel.DeviceSetApi()
    +
    +var deviceSetIndex = 56; // {Integer} Index of device set in the device set list
    +
    +var channelIndex = 56; // {Integer} Index of the channel in the channels list for this device set
    +
    +
    +var callback = function(error, data, response) {
    +  if (error) {
    +    console.error(error);
    +  } else {
    +    console.log('API called successfully. Returned data: ' + data);
    +  }
    +};
    +api.devicesetChannelWorkspaceGet(deviceSetIndex, channelIndex, callback);
    +
    +
    + + +
    +
    using System;
    +using System.Diagnostics;
    +using SWGSDRangel.Api;
    +using SWGSDRangel.Client;
    +using SWGSDRangel.Model;
    +
    +namespace Example
    +{
    +    public class devicesetChannelWorkspaceGetExample
    +    {
    +        public void main()
    +        {
    +            
    +            var apiInstance = new DeviceSetApi();
    +            var deviceSetIndex = 56;  // Integer | Index of device set in the device set list
    +            var channelIndex = 56;  // Integer | Index of the channel in the channels list for this device set
    +
    +            try
    +            {
    +                WorkspaceInfo result = apiInstance.devicesetChannelWorkspaceGet(deviceSetIndex, channelIndex);
    +                Debug.WriteLine(result);
    +            }
    +            catch (Exception e)
    +            {
    +                Debug.Print("Exception when calling DeviceSetApi.devicesetChannelWorkspaceGet: " + e.Message );
    +            }
    +        }
    +    }
    +}
    +
    +
    + +
    +
    <?php
    +require_once(__DIR__ . '/vendor/autoload.php');
    +
    +$api_instance = new Swagger\Client\Api\DeviceSetApi();
    +$deviceSetIndex = 56; // Integer | Index of device set in the device set list
    +$channelIndex = 56; // Integer | Index of the channel in the channels list for this device set
    +
    +try {
    +    $result = $api_instance->devicesetChannelWorkspaceGet($deviceSetIndex, $channelIndex);
    +    print_r($result);
    +} catch (Exception $e) {
    +    echo 'Exception when calling DeviceSetApi->devicesetChannelWorkspaceGet: ', $e->getMessage(), PHP_EOL;
    +}
    +?>
    +
    + +
    +
    use Data::Dumper;
    +use SWGSDRangel::Configuration;
    +use SWGSDRangel::DeviceSetApi;
    +
    +my $api_instance = SWGSDRangel::DeviceSetApi->new();
    +my $deviceSetIndex = 56; # Integer | Index of device set in the device set list
    +my $channelIndex = 56; # Integer | Index of the channel in the channels list for this device set
    +
    +eval { 
    +    my $result = $api_instance->devicesetChannelWorkspaceGet(deviceSetIndex => $deviceSetIndex, channelIndex => $channelIndex);
    +    print Dumper($result);
    +};
    +if ($@) {
    +    warn "Exception when calling DeviceSetApi->devicesetChannelWorkspaceGet: $@\n";
    +}
    +
    + +
    +
    from __future__ import print_statement
    +import time
    +import swagger_sdrangel
    +from swagger_sdrangel.rest import ApiException
    +from pprint import pprint
    +
    +# create an instance of the API class
    +api_instance = swagger_sdrangel.DeviceSetApi()
    +deviceSetIndex = 56 # Integer | Index of device set in the device set list
    +channelIndex = 56 # Integer | Index of the channel in the channels list for this device set
    +
    +try: 
    +    api_response = api_instance.deviceset_channel_workspace_get(deviceSetIndex, channelIndex)
    +    pprint(api_response)
    +except ApiException as e:
    +    print("Exception when calling DeviceSetApi->devicesetChannelWorkspaceGet: %s\n" % e)
    +
    +
    + +

    Parameters

    + +
    Path parameters
    + + + + + + + + + + + + + +
    NameDescription
    deviceSetIndex* + + +
    +
    +
    + + Integer + + +
    + Index of device set in the device set list +
    +
    +
    + Required +
    +
    +
    +
    channelIndex* + + +
    +
    +
    + + Integer + + +
    + Index of the channel in the channels list for this device set +
    +
    +
    + Required +
    +
    +
    +
    + + + + + +

    Responses

    +

    Status: 200 - On success return workspace information

    + + + +
    +
    +
    + +
    + +
    +
    + +

    Status: 400 - Invalid device set index

    + + + +
    +
    +
    + +
    + +
    +
    + +

    Status: 500 - Error

    + + + +
    +
    +
    + +
    + +
    +
    + +

    Status: 501 - Function not implemented

    + + + +
    +
    +
    + +
    + +
    +
    + +
    +
    +
    +
    +
    +
    +

    devicesetChannelWorkspacePut

    +

    +
    +
    +
    +

    +

    move channel UI to workspace (GUI)

    +

    +
    +
    /sdrangel/deviceset/{deviceSetIndex}/channel/{channelIndex}/workspace
    +

    +

    Usage and SDK Samples

    +

    + + +
    +
    +
    curl -X PUT "http://localhost/sdrangel/deviceset/{deviceSetIndex}/channel/{channelIndex}/workspace"
    +
    +
    +
    import SWGSDRangel.*;
    +import SWGSDRangel.auth.*;
    +import SWGSDRangel.model.*;
    +import SWGSDRangel.api.DeviceSetApi;
    +
    +import java.io.File;
    +import java.util.*;
    +
    +public class DeviceSetApiExample {
    +
    +    public static void main(String[] args) {
    +        
    +        DeviceSetApi apiInstance = new DeviceSetApi();
    +        Integer deviceSetIndex = 56; // Integer | Index of device set in the device set list
    +        Integer channelIndex = 56; // Integer | Index of the channel in the channels list for this device set
    +        WorkspaceInfo body = ; // WorkspaceInfo | Destination workspace information
    +        try {
    +            SuccessResponse result = apiInstance.devicesetChannelWorkspacePut(deviceSetIndex, channelIndex, body);
    +            System.out.println(result);
    +        } catch (ApiException e) {
    +            System.err.println("Exception when calling DeviceSetApi#devicesetChannelWorkspacePut");
    +            e.printStackTrace();
    +        }
    +    }
    +}
    +
    + +
    +
    import SWGSDRangel.api.DeviceSetApi;
    +
    +public class DeviceSetApiExample {
    +
    +    public static void main(String[] args) {
    +        DeviceSetApi apiInstance = new DeviceSetApi();
    +        Integer deviceSetIndex = 56; // Integer | Index of device set in the device set list
    +        Integer channelIndex = 56; // Integer | Index of the channel in the channels list for this device set
    +        WorkspaceInfo body = ; // WorkspaceInfo | Destination workspace information
    +        try {
    +            SuccessResponse result = apiInstance.devicesetChannelWorkspacePut(deviceSetIndex, channelIndex, body);
    +            System.out.println(result);
    +        } catch (ApiException e) {
    +            System.err.println("Exception when calling DeviceSetApi#devicesetChannelWorkspacePut");
    +            e.printStackTrace();
    +        }
    +    }
    +}
    +
    + +
    +
    Integer *deviceSetIndex = 56; // Index of device set in the device set list
    +Integer *channelIndex = 56; // Index of the channel in the channels list for this device set
    +WorkspaceInfo *body = ; // Destination workspace information
    +
    +DeviceSetApi *apiInstance = [[DeviceSetApi alloc] init];
    +
    +[apiInstance devicesetChannelWorkspacePutWith:deviceSetIndex
    +    channelIndex:channelIndex
    +    body:body
    +              completionHandler: ^(SuccessResponse output, NSError* error) {
    +                            if (output) {
    +                                NSLog(@"%@", output);
    +                            }
    +                            if (error) {
    +                                NSLog(@"Error: %@", error);
    +                            }
    +                        }];
    +
    +
    + +
    +
    var SdRangel = require('sd_rangel');
    +
    +var api = new SdRangel.DeviceSetApi()
    +
    +var deviceSetIndex = 56; // {Integer} Index of device set in the device set list
    +
    +var channelIndex = 56; // {Integer} Index of the channel in the channels list for this device set
    +
    +var body = ; // {WorkspaceInfo} Destination workspace information
    +
    +
    +var callback = function(error, data, response) {
    +  if (error) {
    +    console.error(error);
    +  } else {
    +    console.log('API called successfully. Returned data: ' + data);
    +  }
    +};
    +api.devicesetChannelWorkspacePut(deviceSetIndex, channelIndex, body, callback);
    +
    +
    + + +
    +
    using System;
    +using System.Diagnostics;
    +using SWGSDRangel.Api;
    +using SWGSDRangel.Client;
    +using SWGSDRangel.Model;
    +
    +namespace Example
    +{
    +    public class devicesetChannelWorkspacePutExample
    +    {
    +        public void main()
    +        {
    +            
    +            var apiInstance = new DeviceSetApi();
    +            var deviceSetIndex = 56;  // Integer | Index of device set in the device set list
    +            var channelIndex = 56;  // Integer | Index of the channel in the channels list for this device set
    +            var body = new WorkspaceInfo(); // WorkspaceInfo | Destination workspace information
    +
    +            try
    +            {
    +                SuccessResponse result = apiInstance.devicesetChannelWorkspacePut(deviceSetIndex, channelIndex, body);
    +                Debug.WriteLine(result);
    +            }
    +            catch (Exception e)
    +            {
    +                Debug.Print("Exception when calling DeviceSetApi.devicesetChannelWorkspacePut: " + e.Message );
    +            }
    +        }
    +    }
    +}
    +
    +
    + +
    +
    <?php
    +require_once(__DIR__ . '/vendor/autoload.php');
    +
    +$api_instance = new Swagger\Client\Api\DeviceSetApi();
    +$deviceSetIndex = 56; // Integer | Index of device set in the device set list
    +$channelIndex = 56; // Integer | Index of the channel in the channels list for this device set
    +$body = ; // WorkspaceInfo | Destination workspace information
    +
    +try {
    +    $result = $api_instance->devicesetChannelWorkspacePut($deviceSetIndex, $channelIndex, $body);
    +    print_r($result);
    +} catch (Exception $e) {
    +    echo 'Exception when calling DeviceSetApi->devicesetChannelWorkspacePut: ', $e->getMessage(), PHP_EOL;
    +}
    +?>
    +
    + +
    +
    use Data::Dumper;
    +use SWGSDRangel::Configuration;
    +use SWGSDRangel::DeviceSetApi;
    +
    +my $api_instance = SWGSDRangel::DeviceSetApi->new();
    +my $deviceSetIndex = 56; # Integer | Index of device set in the device set list
    +my $channelIndex = 56; # Integer | Index of the channel in the channels list for this device set
    +my $body = SWGSDRangel::Object::WorkspaceInfo->new(); # WorkspaceInfo | Destination workspace information
    +
    +eval { 
    +    my $result = $api_instance->devicesetChannelWorkspacePut(deviceSetIndex => $deviceSetIndex, channelIndex => $channelIndex, body => $body);
    +    print Dumper($result);
    +};
    +if ($@) {
    +    warn "Exception when calling DeviceSetApi->devicesetChannelWorkspacePut: $@\n";
    +}
    +
    + +
    +
    from __future__ import print_statement
    +import time
    +import swagger_sdrangel
    +from swagger_sdrangel.rest import ApiException
    +from pprint import pprint
    +
    +# create an instance of the API class
    +api_instance = swagger_sdrangel.DeviceSetApi()
    +deviceSetIndex = 56 # Integer | Index of device set in the device set list
    +channelIndex = 56 # Integer | Index of the channel in the channels list for this device set
    +body =  # WorkspaceInfo | Destination workspace information
    +
    +try: 
    +    api_response = api_instance.deviceset_channel_workspace_put(deviceSetIndex, channelIndex, body)
    +    pprint(api_response)
    +except ApiException as e:
    +    print("Exception when calling DeviceSetApi->devicesetChannelWorkspacePut: %s\n" % e)
    +
    +
    + +

    Parameters

    + +
    Path parameters
    + + + + + + + + + + + + + +
    NameDescription
    deviceSetIndex* + + +
    +
    +
    + + Integer + + +
    + Index of device set in the device set list +
    +
    +
    + Required +
    +
    +
    +
    channelIndex* + + +
    +
    +
    + + Integer + + +
    + Index of the channel in the channels list for this device set +
    +
    +
    + Required +
    +
    +
    +
    + + +
    Body parameters
    + + + + + + + + + +
    NameDescription
    body * + + + +
    +
    + + + +

    Responses

    +

    Status: 202 - Message to perform action was sent successfully

    + + + +
    +
    +
    + +
    + +
    +
    + +

    Status: 400 - Invalid device set or workspace index

    + + + +
    +
    +
    + +
    + +
    +
    + +

    Status: 500 - Error

    + + + +
    +
    +
    + +
    + +
    +
    + +

    Status: 501 - Function not implemented

    + + + +
    +
    +
    + +
    + +
    +
    + +
    +
    +
    @@ -19696,7 +20684,7 @@ $(document).ready(function() {

    Responses

    -

    Status: 202 - On successful semdomg of the message it returns the details of the device being set

    +

    Status: 202 - On successful semding of the message it returns the details of the device being set

    + +
    + + +

    Status: 400 - Invalid device set index

    + + + +
    +
    +
    + +
    + +
    +
    + +

    Status: 500 - Error

    + + + +
    +
    +
    + +
    + +
    +
    + +

    Status: 501 - Function not implemented

    + + + +
    +
    +
    + +
    + +
    +
    + + + +
    +
    +
    +
    +

    devicesetDeviceWorkspacePut

    +

    +
    +
    +
    +

    +

    move device UI to workspace (GUI)

    +

    +
    +
    /sdrangel/deviceset/{deviceSetIndex}/device/workspace
    +

    +

    Usage and SDK Samples

    +

    + + +
    +
    +
    curl -X PUT "http://localhost/sdrangel/deviceset/{deviceSetIndex}/device/workspace"
    +
    +
    +
    import SWGSDRangel.*;
    +import SWGSDRangel.auth.*;
    +import SWGSDRangel.model.*;
    +import SWGSDRangel.api.DeviceSetApi;
    +
    +import java.io.File;
    +import java.util.*;
    +
    +public class DeviceSetApiExample {
    +
    +    public static void main(String[] args) {
    +        
    +        DeviceSetApi apiInstance = new DeviceSetApi();
    +        Integer deviceSetIndex = 56; // Integer | Index of device set in the device set list
    +        WorkspaceInfo body = ; // WorkspaceInfo | Destination workspace information
    +        try {
    +            SuccessResponse result = apiInstance.devicesetDeviceWorkspacePut(deviceSetIndex, body);
    +            System.out.println(result);
    +        } catch (ApiException e) {
    +            System.err.println("Exception when calling DeviceSetApi#devicesetDeviceWorkspacePut");
    +            e.printStackTrace();
    +        }
    +    }
    +}
    +
    + +
    +
    import SWGSDRangel.api.DeviceSetApi;
    +
    +public class DeviceSetApiExample {
    +
    +    public static void main(String[] args) {
    +        DeviceSetApi apiInstance = new DeviceSetApi();
    +        Integer deviceSetIndex = 56; // Integer | Index of device set in the device set list
    +        WorkspaceInfo body = ; // WorkspaceInfo | Destination workspace information
    +        try {
    +            SuccessResponse result = apiInstance.devicesetDeviceWorkspacePut(deviceSetIndex, body);
    +            System.out.println(result);
    +        } catch (ApiException e) {
    +            System.err.println("Exception when calling DeviceSetApi#devicesetDeviceWorkspacePut");
    +            e.printStackTrace();
    +        }
    +    }
    +}
    +
    + +
    +
    Integer *deviceSetIndex = 56; // Index of device set in the device set list
    +WorkspaceInfo *body = ; // Destination workspace information
    +
    +DeviceSetApi *apiInstance = [[DeviceSetApi alloc] init];
    +
    +[apiInstance devicesetDeviceWorkspacePutWith:deviceSetIndex
    +    body:body
    +              completionHandler: ^(SuccessResponse output, NSError* error) {
    +                            if (output) {
    +                                NSLog(@"%@", output);
    +                            }
    +                            if (error) {
    +                                NSLog(@"Error: %@", error);
    +                            }
    +                        }];
    +
    +
    + +
    +
    var SdRangel = require('sd_rangel');
    +
    +var api = new SdRangel.DeviceSetApi()
    +
    +var deviceSetIndex = 56; // {Integer} Index of device set in the device set list
    +
    +var body = ; // {WorkspaceInfo} Destination workspace information
    +
    +
    +var callback = function(error, data, response) {
    +  if (error) {
    +    console.error(error);
    +  } else {
    +    console.log('API called successfully. Returned data: ' + data);
    +  }
    +};
    +api.devicesetDeviceWorkspacePut(deviceSetIndex, body, callback);
    +
    +
    + + +
    +
    using System;
    +using System.Diagnostics;
    +using SWGSDRangel.Api;
    +using SWGSDRangel.Client;
    +using SWGSDRangel.Model;
    +
    +namespace Example
    +{
    +    public class devicesetDeviceWorkspacePutExample
    +    {
    +        public void main()
    +        {
    +            
    +            var apiInstance = new DeviceSetApi();
    +            var deviceSetIndex = 56;  // Integer | Index of device set in the device set list
    +            var body = new WorkspaceInfo(); // WorkspaceInfo | Destination workspace information
    +
    +            try
    +            {
    +                SuccessResponse result = apiInstance.devicesetDeviceWorkspacePut(deviceSetIndex, body);
    +                Debug.WriteLine(result);
    +            }
    +            catch (Exception e)
    +            {
    +                Debug.Print("Exception when calling DeviceSetApi.devicesetDeviceWorkspacePut: " + e.Message );
    +            }
    +        }
    +    }
    +}
    +
    +
    + +
    +
    <?php
    +require_once(__DIR__ . '/vendor/autoload.php');
    +
    +$api_instance = new Swagger\Client\Api\DeviceSetApi();
    +$deviceSetIndex = 56; // Integer | Index of device set in the device set list
    +$body = ; // WorkspaceInfo | Destination workspace information
    +
    +try {
    +    $result = $api_instance->devicesetDeviceWorkspacePut($deviceSetIndex, $body);
    +    print_r($result);
    +} catch (Exception $e) {
    +    echo 'Exception when calling DeviceSetApi->devicesetDeviceWorkspacePut: ', $e->getMessage(), PHP_EOL;
    +}
    +?>
    +
    + +
    +
    use Data::Dumper;
    +use SWGSDRangel::Configuration;
    +use SWGSDRangel::DeviceSetApi;
    +
    +my $api_instance = SWGSDRangel::DeviceSetApi->new();
    +my $deviceSetIndex = 56; # Integer | Index of device set in the device set list
    +my $body = SWGSDRangel::Object::WorkspaceInfo->new(); # WorkspaceInfo | Destination workspace information
    +
    +eval { 
    +    my $result = $api_instance->devicesetDeviceWorkspacePut(deviceSetIndex => $deviceSetIndex, body => $body);
    +    print Dumper($result);
    +};
    +if ($@) {
    +    warn "Exception when calling DeviceSetApi->devicesetDeviceWorkspacePut: $@\n";
    +}
    +
    + +
    +
    from __future__ import print_statement
    +import time
    +import swagger_sdrangel
    +from swagger_sdrangel.rest import ApiException
    +from pprint import pprint
    +
    +# create an instance of the API class
    +api_instance = swagger_sdrangel.DeviceSetApi()
    +deviceSetIndex = 56 # Integer | Index of device set in the device set list
    +body =  # WorkspaceInfo | Destination workspace information
    +
    +try: 
    +    api_response = api_instance.deviceset_device_workspace_put(deviceSetIndex, body)
    +    pprint(api_response)
    +except ApiException as e:
    +    print("Exception when calling DeviceSetApi->devicesetDeviceWorkspacePut: %s\n" % e)
    +
    +
    + +

    Parameters

    + +
    Path parameters
    + + + + + + + + + +
    NameDescription
    deviceSetIndex* + + +
    +
    +
    + + Integer + + +
    + Index of device set in the device set list +
    +
    +
    + Required +
    +
    +
    +
    + + +
    Body parameters
    + + + + + + + + + +
    NameDescription
    body * + + + +
    +
    + + + +

    Responses

    +

    Status: 202 - Message to perform action was sent successfully

    + + + +
    +
    +
    + +
    + +
    +
    + +

    Status: 400 - Invalid device set or workspace index

    + + + +
    +
    +
    + +
    + +
    +
    + +

    Status: 500 - Error

    + + + +
    +
    +
    + +
    + +
    +
    + +

    Status: 501 - Function not implemented

    + + + +
    +
    +
    + +
    + +
    +
    + +
    +
    +
    @@ -28387,6 +30263,894 @@ $(document).ready(function() {

    +
    +
    +
    +

    devicesetSpectrumWorkspaceGet

    +

    +
    +
    +
    +

    +

    Get main spectrum UI workspace information (GUI)

    +

    +
    +
    /sdrangel/deviceset/{deviceSetIndex}/spectrum/workspace
    +

    +

    Usage and SDK Samples

    +

    + + +
    +
    +
    curl -X GET "http://localhost/sdrangel/deviceset/{deviceSetIndex}/spectrum/workspace"
    +
    +
    +
    import SWGSDRangel.*;
    +import SWGSDRangel.auth.*;
    +import SWGSDRangel.model.*;
    +import SWGSDRangel.api.DeviceSetApi;
    +
    +import java.io.File;
    +import java.util.*;
    +
    +public class DeviceSetApiExample {
    +
    +    public static void main(String[] args) {
    +        
    +        DeviceSetApi apiInstance = new DeviceSetApi();
    +        Integer deviceSetIndex = 56; // Integer | Index of device set in the device set list
    +        try {
    +            WorkspaceInfo result = apiInstance.devicesetSpectrumWorkspaceGet(deviceSetIndex);
    +            System.out.println(result);
    +        } catch (ApiException e) {
    +            System.err.println("Exception when calling DeviceSetApi#devicesetSpectrumWorkspaceGet");
    +            e.printStackTrace();
    +        }
    +    }
    +}
    +
    + +
    +
    import SWGSDRangel.api.DeviceSetApi;
    +
    +public class DeviceSetApiExample {
    +
    +    public static void main(String[] args) {
    +        DeviceSetApi apiInstance = new DeviceSetApi();
    +        Integer deviceSetIndex = 56; // Integer | Index of device set in the device set list
    +        try {
    +            WorkspaceInfo result = apiInstance.devicesetSpectrumWorkspaceGet(deviceSetIndex);
    +            System.out.println(result);
    +        } catch (ApiException e) {
    +            System.err.println("Exception when calling DeviceSetApi#devicesetSpectrumWorkspaceGet");
    +            e.printStackTrace();
    +        }
    +    }
    +}
    +
    + +
    +
    Integer *deviceSetIndex = 56; // Index of device set in the device set list
    +
    +DeviceSetApi *apiInstance = [[DeviceSetApi alloc] init];
    +
    +[apiInstance devicesetSpectrumWorkspaceGetWith:deviceSetIndex
    +              completionHandler: ^(WorkspaceInfo output, NSError* error) {
    +                            if (output) {
    +                                NSLog(@"%@", output);
    +                            }
    +                            if (error) {
    +                                NSLog(@"Error: %@", error);
    +                            }
    +                        }];
    +
    +
    + +
    +
    var SdRangel = require('sd_rangel');
    +
    +var api = new SdRangel.DeviceSetApi()
    +
    +var deviceSetIndex = 56; // {Integer} Index of device set in the device set list
    +
    +
    +var callback = function(error, data, response) {
    +  if (error) {
    +    console.error(error);
    +  } else {
    +    console.log('API called successfully. Returned data: ' + data);
    +  }
    +};
    +api.devicesetSpectrumWorkspaceGet(deviceSetIndex, callback);
    +
    +
    + + +
    +
    using System;
    +using System.Diagnostics;
    +using SWGSDRangel.Api;
    +using SWGSDRangel.Client;
    +using SWGSDRangel.Model;
    +
    +namespace Example
    +{
    +    public class devicesetSpectrumWorkspaceGetExample
    +    {
    +        public void main()
    +        {
    +            
    +            var apiInstance = new DeviceSetApi();
    +            var deviceSetIndex = 56;  // Integer | Index of device set in the device set list
    +
    +            try
    +            {
    +                WorkspaceInfo result = apiInstance.devicesetSpectrumWorkspaceGet(deviceSetIndex);
    +                Debug.WriteLine(result);
    +            }
    +            catch (Exception e)
    +            {
    +                Debug.Print("Exception when calling DeviceSetApi.devicesetSpectrumWorkspaceGet: " + e.Message );
    +            }
    +        }
    +    }
    +}
    +
    +
    + +
    +
    <?php
    +require_once(__DIR__ . '/vendor/autoload.php');
    +
    +$api_instance = new Swagger\Client\Api\DeviceSetApi();
    +$deviceSetIndex = 56; // Integer | Index of device set in the device set list
    +
    +try {
    +    $result = $api_instance->devicesetSpectrumWorkspaceGet($deviceSetIndex);
    +    print_r($result);
    +} catch (Exception $e) {
    +    echo 'Exception when calling DeviceSetApi->devicesetSpectrumWorkspaceGet: ', $e->getMessage(), PHP_EOL;
    +}
    +?>
    +
    + +
    +
    use Data::Dumper;
    +use SWGSDRangel::Configuration;
    +use SWGSDRangel::DeviceSetApi;
    +
    +my $api_instance = SWGSDRangel::DeviceSetApi->new();
    +my $deviceSetIndex = 56; # Integer | Index of device set in the device set list
    +
    +eval { 
    +    my $result = $api_instance->devicesetSpectrumWorkspaceGet(deviceSetIndex => $deviceSetIndex);
    +    print Dumper($result);
    +};
    +if ($@) {
    +    warn "Exception when calling DeviceSetApi->devicesetSpectrumWorkspaceGet: $@\n";
    +}
    +
    + +
    +
    from __future__ import print_statement
    +import time
    +import swagger_sdrangel
    +from swagger_sdrangel.rest import ApiException
    +from pprint import pprint
    +
    +# create an instance of the API class
    +api_instance = swagger_sdrangel.DeviceSetApi()
    +deviceSetIndex = 56 # Integer | Index of device set in the device set list
    +
    +try: 
    +    api_response = api_instance.deviceset_spectrum_workspace_get(deviceSetIndex)
    +    pprint(api_response)
    +except ApiException as e:
    +    print("Exception when calling DeviceSetApi->devicesetSpectrumWorkspaceGet: %s\n" % e)
    +
    +
    + +

    Parameters

    + +
    Path parameters
    + + + + + + + + + +
    NameDescription
    deviceSetIndex* + + +
    +
    +
    + + Integer + + +
    + Index of device set in the device set list +
    +
    +
    + Required +
    +
    +
    +
    + + + + + +

    Responses

    +

    Status: 200 - On success return main spectrum UI workspace index

    + + + +
    +
    +
    + +
    + +
    +
    + +

    Status: 404 - Invalid index

    + + + +
    +
    +
    + +
    + +
    +
    + +

    Status: 500 - Error

    + + + +
    +
    +
    + +
    + +
    +
    + +

    Status: 501 - Function not implemented

    + + + +
    +
    +
    + +
    + +
    +
    + +
    +
    +
    +
    +
    +
    +

    devicesetSpectrumWorkspacePut

    +

    +
    +
    +
    +

    +

    move main spectrum UI to workspace (GUI)

    +

    +
    +
    /sdrangel/deviceset/{deviceSetIndex}/spectrum/workspace
    +

    +

    Usage and SDK Samples

    +

    + + +
    +
    +
    curl -X PUT "http://localhost/sdrangel/deviceset/{deviceSetIndex}/spectrum/workspace"
    +
    +
    +
    import SWGSDRangel.*;
    +import SWGSDRangel.auth.*;
    +import SWGSDRangel.model.*;
    +import SWGSDRangel.api.DeviceSetApi;
    +
    +import java.io.File;
    +import java.util.*;
    +
    +public class DeviceSetApiExample {
    +
    +    public static void main(String[] args) {
    +        
    +        DeviceSetApi apiInstance = new DeviceSetApi();
    +        Integer deviceSetIndex = 56; // Integer | Index of device set in the device set list
    +        WorkspaceInfo body = ; // WorkspaceInfo | Destination workspace information
    +        try {
    +            SuccessResponse result = apiInstance.devicesetSpectrumWorkspacePut(deviceSetIndex, body);
    +            System.out.println(result);
    +        } catch (ApiException e) {
    +            System.err.println("Exception when calling DeviceSetApi#devicesetSpectrumWorkspacePut");
    +            e.printStackTrace();
    +        }
    +    }
    +}
    +
    + +
    +
    import SWGSDRangel.api.DeviceSetApi;
    +
    +public class DeviceSetApiExample {
    +
    +    public static void main(String[] args) {
    +        DeviceSetApi apiInstance = new DeviceSetApi();
    +        Integer deviceSetIndex = 56; // Integer | Index of device set in the device set list
    +        WorkspaceInfo body = ; // WorkspaceInfo | Destination workspace information
    +        try {
    +            SuccessResponse result = apiInstance.devicesetSpectrumWorkspacePut(deviceSetIndex, body);
    +            System.out.println(result);
    +        } catch (ApiException e) {
    +            System.err.println("Exception when calling DeviceSetApi#devicesetSpectrumWorkspacePut");
    +            e.printStackTrace();
    +        }
    +    }
    +}
    +
    + +
    +
    Integer *deviceSetIndex = 56; // Index of device set in the device set list
    +WorkspaceInfo *body = ; // Destination workspace information
    +
    +DeviceSetApi *apiInstance = [[DeviceSetApi alloc] init];
    +
    +[apiInstance devicesetSpectrumWorkspacePutWith:deviceSetIndex
    +    body:body
    +              completionHandler: ^(SuccessResponse output, NSError* error) {
    +                            if (output) {
    +                                NSLog(@"%@", output);
    +                            }
    +                            if (error) {
    +                                NSLog(@"Error: %@", error);
    +                            }
    +                        }];
    +
    +
    + +
    +
    var SdRangel = require('sd_rangel');
    +
    +var api = new SdRangel.DeviceSetApi()
    +
    +var deviceSetIndex = 56; // {Integer} Index of device set in the device set list
    +
    +var body = ; // {WorkspaceInfo} Destination workspace information
    +
    +
    +var callback = function(error, data, response) {
    +  if (error) {
    +    console.error(error);
    +  } else {
    +    console.log('API called successfully. Returned data: ' + data);
    +  }
    +};
    +api.devicesetSpectrumWorkspacePut(deviceSetIndex, body, callback);
    +
    +
    + + +
    +
    using System;
    +using System.Diagnostics;
    +using SWGSDRangel.Api;
    +using SWGSDRangel.Client;
    +using SWGSDRangel.Model;
    +
    +namespace Example
    +{
    +    public class devicesetSpectrumWorkspacePutExample
    +    {
    +        public void main()
    +        {
    +            
    +            var apiInstance = new DeviceSetApi();
    +            var deviceSetIndex = 56;  // Integer | Index of device set in the device set list
    +            var body = new WorkspaceInfo(); // WorkspaceInfo | Destination workspace information
    +
    +            try
    +            {
    +                SuccessResponse result = apiInstance.devicesetSpectrumWorkspacePut(deviceSetIndex, body);
    +                Debug.WriteLine(result);
    +            }
    +            catch (Exception e)
    +            {
    +                Debug.Print("Exception when calling DeviceSetApi.devicesetSpectrumWorkspacePut: " + e.Message );
    +            }
    +        }
    +    }
    +}
    +
    +
    + +
    +
    <?php
    +require_once(__DIR__ . '/vendor/autoload.php');
    +
    +$api_instance = new Swagger\Client\Api\DeviceSetApi();
    +$deviceSetIndex = 56; // Integer | Index of device set in the device set list
    +$body = ; // WorkspaceInfo | Destination workspace information
    +
    +try {
    +    $result = $api_instance->devicesetSpectrumWorkspacePut($deviceSetIndex, $body);
    +    print_r($result);
    +} catch (Exception $e) {
    +    echo 'Exception when calling DeviceSetApi->devicesetSpectrumWorkspacePut: ', $e->getMessage(), PHP_EOL;
    +}
    +?>
    +
    + +
    +
    use Data::Dumper;
    +use SWGSDRangel::Configuration;
    +use SWGSDRangel::DeviceSetApi;
    +
    +my $api_instance = SWGSDRangel::DeviceSetApi->new();
    +my $deviceSetIndex = 56; # Integer | Index of device set in the device set list
    +my $body = SWGSDRangel::Object::WorkspaceInfo->new(); # WorkspaceInfo | Destination workspace information
    +
    +eval { 
    +    my $result = $api_instance->devicesetSpectrumWorkspacePut(deviceSetIndex => $deviceSetIndex, body => $body);
    +    print Dumper($result);
    +};
    +if ($@) {
    +    warn "Exception when calling DeviceSetApi->devicesetSpectrumWorkspacePut: $@\n";
    +}
    +
    + +
    +
    from __future__ import print_statement
    +import time
    +import swagger_sdrangel
    +from swagger_sdrangel.rest import ApiException
    +from pprint import pprint
    +
    +# create an instance of the API class
    +api_instance = swagger_sdrangel.DeviceSetApi()
    +deviceSetIndex = 56 # Integer | Index of device set in the device set list
    +body =  # WorkspaceInfo | Destination workspace information
    +
    +try: 
    +    api_response = api_instance.deviceset_spectrum_workspace_put(deviceSetIndex, body)
    +    pprint(api_response)
    +except ApiException as e:
    +    print("Exception when calling DeviceSetApi->devicesetSpectrumWorkspacePut: %s\n" % e)
    +
    +
    + +

    Parameters

    + +
    Path parameters
    + + + + + + + + + +
    NameDescription
    deviceSetIndex* + + +
    +
    +
    + + Integer + + +
    + Index of device set in the device set list +
    +
    +
    + Required +
    +
    +
    +
    + + +
    Body parameters
    + + + + + + + + + +
    NameDescription
    body * + + + +
    +
    + + + +

    Responses

    +

    Status: 202 - Message to perform action was sent successfully

    + + + +
    +
    +
    + +
    + +
    +
    + +

    Status: 400 - Invalid device set or workspace index

    + + + +
    +
    +
    + +
    + +
    +
    + +

    Status: 500 - Error

    + + + +
    +
    +
    + +
    + +
    +
    + +

    Status: 501 - Function not implemented

    + + + +
    +
    +
    + +
    + +
    +
    + +
    +
    +
    @@ -33790,6 +36554,894 @@ $(document).ready(function() {

    +
    +
    +
    +

    featuresetFeatureWorkspaceGet

    +

    +
    +
    +
    +

    +

    get feature UI workspace information (GUI)

    +

    +
    +
    /sdrangel/featureset/feature/{featureIndex}/workspace
    +

    +

    Usage and SDK Samples

    +

    + + +
    +
    +
    curl -X GET "http://localhost/sdrangel/featureset/feature/{featureIndex}/workspace"
    +
    +
    +
    import SWGSDRangel.*;
    +import SWGSDRangel.auth.*;
    +import SWGSDRangel.model.*;
    +import SWGSDRangel.api.FeatureSetApi;
    +
    +import java.io.File;
    +import java.util.*;
    +
    +public class FeatureSetApiExample {
    +
    +    public static void main(String[] args) {
    +        
    +        FeatureSetApi apiInstance = new FeatureSetApi();
    +        Integer featureIndex = 56; // Integer | Index of feature in the feature set
    +        try {
    +            WorkspaceInfo result = apiInstance.featuresetFeatureWorkspaceGet(featureIndex);
    +            System.out.println(result);
    +        } catch (ApiException e) {
    +            System.err.println("Exception when calling FeatureSetApi#featuresetFeatureWorkspaceGet");
    +            e.printStackTrace();
    +        }
    +    }
    +}
    +
    + +
    +
    import SWGSDRangel.api.FeatureSetApi;
    +
    +public class FeatureSetApiExample {
    +
    +    public static void main(String[] args) {
    +        FeatureSetApi apiInstance = new FeatureSetApi();
    +        Integer featureIndex = 56; // Integer | Index of feature in the feature set
    +        try {
    +            WorkspaceInfo result = apiInstance.featuresetFeatureWorkspaceGet(featureIndex);
    +            System.out.println(result);
    +        } catch (ApiException e) {
    +            System.err.println("Exception when calling FeatureSetApi#featuresetFeatureWorkspaceGet");
    +            e.printStackTrace();
    +        }
    +    }
    +}
    +
    + +
    +
    Integer *featureIndex = 56; // Index of feature in the feature set
    +
    +FeatureSetApi *apiInstance = [[FeatureSetApi alloc] init];
    +
    +[apiInstance featuresetFeatureWorkspaceGetWith:featureIndex
    +              completionHandler: ^(WorkspaceInfo output, NSError* error) {
    +                            if (output) {
    +                                NSLog(@"%@", output);
    +                            }
    +                            if (error) {
    +                                NSLog(@"Error: %@", error);
    +                            }
    +                        }];
    +
    +
    + +
    +
    var SdRangel = require('sd_rangel');
    +
    +var api = new SdRangel.FeatureSetApi()
    +
    +var featureIndex = 56; // {Integer} Index of feature in the feature set
    +
    +
    +var callback = function(error, data, response) {
    +  if (error) {
    +    console.error(error);
    +  } else {
    +    console.log('API called successfully. Returned data: ' + data);
    +  }
    +};
    +api.featuresetFeatureWorkspaceGet(featureIndex, callback);
    +
    +
    + + +
    +
    using System;
    +using System.Diagnostics;
    +using SWGSDRangel.Api;
    +using SWGSDRangel.Client;
    +using SWGSDRangel.Model;
    +
    +namespace Example
    +{
    +    public class featuresetFeatureWorkspaceGetExample
    +    {
    +        public void main()
    +        {
    +            
    +            var apiInstance = new FeatureSetApi();
    +            var featureIndex = 56;  // Integer | Index of feature in the feature set
    +
    +            try
    +            {
    +                WorkspaceInfo result = apiInstance.featuresetFeatureWorkspaceGet(featureIndex);
    +                Debug.WriteLine(result);
    +            }
    +            catch (Exception e)
    +            {
    +                Debug.Print("Exception when calling FeatureSetApi.featuresetFeatureWorkspaceGet: " + e.Message );
    +            }
    +        }
    +    }
    +}
    +
    +
    + +
    +
    <?php
    +require_once(__DIR__ . '/vendor/autoload.php');
    +
    +$api_instance = new Swagger\Client\Api\FeatureSetApi();
    +$featureIndex = 56; // Integer | Index of feature in the feature set
    +
    +try {
    +    $result = $api_instance->featuresetFeatureWorkspaceGet($featureIndex);
    +    print_r($result);
    +} catch (Exception $e) {
    +    echo 'Exception when calling FeatureSetApi->featuresetFeatureWorkspaceGet: ', $e->getMessage(), PHP_EOL;
    +}
    +?>
    +
    + +
    +
    use Data::Dumper;
    +use SWGSDRangel::Configuration;
    +use SWGSDRangel::FeatureSetApi;
    +
    +my $api_instance = SWGSDRangel::FeatureSetApi->new();
    +my $featureIndex = 56; # Integer | Index of feature in the feature set
    +
    +eval { 
    +    my $result = $api_instance->featuresetFeatureWorkspaceGet(featureIndex => $featureIndex);
    +    print Dumper($result);
    +};
    +if ($@) {
    +    warn "Exception when calling FeatureSetApi->featuresetFeatureWorkspaceGet: $@\n";
    +}
    +
    + +
    +
    from __future__ import print_statement
    +import time
    +import swagger_sdrangel
    +from swagger_sdrangel.rest import ApiException
    +from pprint import pprint
    +
    +# create an instance of the API class
    +api_instance = swagger_sdrangel.FeatureSetApi()
    +featureIndex = 56 # Integer | Index of feature in the feature set
    +
    +try: 
    +    api_response = api_instance.featureset_feature_workspace_get(featureIndex)
    +    pprint(api_response)
    +except ApiException as e:
    +    print("Exception when calling FeatureSetApi->featuresetFeatureWorkspaceGet: %s\n" % e)
    +
    +
    + +

    Parameters

    + +
    Path parameters
    + + + + + + + + + +
    NameDescription
    featureIndex* + + +
    +
    +
    + + Integer + + +
    + Index of feature in the feature set +
    +
    +
    + Required +
    +
    +
    +
    + + + + + +

    Responses

    +

    Status: 200 - On success return workspace information

    + + + +
    +
    +
    + +
    + +
    +
    + +

    Status: 400 - Invalid feature set index

    + + + +
    +
    +
    + +
    + +
    +
    + +

    Status: 500 - Error

    + + + +
    +
    +
    + +
    + +
    +
    + +

    Status: 501 - Function not implemented

    + + + +
    +
    +
    + +
    + +
    +
    + +
    +
    +
    +
    +
    +
    +

    featuresetFeatureWorkspacePut

    +

    +
    +
    +
    +

    +

    move feature UI to workspace (GUI)

    +

    +
    +
    /sdrangel/featureset/feature/{featureIndex}/workspace
    +

    +

    Usage and SDK Samples

    +

    + + +
    +
    +
    curl -X PUT "http://localhost/sdrangel/featureset/feature/{featureIndex}/workspace"
    +
    +
    +
    import SWGSDRangel.*;
    +import SWGSDRangel.auth.*;
    +import SWGSDRangel.model.*;
    +import SWGSDRangel.api.FeatureSetApi;
    +
    +import java.io.File;
    +import java.util.*;
    +
    +public class FeatureSetApiExample {
    +
    +    public static void main(String[] args) {
    +        
    +        FeatureSetApi apiInstance = new FeatureSetApi();
    +        Integer featureIndex = 56; // Integer | Index of feature in the feature set
    +        WorkspaceInfo body = ; // WorkspaceInfo | Destination workspace information
    +        try {
    +            SuccessResponse result = apiInstance.featuresetFeatureWorkspacePut(featureIndex, body);
    +            System.out.println(result);
    +        } catch (ApiException e) {
    +            System.err.println("Exception when calling FeatureSetApi#featuresetFeatureWorkspacePut");
    +            e.printStackTrace();
    +        }
    +    }
    +}
    +
    + +
    +
    import SWGSDRangel.api.FeatureSetApi;
    +
    +public class FeatureSetApiExample {
    +
    +    public static void main(String[] args) {
    +        FeatureSetApi apiInstance = new FeatureSetApi();
    +        Integer featureIndex = 56; // Integer | Index of feature in the feature set
    +        WorkspaceInfo body = ; // WorkspaceInfo | Destination workspace information
    +        try {
    +            SuccessResponse result = apiInstance.featuresetFeatureWorkspacePut(featureIndex, body);
    +            System.out.println(result);
    +        } catch (ApiException e) {
    +            System.err.println("Exception when calling FeatureSetApi#featuresetFeatureWorkspacePut");
    +            e.printStackTrace();
    +        }
    +    }
    +}
    +
    + +
    +
    Integer *featureIndex = 56; // Index of feature in the feature set
    +WorkspaceInfo *body = ; // Destination workspace information
    +
    +FeatureSetApi *apiInstance = [[FeatureSetApi alloc] init];
    +
    +[apiInstance featuresetFeatureWorkspacePutWith:featureIndex
    +    body:body
    +              completionHandler: ^(SuccessResponse output, NSError* error) {
    +                            if (output) {
    +                                NSLog(@"%@", output);
    +                            }
    +                            if (error) {
    +                                NSLog(@"Error: %@", error);
    +                            }
    +                        }];
    +
    +
    + +
    +
    var SdRangel = require('sd_rangel');
    +
    +var api = new SdRangel.FeatureSetApi()
    +
    +var featureIndex = 56; // {Integer} Index of feature in the feature set
    +
    +var body = ; // {WorkspaceInfo} Destination workspace information
    +
    +
    +var callback = function(error, data, response) {
    +  if (error) {
    +    console.error(error);
    +  } else {
    +    console.log('API called successfully. Returned data: ' + data);
    +  }
    +};
    +api.featuresetFeatureWorkspacePut(featureIndex, body, callback);
    +
    +
    + + +
    +
    using System;
    +using System.Diagnostics;
    +using SWGSDRangel.Api;
    +using SWGSDRangel.Client;
    +using SWGSDRangel.Model;
    +
    +namespace Example
    +{
    +    public class featuresetFeatureWorkspacePutExample
    +    {
    +        public void main()
    +        {
    +            
    +            var apiInstance = new FeatureSetApi();
    +            var featureIndex = 56;  // Integer | Index of feature in the feature set
    +            var body = new WorkspaceInfo(); // WorkspaceInfo | Destination workspace information
    +
    +            try
    +            {
    +                SuccessResponse result = apiInstance.featuresetFeatureWorkspacePut(featureIndex, body);
    +                Debug.WriteLine(result);
    +            }
    +            catch (Exception e)
    +            {
    +                Debug.Print("Exception when calling FeatureSetApi.featuresetFeatureWorkspacePut: " + e.Message );
    +            }
    +        }
    +    }
    +}
    +
    +
    + +
    +
    <?php
    +require_once(__DIR__ . '/vendor/autoload.php');
    +
    +$api_instance = new Swagger\Client\Api\FeatureSetApi();
    +$featureIndex = 56; // Integer | Index of feature in the feature set
    +$body = ; // WorkspaceInfo | Destination workspace information
    +
    +try {
    +    $result = $api_instance->featuresetFeatureWorkspacePut($featureIndex, $body);
    +    print_r($result);
    +} catch (Exception $e) {
    +    echo 'Exception when calling FeatureSetApi->featuresetFeatureWorkspacePut: ', $e->getMessage(), PHP_EOL;
    +}
    +?>
    +
    + +
    +
    use Data::Dumper;
    +use SWGSDRangel::Configuration;
    +use SWGSDRangel::FeatureSetApi;
    +
    +my $api_instance = SWGSDRangel::FeatureSetApi->new();
    +my $featureIndex = 56; # Integer | Index of feature in the feature set
    +my $body = SWGSDRangel::Object::WorkspaceInfo->new(); # WorkspaceInfo | Destination workspace information
    +
    +eval { 
    +    my $result = $api_instance->featuresetFeatureWorkspacePut(featureIndex => $featureIndex, body => $body);
    +    print Dumper($result);
    +};
    +if ($@) {
    +    warn "Exception when calling FeatureSetApi->featuresetFeatureWorkspacePut: $@\n";
    +}
    +
    + +
    +
    from __future__ import print_statement
    +import time
    +import swagger_sdrangel
    +from swagger_sdrangel.rest import ApiException
    +from pprint import pprint
    +
    +# create an instance of the API class
    +api_instance = swagger_sdrangel.FeatureSetApi()
    +featureIndex = 56 # Integer | Index of feature in the feature set
    +body =  # WorkspaceInfo | Destination workspace information
    +
    +try: 
    +    api_response = api_instance.featureset_feature_workspace_put(featureIndex, body)
    +    pprint(api_response)
    +except ApiException as e:
    +    print("Exception when calling FeatureSetApi->featuresetFeatureWorkspacePut: %s\n" % e)
    +
    +
    + +

    Parameters

    + +
    Path parameters
    + + + + + + + + + +
    NameDescription
    featureIndex* + + +
    +
    +
    + + Integer + + +
    + Index of feature in the feature set +
    +
    +
    + Required +
    +
    +
    +
    + + +
    Body parameters
    + + + + + + + + + +
    NameDescription
    body * + + + +
    +
    + + + +

    Responses

    +

    Status: 202 - Message to perform action was sent successfully

    + + + +
    +
    +
    + +
    + +
    +
    + +

    Status: 400 - Invalid device set or workspace index

    + + + +
    +
    +
    + +
    + +
    +
    + +

    Status: 500 - Error

    + + + +
    +
    +
    + +
    + +
    +
    + +

    Status: 501 - Function not implemented

    + + + +
    +
    +
    + +
    + +
    +
    + +
    +
    +
    @@ -56503,7 +60155,7 @@ except ApiException as e:
    - Generated 2022-05-09T01:10:06.823+02:00 + Generated 2022-05-13T21:51:44.838+02:00
    diff --git a/sdrbase/resources/webapi/doc/swagger/swagger.yaml b/sdrbase/resources/webapi/doc/swagger/swagger.yaml index d6b6fda7e..1a659acfd 100644 --- a/sdrbase/resources/webapi/doc/swagger/swagger.yaml +++ b/sdrbase/resources/webapi/doc/swagger/swagger.yaml @@ -1584,6 +1584,63 @@ paths: "501": $ref: "#/responses/Response_501" + /sdrangel/deviceset/{deviceSetIndex}/spectrum/workspace: + x-swagger-router-controller: deviceset + get: + description: Get main spectrum UI workspace information (GUI) + operationId: devicesetSpectrumWorkspaceGet + tags: + - DeviceSet + parameters: + - in: path + name: deviceSetIndex + type: integer + required: true + description: Index of device set in the device set list + responses: + "200": + description: On success return main spectrum UI workspace index + schema: + $ref: "#/definitions/WorkspaceInfo" + "404": + description: Invalid index + schema: + $ref: "#/definitions/ErrorResponse" + "500": + $ref: "#/responses/Response_500" + "501": + $ref: "#/responses/Response_501" + put: + description: move main spectrum UI to workspace (GUI) + operationId: devicesetSpectrumWorkspacePut + tags: + - DeviceSet + parameters: + - in: path + name: deviceSetIndex + type: integer + required: true + description: Index of device set in the device set list + - name: body + in: body + description: Destination workspace information + required: true + schema: + $ref: "#/definitions/WorkspaceInfo" + responses: + "202": + description: Message to perform action was sent successfully + schema: + $ref: "#/definitions/SuccessResponse" + "400": + description: Invalid device set or workspace index + schema: + $ref: "#/definitions/ErrorResponse" + "500": + $ref: "#/responses/Response_500" + "501": + $ref: "#/responses/Response_501" + /sdrangel/deviceset/{deviceSetIndex}/device: x-swagger-router-controller: deviceset put: @@ -1605,7 +1662,7 @@ paths: $ref: "#/definitions/DeviceListItem" responses: "202": - description: On successful semdomg of the message it returns the details of the device being set + description: On successful semding of the message it returns the details of the device being set schema: $ref: "#/definitions/DeviceListItem" "400": @@ -2021,6 +2078,63 @@ paths: "501": $ref: "#/responses/Response_501" + /sdrangel/deviceset/{deviceSetIndex}/device/workspace: + x-swagger-router-controller: deviceset + get: + description: get device UI workspace information (GUI) + operationId: devicesetDeviceWorkspaceGet + tags: + - DeviceSet + parameters: + - in: path + name: deviceSetIndex + type: integer + required: true + description: Index of device set in the device set list + responses: + "200": + description: On success return workspace information + schema: + $ref: "#/definitions/WorkspaceInfo" + "400": + description: Invalid device set index + schema: + $ref: "#/definitions/ErrorResponse" + "500": + $ref: "#/responses/Response_500" + "501": + $ref: "#/responses/Response_501" + put: + description: move device UI to workspace (GUI) + operationId: devicesetDeviceWorkspacePut + tags: + - DeviceSet + parameters: + - in: path + name: deviceSetIndex + type: integer + required: true + description: Index of device set in the device set list + - name: body + in: body + description: Destination workspace information + required: true + schema: + $ref: "#/definitions/WorkspaceInfo" + responses: + "202": + description: Message to perform action was sent successfully + schema: + $ref: "#/definitions/SuccessResponse" + "400": + description: Invalid device set or workspace index + schema: + $ref: "#/definitions/ErrorResponse" + "500": + $ref: "#/responses/Response_500" + "501": + $ref: "#/responses/Response_501" + /sdrangel/deviceset/{deviceSetIndex}/channel: x-swagger-router-controller: deviceset post: @@ -2286,6 +2400,73 @@ paths: "501": $ref: "#/responses/Response_501" + /sdrangel/deviceset/{deviceSetIndex}/channel/{channelIndex}/workspace: + x-swagger-router-controller: deviceset + get: + description: get channel UI workspace information (GUI) + operationId: devicesetChannelWorkspaceGet + tags: + - DeviceSet + parameters: + - in: path + name: deviceSetIndex + type: integer + required: true + description: Index of device set in the device set list + - in: path + name: channelIndex + type: integer + required: true + description: Index of the channel in the channels list for this device set + responses: + "200": + description: On success return workspace information + schema: + $ref: "#/definitions/WorkspaceInfo" + "400": + description: Invalid device set index + schema: + $ref: "#/definitions/ErrorResponse" + "500": + $ref: "#/responses/Response_500" + "501": + $ref: "#/responses/Response_501" + put: + description: move channel UI to workspace (GUI) + operationId: devicesetChannelWorkspacePut + tags: + - DeviceSet + parameters: + - in: path + name: deviceSetIndex + type: integer + required: true + description: Index of device set in the device set list + - in: path + name: channelIndex + type: integer + required: true + description: Index of the channel in the channels list for this device set + - name: body + in: body + description: Destination workspace information + required: true + schema: + $ref: "#/definitions/WorkspaceInfo" + responses: + "202": + description: Message to perform action was sent successfully + schema: + $ref: "#/definitions/SuccessResponse" + "400": + description: Invalid device set or workspace index + schema: + $ref: "#/definitions/ErrorResponse" + "500": + $ref: "#/responses/Response_500" + "501": + $ref: "#/responses/Response_501" + /sdrangel/featureset: x-swagger-router-controller: featureset get: @@ -2708,6 +2889,63 @@ paths: "501": $ref: "#/responses/Response_501" + /sdrangel/featureset/feature/{featureIndex}/workspace: + x-swagger-router-controller: featureset + get: + description: get feature UI workspace information (GUI) + operationId: featuresetFeatureWorkspaceGet + tags: + - FeatureSet + parameters: + - in: path + name: featureIndex + type: integer + required: true + description: Index of feature in the feature set + responses: + "200": + description: On success return workspace information + schema: + $ref: "#/definitions/WorkspaceInfo" + "400": + description: Invalid feature set index + schema: + $ref: "#/definitions/ErrorResponse" + "500": + $ref: "#/responses/Response_500" + "501": + $ref: "#/responses/Response_501" + put: + description: move feature UI to workspace (GUI) + operationId: featuresetFeatureWorkspacePut + tags: + - FeatureSet + parameters: + - in: path + name: featureIndex + type: integer + required: true + description: Index of feature in the feature set + - name: body + in: body + description: Destination workspace information + required: true + schema: + $ref: "#/definitions/WorkspaceInfo" + responses: + "202": + description: Message to perform action was sent successfully + schema: + $ref: "#/definitions/SuccessResponse" + "400": + description: Invalid device set or workspace index + schema: + $ref: "#/definitions/ErrorResponse" + "500": + $ref: "#/responses/Response_500" + "501": + $ref: "#/responses/Response_501" + /swagger: x-swagger-pipe: swagger_raw @@ -3116,6 +3354,14 @@ definitions: items: $ref: "#/definitions/Channel" + WorkspaceInfo: + description: "Workspace information" + required: + - index + properties: + index: + description: index of workspace in the list of workspaces + type: integer AudioDevices: description: "List of audio devices available in the system" diff --git a/sdrbase/webapi/webapiadapter.cpp b/sdrbase/webapi/webapiadapter.cpp index 7b5e84aba..0e2b859ef 100644 --- a/sdrbase/webapi/webapiadapter.cpp +++ b/sdrbase/webapi/webapiadapter.cpp @@ -36,6 +36,7 @@ #include "dsp/dspdevicesinkengine.h" #include "dsp/dspdevicemimoengine.h" #include "dsp/dspengine.h" +#include "dsp/spectrumvis.h" #include "plugin/pluginapi.h" #include "plugin/pluginmanager.h" #include "channel/channelapi.h" @@ -68,6 +69,7 @@ #include "SWGDeviceState.h" #include "SWGDeviceReport.h" #include "SWGDeviceActions.h" +#include "SWGWorkspaceInfo.h" #include "SWGChannelsDetail.h" #include "SWGChannelSettings.h" #include "SWGChannelReport.h" @@ -2254,6 +2256,48 @@ int WebAPIAdapter::devicesetSpectrumServerDelete( } } +int WebAPIAdapter::devicesetSpectrumWorkspaceGet( + int deviceSetIndex, + SWGSDRangel::SWGWorkspaceInfo& response, + SWGSDRangel::SWGErrorResponse& error) +{ + if ((deviceSetIndex >= 0) && (deviceSetIndex < (int) m_mainCore->m_deviceSets.size())) + { + const DeviceSet *deviceSet = m_mainCore->m_deviceSets[deviceSetIndex]; + response.setIndex(deviceSet->m_spectrumVis->getWorkspaceIndex()); + return 200; + } + else + { + error.init(); + *error.getMessage() = QString("There is no device set with index %1").arg(deviceSetIndex); + return 404; + } +} + +int WebAPIAdapter::devicesetSpectrumWorkspacePut( + int deviceSetIndex, + SWGSDRangel::SWGWorkspaceInfo& query, + SWGSDRangel::SWGSuccessResponse& response, + SWGSDRangel::SWGErrorResponse& error) +{ + if ((deviceSetIndex >= 0) && (deviceSetIndex < (int) m_mainCore->m_deviceSets.size())) + { + int workspaceIndex = query.getIndex(); + MainCore::MsgMoveMainSpectrumUIToWorkspace *msg = MainCore::MsgMoveMainSpectrumUIToWorkspace::create(deviceSetIndex, workspaceIndex); + m_mainCore->m_mainMessageQueue->push(msg); + response.init(); + *response.getMessage() = QString("Message to move a main spectrum to workspace (MsgMoveMainSpectrumUIToWorkspace) was submitted successfully"); + return 202; + } + else + { + error.init(); + *error.getMessage() = QString("There is no device set with index %1").arg(deviceSetIndex); + return 404; + } +} + int WebAPIAdapter::devicesetDevicePut( int deviceSetIndex, SWGSDRangel::SWGDeviceListItem& query, @@ -2510,6 +2554,47 @@ int WebAPIAdapter::devicesetDeviceActionsPost( } } +int WebAPIAdapter::devicesetDeviceWorkspaceGet( + int deviceSetIndex, + SWGSDRangel::SWGWorkspaceInfo& response, + SWGSDRangel::SWGErrorResponse& error) +{ + if ((deviceSetIndex >= 0) && (deviceSetIndex < (int) m_mainCore->m_deviceSets.size())) + { + response.setIndex(m_mainCore->m_deviceSets[deviceSetIndex]->m_deviceAPI->getWorkspaceIndex()); + return 200; + } + else + { + error.init(); + *error.getMessage() = QString("There is no device set with index %1").arg(deviceSetIndex); + return 404; + } +} + +int WebAPIAdapter::devicesetDeviceWorkspacePut( + int deviceSetIndex, + SWGSDRangel::SWGWorkspaceInfo& query, + SWGSDRangel::SWGSuccessResponse& response, + SWGSDRangel::SWGErrorResponse& error) +{ + if ((deviceSetIndex >= 0) && (deviceSetIndex < (int) m_mainCore->m_deviceSets.size())) + { + int workspaceIndex = query.getIndex(); + MainCore::MsgMoveDeviceUIToWorkspace *msg = MainCore::MsgMoveDeviceUIToWorkspace::create(deviceSetIndex, workspaceIndex); + m_mainCore->m_mainMessageQueue->push(msg); + response.init(); + *response.getMessage() = QString("Message to move a device UI to workspace (MsgMoveDeviceUIToWorkspace) was submitted successfully"); + return 202; + } + else + { + error.init(); + *error.getMessage() = QString("There is no device set with index %1").arg(deviceSetIndex); + return 404; + } +} + int WebAPIAdapter::devicesetDeviceSettingsPutPatch( int deviceSetIndex, bool force, @@ -3380,6 +3465,129 @@ int WebAPIAdapter::devicesetChannelActionsPost( } } +int WebAPIAdapter::devicesetChannelWorkspaceGet( + int deviceSetIndex, + int channelIndex, + SWGSDRangel::SWGWorkspaceInfo& response, + SWGSDRangel::SWGErrorResponse& error) +{ + error.init(); + + if ((deviceSetIndex >= 0) && (deviceSetIndex < (int) m_mainCore->m_deviceSets.size())) + { + DeviceSet *deviceSet = m_mainCore->m_deviceSets[deviceSetIndex]; + + if (deviceSet->m_deviceSourceEngine) // Single Rx + { + ChannelAPI *channelAPI = deviceSet->m_deviceAPI->getChanelSinkAPIAt(channelIndex); + + if (channelAPI == nullptr) + { + *error.getMessage() = QString("There is no channel with index %1").arg(channelIndex); + return 404; + } + else + { + return channelAPI->webapiWorkspaceGet(response, *error.getMessage()); + } + } + else if (deviceSet->m_deviceSinkEngine) // Single Tx + { + ChannelAPI *channelAPI = deviceSet->m_deviceAPI->getChanelSourceAPIAt(channelIndex); + + if (channelAPI == 0) + { + *error.getMessage() = QString("There is no channel with index %1").arg(channelIndex); + return 404; + } + else + { + return channelAPI->webapiWorkspaceGet(response, *error.getMessage()); + } + } + else if (deviceSet->m_deviceMIMOEngine) // MIMO + { + int nbSinkChannels = deviceSet->m_deviceAPI->getNbSinkChannels(); + int nbSourceChannels = deviceSet->m_deviceAPI->getNbSourceChannels(); + int nbMIMOChannels = deviceSet->m_deviceAPI->getNbMIMOChannels(); + ChannelAPI *channelAPI = nullptr; + + if (channelIndex < nbSinkChannels) + { + channelAPI = deviceSet->m_deviceAPI->getChanelSinkAPIAt(channelIndex); + } + else if (channelIndex < nbSinkChannels + nbSourceChannels) + { + channelAPI = deviceSet->m_deviceAPI->getChanelSourceAPIAt(channelIndex - nbSinkChannels); + } + else if (channelIndex < nbSinkChannels + nbSourceChannels + nbMIMOChannels) + { + channelAPI = deviceSet->m_deviceAPI->getMIMOChannelAPIAt(channelIndex - nbSinkChannels - nbSourceChannels); + } + else + { + *error.getMessage() = QString("There is no channel with index %1").arg(channelIndex); + return 404; + } + + if (channelAPI) + { + return channelAPI->webapiWorkspaceGet(response, *error.getMessage()); + } + else + { + *error.getMessage() = QString("There is no channel with index %1").arg(channelIndex); + return 404; + } + } + else + { + *error.getMessage() = QString("DeviceSet error"); + return 500; + } + } + else + { + *error.getMessage() = QString("There is no device set with index %1").arg(deviceSetIndex); + return 404; + } +} + +int WebAPIAdapter::devicesetChannelWorkspacePut( + int deviceSetIndex, + int channelIndex, + SWGSDRangel::SWGWorkspaceInfo& query, + SWGSDRangel::SWGSuccessResponse& response, + SWGSDRangel::SWGErrorResponse& error) +{ + error.init(); + + if ((deviceSetIndex >= 0) && (deviceSetIndex < (int) m_mainCore->m_deviceSets.size())) + { + DeviceSet *deviceSet = m_mainCore->m_deviceSets[deviceSetIndex]; + + if ((channelIndex >= 0) && (channelIndex < deviceSet->getNumberOfChannels())) + { + int workspaceIndex = query.getIndex(); + MainCore::MsgMoveChannelUIToWorkspace *msg = MainCore::MsgMoveChannelUIToWorkspace::create(deviceSetIndex, channelIndex, workspaceIndex); + m_mainCore->m_mainMessageQueue->push(msg); + response.init(); + *response.getMessage() = QString("Message to move a channel UI to workspace (MsgMoveChannelUIToWorkspace) was submitted successfully"); + return 202; + } + else + { + *error.getMessage() = QString("There is no channel with index %1 in device set %2").arg(channelIndex).arg(deviceSetIndex); + return 404; + } + } + else + { + *error.getMessage() = QString("There is no device set with index %1").arg(deviceSetIndex); + return 404; + } +} + int WebAPIAdapter::devicesetChannelSettingsPutPatch( int deviceSetIndex, int channelIndex, @@ -3962,6 +4170,49 @@ int WebAPIAdapter::featuresetFeatureActionsPost( } } +int WebAPIAdapter::featuresetFeatureWorkspaceGet( + int featureIndex, + SWGSDRangel::SWGWorkspaceInfo& response, + SWGSDRangel::SWGErrorResponse& error) +{ + if ((featureIndex >= 0) && (featureIndex < (int) m_mainCore->m_featureSets.size())) + { + FeatureSet *featureSet = m_mainCore->m_featureSets[0]; + Feature *feature = featureSet->getFeatureAt(featureIndex); + response.setIndex(feature->getWorkspaceIndex()); + return 200; + } + else + { + error.init(); + *error.getMessage() = QString("There is no feature with index %1").arg(featureIndex); + return 404; + } +} + +int WebAPIAdapter::featuresetFeatureWorkspacePut( + int featureIndex, + SWGSDRangel::SWGWorkspaceInfo& query, + SWGSDRangel::SWGSuccessResponse& response, + SWGSDRangel::SWGErrorResponse& error) +{ + if ((featureIndex >= 0) && (featureIndex < (int) m_mainCore->m_featureSets.size())) + { + int workspaceIndex = query.getIndex(); + MainCore::MsgMoveFeatureUIToWorkspace *msg = MainCore::MsgMoveFeatureUIToWorkspace::create(featureIndex, workspaceIndex); + m_mainCore->m_mainMessageQueue->push(msg); + response.init(); + *response.getMessage() = QString("Message to move a feature UI to workspace (MsgMoveFeatureUIToWorkspace) was submitted successfully"); + return 202; + } + else + { + error.init(); + *error.getMessage() = QString("There is no feature with index %1").arg(featureIndex); + return 404; + } +} + void WebAPIAdapter::getDeviceSetList(SWGSDRangel::SWGDeviceSetList* deviceSetList) { deviceSetList->init(); diff --git a/sdrbase/webapi/webapiadapter.h b/sdrbase/webapi/webapiadapter.h index e90d3416a..f8966aefc 100644 --- a/sdrbase/webapi/webapiadapter.h +++ b/sdrbase/webapi/webapiadapter.h @@ -321,6 +321,17 @@ public: SWGSDRangel::SWGSuccessResponse& response, SWGSDRangel::SWGErrorResponse& error); + virtual int devicesetSpectrumWorkspaceGet( + int deviceSetIndex, + SWGSDRangel::SWGWorkspaceInfo& response, + SWGSDRangel::SWGErrorResponse& error); + + virtual int devicesetSpectrumWorkspacePut( + int deviceSetIndex, + SWGSDRangel::SWGWorkspaceInfo& query, + SWGSDRangel::SWGSuccessResponse& response, + SWGSDRangel::SWGErrorResponse& error); + virtual int devicesetDevicePut( int deviceSetIndex, SWGSDRangel::SWGDeviceListItem& query, @@ -339,6 +350,17 @@ public: SWGSDRangel::SWGSuccessResponse& response, SWGSDRangel::SWGErrorResponse& error); + virtual int devicesetDeviceWorkspaceGet( + int deviceSetIndex, + SWGSDRangel::SWGWorkspaceInfo& response, + SWGSDRangel::SWGErrorResponse& error); + + virtual int devicesetDeviceWorkspacePut( + int deviceSetIndex, + SWGSDRangel::SWGWorkspaceInfo& query, + SWGSDRangel::SWGSuccessResponse& response, + SWGSDRangel::SWGErrorResponse& error); + virtual int devicesetDeviceSettingsPutPatch( int deviceSetIndex, bool force, @@ -429,6 +451,19 @@ public: SWGSDRangel::SWGChannelReport& response, SWGSDRangel::SWGErrorResponse& error); + virtual int devicesetChannelWorkspaceGet( + int deviceSetIndex, + int channelIndex, + SWGSDRangel::SWGWorkspaceInfo& response, + SWGSDRangel::SWGErrorResponse& error); + + virtual int devicesetChannelWorkspacePut( + int deviceSetIndex, + int channelIndex, + SWGSDRangel::SWGWorkspaceInfo& query, + SWGSDRangel::SWGSuccessResponse& response, + SWGSDRangel::SWGErrorResponse& error); + virtual int featuresetGet( int deviceSetIndex, SWGSDRangel::SWGFeatureSet& response, @@ -507,6 +542,17 @@ public: SWGSDRangel::SWGSuccessResponse& response, SWGSDRangel::SWGErrorResponse& error); + virtual int featuresetFeatureWorkspaceGet( + int featureIndex, + SWGSDRangel::SWGWorkspaceInfo& response, + SWGSDRangel::SWGErrorResponse& error); + + virtual int featuresetFeatureWorkspacePut( + int featureIndex, + SWGSDRangel::SWGWorkspaceInfo& query, + SWGSDRangel::SWGSuccessResponse& response, + SWGSDRangel::SWGErrorResponse& error); + private: MainCore *m_mainCore; diff --git a/sdrbase/webapi/webapiadapterinterface.cpp b/sdrbase/webapi/webapiadapterinterface.cpp index 5aa38447a..2bef497f6 100644 --- a/sdrbase/webapi/webapiadapterinterface.cpp +++ b/sdrbase/webapi/webapiadapterinterface.cpp @@ -58,24 +58,28 @@ std::regex WebAPIAdapterInterface::devicesetURLRe("^/sdrangel/deviceset/([0-9]{1 std::regex WebAPIAdapterInterface::devicesetFocusURLRe("^/sdrangel/deviceset/([0-9]{1,2})/focus$"); std::regex WebAPIAdapterInterface::devicesetSpectrumSettingsURLRe("^/sdrangel/deviceset/([0-9]{1,2})/spectrum/settings$"); std::regex WebAPIAdapterInterface::devicesetSpectrumServerURLRe("^/sdrangel/deviceset/([0-9]{1,2})/spectrum/server$"); +std::regex WebAPIAdapterInterface::devicesetSpectrumWorkspaceURLRe("^/sdrangel/deviceset/([0-9]{1,2})/spectrum/workspace$"); std::regex WebAPIAdapterInterface::devicesetDeviceURLRe("^/sdrangel/deviceset/([0-9]{1,2})/device$"); std::regex WebAPIAdapterInterface::devicesetDeviceSettingsURLRe("^/sdrangel/deviceset/([0-9]{1,2})/device/settings$"); std::regex WebAPIAdapterInterface::devicesetDeviceRunURLRe("^/sdrangel/deviceset/([0-9]{1,2})/device/run$"); std::regex WebAPIAdapterInterface::devicesetDeviceSubsystemRunURLRe("^/sdrangel/deviceset/([0-9]{1,2})/subdevice/([0-9]{1,2})/run$"); std::regex WebAPIAdapterInterface::devicesetDeviceReportURLRe("^/sdrangel/deviceset/([0-9]{1,2})/device/report$"); std::regex WebAPIAdapterInterface::devicesetDeviceActionsURLRe("^/sdrangel/deviceset/([0-9]{1,2})/device/actions$"); +std::regex WebAPIAdapterInterface::devicesetDeviceWorkspaceURLRe("^/sdrangel/deviceset/([0-9]{1,2})/device/workspace$"); std::regex WebAPIAdapterInterface::devicesetChannelsReportURLRe("^/sdrangel/deviceset/([0-9]{1,2})/channels/report$"); std::regex WebAPIAdapterInterface::devicesetChannelURLRe("^/sdrangel/deviceset/([0-9]{1,2})/channel$"); std::regex WebAPIAdapterInterface::devicesetChannelIndexURLRe("^/sdrangel/deviceset/([0-9]{1,2})/channel/([0-9]{1,2})$"); std::regex WebAPIAdapterInterface::devicesetChannelSettingsURLRe("^/sdrangel/deviceset/([0-9]{1,2})/channel/([0-9]{1,2})/settings$"); std::regex WebAPIAdapterInterface::devicesetChannelReportURLRe("^/sdrangel/deviceset/([0-9]{1,2})/channel/([0-9]{1,2})/report"); std::regex WebAPIAdapterInterface::devicesetChannelActionsURLRe("^/sdrangel/deviceset/([0-9]{1,2})/channel/([0-9]{1,2})/actions"); +std::regex WebAPIAdapterInterface::devicesetChannelWorkspaceURLRe("^/sdrangel/deviceset/([0-9]{1,2})/channel/([0-9]{1,2})/workspace"); std::regex WebAPIAdapterInterface::featuresetFeatureIndexURLRe("^/sdrangel/featureset/feature/([0-9]{1,2})$"); std::regex WebAPIAdapterInterface::featuresetFeatureRunURLRe("^/sdrangel/featureset/feature/([0-9]{1,2})/run$"); std::regex WebAPIAdapterInterface::featuresetFeatureSettingsURLRe("^/sdrangel/featureset/feature/([0-9]{1,2})/settings$"); std::regex WebAPIAdapterInterface::featuresetFeatureReportURLRe("^/sdrangel/featureset/feature/([0-9]{1,2})/report$"); std::regex WebAPIAdapterInterface::featuresetFeatureActionsURLRe("^/sdrangel/featureset/feature/([0-9]{1,2})/actions$"); +std::regex WebAPIAdapterInterface::featuresetFeatureWorkspaceURLRe("^/sdrangel/featureset/feature/([0-9]{1,2})/workspace$"); void WebAPIAdapterInterface::ConfigKeys::debug() const { diff --git a/sdrbase/webapi/webapiadapterinterface.h b/sdrbase/webapi/webapiadapterinterface.h index 11c4cd31f..0e91f9a53 100644 --- a/sdrbase/webapi/webapiadapterinterface.h +++ b/sdrbase/webapi/webapiadapterinterface.h @@ -66,6 +66,7 @@ namespace SWGSDRangel class SWGDeviceState; class SWGDeviceReport; class SWGDeviceActions; + class SWGWorkspaceInfo; class SWGChannelsDetail; class SWGChannelSettings; class SWGChannelReport; @@ -1084,6 +1085,40 @@ public: return 501; } + /** + * Handler of /sdrangel/deviceset/{devicesetIndex}/spectrum/workspace (GET) + * returns the Http status code (default 501: not implemented) + */ + virtual int devicesetSpectrumWorkspaceGet( + int deviceSetIndex, + SWGSDRangel::SWGWorkspaceInfo& response, + SWGSDRangel::SWGErrorResponse& error) + { + (void) deviceSetIndex; + (void) response; + error.init(); + *error.getMessage() = QString("Function not implemented"); + return 501; + } + + /** + * Handler of /sdrangel/deviceset/{devicesetIndex}/spectrum/workspace (GET) + * returns the Http status code (default 501: not implemented) + */ + virtual int devicesetSpectrumWorkspacePut( + int deviceSetIndex, + SWGSDRangel::SWGWorkspaceInfo& query, + SWGSDRangel::SWGSuccessResponse& response, + SWGSDRangel::SWGErrorResponse& error) + { + (void) deviceSetIndex; + (void) query; + (void) response; + error.init(); + *error.getMessage() = QString("Function not implemented"); + return 501; + } + /** * Handler of /sdrangel/deviceset/{devicesetIndex}/device (PUT) swagger/sdrangel/code/html2/index.html#api-Default-instanceChannels * returns the Http status code (default 501: not implemented) @@ -1293,6 +1328,40 @@ public: return 501; } + /** + * Handler of /sdrangel/deviceset/{devicesetIndex}/device/workspace (GET) swagger/sdrangel/code/html2/index.html#api-Default-instanceChannels + * returns the Http status code (default 501: not implemented) + */ + virtual int devicesetDeviceWorkspaceGet( + int deviceSetIndex, + SWGSDRangel::SWGWorkspaceInfo& response, + SWGSDRangel::SWGErrorResponse& error) + { + (void) deviceSetIndex; + (void) response; + error.init(); + *error.getMessage() = QString("Function not implemented"); + return 501; + } + + /** + * Handler of /sdrangel/deviceset/{devicesetIndex}/device/workspace (PUT) swagger/sdrangel/code/html2/index.html#api-Default-instanceChannels + * returns the Http status code (default 501: not implemented) + */ + virtual int devicesetDeviceWorkspacePut( + int deviceSetIndex, + SWGSDRangel::SWGWorkspaceInfo& query, + SWGSDRangel::SWGSuccessResponse& response, + SWGSDRangel::SWGErrorResponse& error) + { + (void) deviceSetIndex; + (void) query; + (void) response; + error.init(); + *error.getMessage() = QString("Function not implemented"); + return 501; + } + /** * Handler of /sdrangel/deviceset/{deviceSetIndex}/channel (POST) swagger/sdrangel/code/html2/index.html#api-Default-instanceChannels * returns the Http status code (default 501: not implemented) @@ -1372,7 +1441,7 @@ public: /** - * Handler of /sdrangel/deviceset/{deviceSetIndex}/channel/{channelIndex}/settings (GET) swagger/sdrangel/code/html2/index.html#api-Default-instanceChannels + * Handler of /sdrangel/deviceset/{deviceSetIndex}/channel/{channelIndex}/report (GET) swagger/sdrangel/code/html2/index.html#api-Default-instanceChannels * returns the Http status code (default 501: not implemented) */ virtual int devicesetChannelReportGet( @@ -1411,6 +1480,44 @@ public: return 501; } + /** + * Handler of /sdrangel/deviceset/{deviceSetIndex}/channel/{channelIndex}/workspace (GET) swagger/sdrangel/code/html2/index.html#api-Default-instanceChannels + * returns the Http status code (default 501: not implemented) + */ + virtual int devicesetChannelWorkspaceGet( + int deviceSetIndex, + int channelIndex, + SWGSDRangel::SWGWorkspaceInfo& response, + SWGSDRangel::SWGErrorResponse& error) + { + (void) deviceSetIndex; + (void) channelIndex; + (void) response; + error.init(); + *error.getMessage() = QString("Function not implemented"); + return 501; + } + + /** + * Handler of /sdrangel/deviceset/{deviceSetIndex}/channel/{channelIndex}/workspace (GET) swagger/sdrangel/code/html2/index.html#api-Default-instanceChannels + * returns the Http status code (default 501: not implemented) + */ + virtual int devicesetChannelWorkspacePut( + int deviceSetIndex, + int channelIndex, + SWGSDRangel::SWGWorkspaceInfo& query, + SWGSDRangel::SWGSuccessResponse& response, + SWGSDRangel::SWGErrorResponse& error) + { + (void) deviceSetIndex; + (void) channelIndex; + (void) query; + (void) response; + error.init(); + *error.getMessage() = QString("Function not implemented"); + return 501; + } + /** * Handler of /sdrangel/featureset/{featuresetIndex} (GET) * returns the Http status code (default 501: not implemented) @@ -1649,6 +1756,40 @@ public: return 501; } + /** + * Handler of /sdrangel/featureset/feature/{featureIndex}/workspace (GET) + * returns the Http status code (default 501: not implemented) + */ + virtual int featuresetFeatureWorkspaceGet( + int featureIndex, + SWGSDRangel::SWGWorkspaceInfo& response, + SWGSDRangel::SWGErrorResponse& error) + { + (void) featureIndex; + (void) response; + error.init(); + *error.getMessage() = QString("Function not implemented"); + return 501; + } + + /** + * Handler of /sdrangel/featureset/feature/{featureIndex}/workspace (PUT) + * returns the Http status code (default 501: not implemented) + */ + virtual int featuresetFeatureWorkspacePut( + int featureIndex, + SWGSDRangel::SWGWorkspaceInfo& query, + SWGSDRangel::SWGSuccessResponse& response, + SWGSDRangel::SWGErrorResponse& error) + { + (void) featureIndex; + (void) query; + (void) response; + error.init(); + *error.getMessage() = QString("Function not implemented"); + return 501; + } + static QString instanceSummaryURL; static QString instanceConfigURL; static QString instanceDevicesURL; @@ -1687,23 +1828,27 @@ public: static std::regex devicesetFocusURLRe; static std::regex devicesetSpectrumSettingsURLRe; static std::regex devicesetSpectrumServerURLRe; + static std::regex devicesetSpectrumWorkspaceURLRe; static std::regex devicesetDeviceURLRe; static std::regex devicesetDeviceSettingsURLRe; static std::regex devicesetDeviceRunURLRe; static std::regex devicesetDeviceSubsystemRunURLRe; static std::regex devicesetDeviceReportURLRe; static std::regex devicesetDeviceActionsURLRe; + static std::regex devicesetDeviceWorkspaceURLRe; static std::regex devicesetChannelURLRe; static std::regex devicesetChannelIndexURLRe; static std::regex devicesetChannelSettingsURLRe; static std::regex devicesetChannelReportURLRe; static std::regex devicesetChannelActionsURLRe; + static std::regex devicesetChannelWorkspaceURLRe; static std::regex devicesetChannelsReportURLRe; static std::regex featuresetFeatureIndexURLRe; static std::regex featuresetFeatureRunURLRe; static std::regex featuresetFeatureSettingsURLRe; static std::regex featuresetFeatureReportURLRe; static std::regex featuresetFeatureActionsURLRe; + static std::regex featuresetFeatureWorkspaceURLRe; }; diff --git a/sdrbase/webapi/webapirequestmapper.cpp b/sdrbase/webapi/webapirequestmapper.cpp index e2f7566ec..2c5b42369 100644 --- a/sdrbase/webapi/webapirequestmapper.cpp +++ b/sdrbase/webapi/webapirequestmapper.cpp @@ -52,6 +52,7 @@ #include "SWGDeviceState.h" #include "SWGDeviceReport.h" #include "SWGDeviceActions.h" +#include "SWGWorkspaceInfo.h" #include "SWGChannelsDetail.h" #include "SWGChannelSettings.h" #include "SWGChannelReport.h" @@ -198,6 +199,8 @@ void WebAPIRequestMapper::service(qtwebapp::HttpRequest& request, qtwebapp::Http devicesetSpectrumSettingsService(std::string(desc_match[1]), request, response); } else if (std::regex_match(pathStr, desc_match, WebAPIAdapterInterface::devicesetSpectrumServerURLRe)) { devicesetSpectrumServerService(std::string(desc_match[1]), request, response); + } else if (std::regex_match(pathStr, desc_match, WebAPIAdapterInterface::devicesetSpectrumWorkspaceURLRe)) { + devicesetSpectrumWorkspaceService(std::string(desc_match[1]), request, response); } else if (std::regex_match(pathStr, desc_match, WebAPIAdapterInterface::devicesetDeviceSettingsURLRe)) { devicesetDeviceSettingsService(std::string(desc_match[1]), request, response); } else if (std::regex_match(pathStr, desc_match, WebAPIAdapterInterface::devicesetDeviceRunURLRe)) { @@ -208,6 +211,8 @@ void WebAPIRequestMapper::service(qtwebapp::HttpRequest& request, qtwebapp::Http devicesetDeviceReportService(std::string(desc_match[1]), request, response); } else if (std::regex_match(pathStr, desc_match, WebAPIAdapterInterface::devicesetDeviceActionsURLRe)) { devicesetDeviceActionsService(std::string(desc_match[1]), request, response); + } else if (std::regex_match(pathStr, desc_match, WebAPIAdapterInterface::devicesetDeviceWorkspaceURLRe)) { + devicesetDeviceWorkspaceService(std::string(desc_match[1]), request, response); } else if (std::regex_match(pathStr, desc_match, WebAPIAdapterInterface::devicesetChannelsReportURLRe)) { devicesetChannelsReportService(std::string(desc_match[1]), request, response); } else if (std::regex_match(pathStr, desc_match, WebAPIAdapterInterface::devicesetChannelURLRe)) { @@ -220,6 +225,8 @@ void WebAPIRequestMapper::service(qtwebapp::HttpRequest& request, qtwebapp::Http devicesetChannelReportService(std::string(desc_match[1]), std::string(desc_match[2]), request, response); } else if (std::regex_match(pathStr, desc_match, WebAPIAdapterInterface::devicesetChannelActionsURLRe)) { devicesetChannelActionsService(std::string(desc_match[1]), std::string(desc_match[2]), request, response); + } else if (std::regex_match(pathStr, desc_match, WebAPIAdapterInterface::devicesetChannelWorkspaceURLRe)) { + devicesetChannelWorkspaceService(std::string(desc_match[1]), std::string(desc_match[2]), request, response); } else if (std::regex_match(pathStr, desc_match, WebAPIAdapterInterface::featuresetFeatureIndexURLRe)) { featuresetFeatureIndexService(std::string(desc_match[1]), request, response); } else if (std::regex_match(pathStr, desc_match, WebAPIAdapterInterface::featuresetFeatureRunURLRe)) { @@ -230,6 +237,8 @@ void WebAPIRequestMapper::service(qtwebapp::HttpRequest& request, qtwebapp::Http featuresetFeatureReportService(std::string(desc_match[1]), request, response); } else if (std::regex_match(pathStr, desc_match, WebAPIAdapterInterface::featuresetFeatureActionsURLRe)) { featuresetFeatureActionsService(std::string(desc_match[1]), request, response); + } else if (std::regex_match(pathStr, desc_match, WebAPIAdapterInterface::featuresetFeatureWorkspaceURLRe)) { + featuresetFeatureWorkspaceService(std::string(desc_match[1]), request, response); } else // serve static documentation pages { @@ -2259,6 +2268,80 @@ void WebAPIRequestMapper::devicesetSpectrumServerService(const std::string& inde } } +void WebAPIRequestMapper::devicesetSpectrumWorkspaceService(const std::string& indexStr, qtwebapp::HttpRequest& request, qtwebapp::HttpResponse& response) +{ + SWGSDRangel::SWGErrorResponse errorResponse; + response.setHeader("Content-Type", "application/json"); + response.setHeader("Access-Control-Allow-Origin", "*"); + + try + { + int deviceSetIndex = boost::lexical_cast(indexStr); + + if (request.getMethod() == "GET") + { + SWGSDRangel::SWGWorkspaceInfo normalResponse; + int status = m_adapter->devicesetSpectrumWorkspaceGet(deviceSetIndex, normalResponse, errorResponse); + response.setStatus(status); + + if (status/100 == 2) { + response.write(normalResponse.asJson().toUtf8()); + } else { + response.write(errorResponse.asJson().toUtf8()); + } + } + else if (request.getMethod() == "PUT") + { + QString jsonStr = request.getBody(); + QJsonObject jsonObject; + + if (parseJsonBody(jsonStr, jsonObject, response)) + { + SWGSDRangel::SWGWorkspaceInfo query; + SWGSDRangel::SWGSuccessResponse normalResponse; + + if (validateWorkspaceInfo(query, jsonObject)) + { + int status = m_adapter->devicesetSpectrumWorkspacePut( + deviceSetIndex, + query, + normalResponse, + errorResponse + ); + response.setStatus(status); + + if (status/100 == 2) { + response.write(normalResponse.asJson().toUtf8()); + } else { + response.write(errorResponse.asJson().toUtf8()); + } + } + else + { + response.setStatus(400,"Invalid JSON request"); + errorResponse.init(); + *errorResponse.getMessage() = "Invalid JSON request"; + response.write(errorResponse.asJson().toUtf8()); + } + } + else + { + response.setStatus(400,"Invalid JSON format"); + errorResponse.init(); + *errorResponse.getMessage() = "Invalid JSON format"; + response.write(errorResponse.asJson().toUtf8()); + } + } + } + catch (const boost::bad_lexical_cast &e) + { + errorResponse.init(); + *errorResponse.getMessage() = "Wrong integer conversion on device set index"; + response.setStatus(400,"Invalid data"); + response.write(errorResponse.asJson().toUtf8()); + } +} + void WebAPIRequestMapper::devicesetDeviceService(const std::string& indexStr, qtwebapp::HttpRequest& request, qtwebapp::HttpResponse& response) { SWGSDRangel::SWGErrorResponse errorResponse; @@ -2582,6 +2665,80 @@ void WebAPIRequestMapper::devicesetDeviceReportService(const std::string& indexS } } +void WebAPIRequestMapper::devicesetDeviceWorkspaceService(const std::string& indexStr, qtwebapp::HttpRequest& request, qtwebapp::HttpResponse& response) +{ + SWGSDRangel::SWGErrorResponse errorResponse; + response.setHeader("Content-Type", "application/json"); + response.setHeader("Access-Control-Allow-Origin", "*"); + + try + { + int deviceSetIndex = boost::lexical_cast(indexStr); + + if (request.getMethod() == "GET") + { + SWGSDRangel::SWGWorkspaceInfo normalResponse; + int status = m_adapter->devicesetDeviceWorkspaceGet(deviceSetIndex, normalResponse, errorResponse); + response.setStatus(status); + + if (status/100 == 2) { + response.write(normalResponse.asJson().toUtf8()); + } else { + response.write(errorResponse.asJson().toUtf8()); + } + } + else if (request.getMethod() == "PUT") + { + QString jsonStr = request.getBody(); + QJsonObject jsonObject; + + if (parseJsonBody(jsonStr, jsonObject, response)) + { + SWGSDRangel::SWGWorkspaceInfo query; + SWGSDRangel::SWGSuccessResponse normalResponse; + + if (validateWorkspaceInfo(query, jsonObject)) + { + int status = m_adapter->devicesetDeviceWorkspacePut( + deviceSetIndex, + query, + normalResponse, + errorResponse + ); + response.setStatus(status); + + if (status/100 == 2) { + response.write(normalResponse.asJson().toUtf8()); + } else { + response.write(errorResponse.asJson().toUtf8()); + } + } + else + { + response.setStatus(400,"Invalid JSON request"); + errorResponse.init(); + *errorResponse.getMessage() = "Invalid JSON request"; + response.write(errorResponse.asJson().toUtf8()); + } + } + else + { + response.setStatus(400,"Invalid JSON format"); + errorResponse.init(); + *errorResponse.getMessage() = "Invalid JSON format"; + response.write(errorResponse.asJson().toUtf8()); + } + } + } + catch(const boost::bad_lexical_cast &e) + { + errorResponse.init(); + *errorResponse.getMessage() = "Wrong integer conversion on device set index"; + response.setStatus(400,"Invalid data"); + response.write(errorResponse.asJson().toUtf8()); + } +} + void WebAPIRequestMapper::devicesetDeviceActionsService(const std::string& indexStr, qtwebapp::HttpRequest& request, qtwebapp::HttpResponse& response) { SWGSDRangel::SWGErrorResponse errorResponse; @@ -3024,6 +3181,94 @@ void WebAPIRequestMapper::devicesetChannelActionsService( } } + +void WebAPIRequestMapper::devicesetChannelWorkspaceService( + const std::string& deviceSetIndexStr, + const std::string& channelIndexStr, + qtwebapp::HttpRequest& request, + qtwebapp::HttpResponse& response) +{ + SWGSDRangel::SWGErrorResponse errorResponse; + response.setHeader("Content-Type", "application/json"); + response.setHeader("Access-Control-Allow-Origin", "*"); + + try + { + int deviceSetIndex = boost::lexical_cast(deviceSetIndexStr); + int channelIndex = boost::lexical_cast(channelIndexStr); + + if (request.getMethod() == "GET") + { + SWGSDRangel::SWGWorkspaceInfo normalResponse; + int status = m_adapter->devicesetChannelWorkspaceGet(deviceSetIndex, channelIndex, normalResponse, errorResponse); + response.setStatus(status); + + if (status/100 == 2) { + response.write(normalResponse.asJson().toUtf8()); + } else { + response.write(errorResponse.asJson().toUtf8()); + } + } + else if (request.getMethod() == "PUT") + { + QString jsonStr = request.getBody(); + QJsonObject jsonObject; + + if (parseJsonBody(jsonStr, jsonObject, response)) + { + SWGSDRangel::SWGWorkspaceInfo query; + SWGSDRangel::SWGSuccessResponse normalResponse; + + if (validateWorkspaceInfo(query, jsonObject)) + { + int status = m_adapter->devicesetChannelWorkspacePut( + deviceSetIndex, + channelIndex, + query, + normalResponse, + errorResponse + ); + response.setStatus(status); + + if (status/100 == 2) { + response.write(normalResponse.asJson().toUtf8()); + } else { + response.write(errorResponse.asJson().toUtf8()); + } + } + else + { + response.setStatus(400,"Invalid JSON request"); + errorResponse.init(); + *errorResponse.getMessage() = "Invalid JSON request"; + response.write(errorResponse.asJson().toUtf8()); + } + } + else + { + response.setStatus(400,"Invalid JSON format"); + errorResponse.init(); + *errorResponse.getMessage() = "Invalid JSON format"; + response.write(errorResponse.asJson().toUtf8()); + } + } + else + { + response.setStatus(405,"Invalid HTTP method"); + errorResponse.init(); + *errorResponse.getMessage() = "Invalid HTTP method"; + response.write(errorResponse.asJson().toUtf8()); + } + } + catch(const boost::bad_lexical_cast &e) + { + errorResponse.init(); + *errorResponse.getMessage() = "Wrong integer conversion on index"; + response.setStatus(400,"Invalid data"); + response.write(errorResponse.asJson().toUtf8()); + } +} + void WebAPIRequestMapper::featuresetService(qtwebapp::HttpRequest& request, qtwebapp::HttpResponse& response) { SWGSDRangel::SWGErrorResponse errorResponse; @@ -3555,6 +3800,90 @@ void WebAPIRequestMapper::featuresetFeatureActionsService( } } +void WebAPIRequestMapper::featuresetFeatureWorkspaceService( + const std::string& featureIndexStr, + qtwebapp::HttpRequest& request, + qtwebapp::HttpResponse& response) +{ + SWGSDRangel::SWGErrorResponse errorResponse; + response.setHeader("Content-Type", "application/json"); + response.setHeader("Access-Control-Allow-Origin", "*"); + + try + { + int featureIndex = boost::lexical_cast(featureIndexStr); + + if (request.getMethod() == "GET") + { + SWGSDRangel::SWGWorkspaceInfo normalResponse; + int status = m_adapter->featuresetFeatureWorkspaceGet(featureIndex, normalResponse, errorResponse); + response.setStatus(status); + + if (status/100 == 2) { + response.write(normalResponse.asJson().toUtf8()); + } else { + response.write(errorResponse.asJson().toUtf8()); + } + } + else if (request.getMethod() == "PUT") + { + QString jsonStr = request.getBody(); + QJsonObject jsonObject; + + if (parseJsonBody(jsonStr, jsonObject, response)) + { + SWGSDRangel::SWGWorkspaceInfo query; + SWGSDRangel::SWGSuccessResponse normalResponse; + + if (validateWorkspaceInfo(query, jsonObject)) + { + int status = m_adapter->featuresetFeatureWorkspacePut( + featureIndex, + query, + normalResponse, + errorResponse + ); + response.setStatus(status); + + if (status/100 == 2) { + response.write(normalResponse.asJson().toUtf8()); + } else { + response.write(errorResponse.asJson().toUtf8()); + } + } + else + { + response.setStatus(400,"Invalid JSON request"); + errorResponse.init(); + *errorResponse.getMessage() = "Invalid JSON request"; + response.write(errorResponse.asJson().toUtf8()); + } + } + else + { + response.setStatus(400,"Invalid JSON format"); + errorResponse.init(); + *errorResponse.getMessage() = "Invalid JSON format"; + response.write(errorResponse.asJson().toUtf8()); + } + } + else + { + response.setStatus(405,"Invalid HTTP method"); + errorResponse.init(); + *errorResponse.getMessage() = "Invalid HTTP method"; + response.write(errorResponse.asJson().toUtf8()); + } + } + catch(const boost::bad_lexical_cast &e) + { + errorResponse.init(); + *errorResponse.getMessage() = "Wrong integer conversion on index"; + response.setStatus(400,"Invalid data"); + response.write(errorResponse.asJson().toUtf8()); + } +} + bool WebAPIRequestMapper::parseJsonBody(QString& jsonStr, QJsonObject& jsonObject, qtwebapp::HttpResponse& response) { SWGSDRangel::SWGErrorResponse errorResponse; @@ -4237,6 +4566,17 @@ bool WebAPIRequestMapper::validateSpectrumSettings(SWGSDRangel::SWGGLSpectrum& s return true; } +bool WebAPIRequestMapper::validateWorkspaceInfo(SWGSDRangel::SWGWorkspaceInfo& workspaceInfo, QJsonObject& jsonObject) +{ + if (jsonObject.contains("index")) + { + workspaceInfo.setIndex(jsonObject["index"].toInt()); + return true; + } + + return false; +} + bool WebAPIRequestMapper::validateConfig( SWGSDRangel::SWGInstanceConfigResponse& config, QJsonObject& jsonObject, diff --git a/sdrbase/webapi/webapirequestmapper.h b/sdrbase/webapi/webapirequestmapper.h index 3921a5ad4..663ac030f 100644 --- a/sdrbase/webapi/webapirequestmapper.h +++ b/sdrbase/webapi/webapirequestmapper.h @@ -91,18 +91,21 @@ private: void devicesetFocusService(const std::string& indexStr, qtwebapp::HttpRequest& request, qtwebapp::HttpResponse& response); void devicesetSpectrumSettingsService(const std::string& indexStr, qtwebapp::HttpRequest& request, qtwebapp::HttpResponse& response); void devicesetSpectrumServerService(const std::string& indexStr, qtwebapp::HttpRequest& request, qtwebapp::HttpResponse& response); + void devicesetSpectrumWorkspaceService(const std::string& indexStr, qtwebapp::HttpRequest& request, qtwebapp::HttpResponse& response); void devicesetDeviceService(const std::string& indexStr, qtwebapp::HttpRequest& request, qtwebapp::HttpResponse& response); void devicesetDeviceSettingsService(const std::string& indexStr, qtwebapp::HttpRequest& request, qtwebapp::HttpResponse& response); void devicesetDeviceRunService(const std::string& indexStr, qtwebapp::HttpRequest& request, qtwebapp::HttpResponse& response); void devicesetDeviceSubsystemRunService(const std::string& indexStr, const std::string& subsystemIndexStr,qtwebapp::HttpRequest& request, qtwebapp::HttpResponse& response); void devicesetDeviceReportService(const std::string& indexStr, qtwebapp::HttpRequest& request, qtwebapp::HttpResponse& response); void devicesetDeviceActionsService(const std::string& indexStr, qtwebapp::HttpRequest& request, qtwebapp::HttpResponse& response); + void devicesetDeviceWorkspaceService(const std::string& indexStr, qtwebapp::HttpRequest& request, qtwebapp::HttpResponse& response); void devicesetChannelsReportService(const std::string& deviceSetIndexStr, qtwebapp::HttpRequest& request, qtwebapp::HttpResponse& response); void devicesetChannelService(const std::string& deviceSetIndexStr, qtwebapp::HttpRequest& request, qtwebapp::HttpResponse& response); void devicesetChannelIndexService(const std::string& deviceSetIndexStr, const std::string& channelIndexStr, qtwebapp::HttpRequest& request, qtwebapp::HttpResponse& response); void devicesetChannelSettingsService(const std::string& deviceSetIndexStr, const std::string& channelIndexStr, qtwebapp::HttpRequest& request, qtwebapp::HttpResponse& response); void devicesetChannelReportService(const std::string& deviceSetIndexStr, const std::string& channelIndexStr, qtwebapp::HttpRequest& request, qtwebapp::HttpResponse& response); void devicesetChannelActionsService(const std::string& deviceSetIndexStr, const std::string& channelIndexStr, qtwebapp::HttpRequest& request, qtwebapp::HttpResponse& response); + void devicesetChannelWorkspaceService(const std::string& deviceSetIndexStr, const std::string& channelIndexStr, qtwebapp::HttpRequest& request, qtwebapp::HttpResponse& response); void featuresetService(qtwebapp::HttpRequest& request, qtwebapp::HttpResponse& response); void featuresetFeatureService(qtwebapp::HttpRequest& request, qtwebapp::HttpResponse& response); @@ -112,7 +115,7 @@ private: void featuresetFeatureSettingsService(const std::string& featureIndexStr, qtwebapp::HttpRequest& request, qtwebapp::HttpResponse& response); void featuresetFeatureReportService(const std::string& featureIndexStr, qtwebapp::HttpRequest& request, qtwebapp::HttpResponse& response); void featuresetFeatureActionsService(const std::string& featureIndexStr, qtwebapp::HttpRequest& request, qtwebapp::HttpResponse& response); - + void featuresetFeatureWorkspaceService(const std::string& featureIndexStr, qtwebapp::HttpRequest& request, qtwebapp::HttpResponse& response); bool validatePresetTransfer(SWGSDRangel::SWGPresetTransfer& presetTransfer); bool validatePresetIdentifer(SWGSDRangel::SWGPresetIdentifier& presetIdentifier); @@ -131,6 +134,7 @@ private: bool validateAMBEDevices(SWGSDRangel::SWGAMBEDevices& ambeDevices, QJsonObject& jsonObject); bool validateLimeRFEConfig(SWGSDRangel::SWGLimeRFESettings& limeRFESettings, QJsonObject& jsonObject, QStringList& limeRFESettingsKeys); bool validateConfig(SWGSDRangel::SWGInstanceConfigResponse& config, QJsonObject& jsonObject, WebAPIAdapterInterface::ConfigKeys& configKeys); + bool validateWorkspaceInfo(SWGSDRangel::SWGWorkspaceInfo& workspaceInfo, QJsonObject& jsonObject); bool validateConfigurationIdentifier(SWGSDRangel::SWGConfigurationIdentifier& configurationIdentifier); bool validateConfigurationImportExport(SWGSDRangel::SWGConfigurationImportExport& configuratopmImportExport); diff --git a/sdrgui/device/devicegui.cpp b/sdrgui/device/devicegui.cpp index 4e313c03b..e7f32e484 100644 --- a/sdrgui/device/devicegui.cpp +++ b/sdrgui/device/devicegui.cpp @@ -29,10 +29,13 @@ #include "mainwindow.h" #include "gui/workspaceselectiondialog.h" #include "gui/samplingdevicedialog.h" +#include "device/deviceuiset.h" +#include "device/deviceapi.h" #include "devicegui.h" DeviceGUI::DeviceGUI(QWidget *parent) : QMdiSubWindow(parent), + m_deviceUISet(nullptr), m_deviceType(DeviceRx), m_deviceSetIndex(0), m_contextMenuType(ContextMenuNone), @@ -223,6 +226,15 @@ DeviceGUI::~DeviceGUI() qDebug("DeviceGUI::~DeviceGUI: end"); } +void DeviceGUI::setWorkspaceIndex(int index) +{ + m_workspaceIndex = index; + + if (m_deviceUISet) { + m_deviceUISet->m_deviceAPI->setWorkspaceIndex(index); + } +} + void DeviceGUI::closeEvent(QCloseEvent *event) { qDebug("DeviceGUI::closeEvent"); diff --git a/sdrgui/device/devicegui.h b/sdrgui/device/devicegui.h index a615e2643..32f4a90f8 100644 --- a/sdrgui/device/devicegui.h +++ b/sdrgui/device/devicegui.h @@ -37,6 +37,8 @@ class QPushButton; class QVBoxLayout; class QHBoxLayout; class QSizeGrip; +class DeviceUISet; + class SDRGUI_API DeviceGUI : public QMdiSubWindow { Q_OBJECT public: @@ -60,7 +62,7 @@ public: virtual void resetToDefaults() = 0; virtual QByteArray serialize() const = 0; virtual bool deserialize(const QByteArray& data) = 0; - void setWorkspaceIndex(int index) { m_workspaceIndex = index; } + void setWorkspaceIndex(int index); int getWorkspaceIndex() const { return m_workspaceIndex; } virtual MessageQueue* getInputMessageQueue() = 0; @@ -85,6 +87,7 @@ protected: void resetContextMenuType() { m_contextMenuType = ContextMenuNone; } int getAdditionalHeight() const { return 22 + 22; } + DeviceUISet* m_deviceUISet; DeviceType m_deviceType; int m_deviceSetIndex; int m_workspaceIndex; diff --git a/sdrgui/feature/featuregui.h b/sdrgui/feature/featuregui.h index 2d764f2a2..0491d7ad8 100644 --- a/sdrgui/feature/featuregui.h +++ b/sdrgui/feature/featuregui.h @@ -32,6 +32,7 @@ class QPushButton; class QVBoxLayout; class QHBoxLayout; class QSizeGrip; +class Feature; class SDRGUI_API FeatureGUI : public QMdiSubWindow { @@ -74,6 +75,7 @@ protected: void resetContextMenuType() { m_contextMenuType = ContextMenuNone; } int getAdditionalHeight() const { return 22 + 22; } // height of top and bottom bars + Feature *m_feature; int m_featureIndex; QString m_helpURL; RollupContents m_rollupContents; diff --git a/sdrgui/gui/glspectrum.h b/sdrgui/gui/glspectrum.h index a7676afd9..5347b5746 100644 --- a/sdrgui/gui/glspectrum.h +++ b/sdrgui/gui/glspectrum.h @@ -170,6 +170,7 @@ public: m_displayStreamIndex = streamIndex; } void setSpectrumVis(SpectrumVis *spectrumVis) { m_spectrumVis = spectrumVis; } + SpectrumVis *getSpectrumVis() { return m_spectrumVis; } const QList& getHistogramMarkers() const { return m_histogramMarkers; } QList& getHistogramMarkers() { return m_histogramMarkers; } void setHistogramMarkers(const QList& histogramMarkers); diff --git a/sdrgui/mainspectrum/mainspectrumgui.cpp b/sdrgui/mainspectrum/mainspectrumgui.cpp index 55de3a1f0..3eadc41d0 100644 --- a/sdrgui/mainspectrum/mainspectrumgui.cpp +++ b/sdrgui/mainspectrum/mainspectrumgui.cpp @@ -28,6 +28,7 @@ #include "gui/glspectrum.h" #include "gui/glspectrumgui.h" #include "gui/workspaceselectiondialog.h" +#include "dsp/spectrumvis.h" #include "mainspectrumgui.h" MainSpectrumGUI::MainSpectrumGUI(GLSpectrum *spectrum, GLSpectrumGUI *spectrumGUI, QWidget *parent) : @@ -166,6 +167,12 @@ MainSpectrumGUI::~MainSpectrumGUI() qDebug("MainSpectrumGUI::~MainSpectrumGUI: end"); } +void MainSpectrumGUI::setWorkspaceIndex(int index) +{ + m_workspaceIndex = index; + m_spectrum->getSpectrumVis()->setWorkspaceIndex(index); +} + void MainSpectrumGUI::closeEvent(QCloseEvent *event) { qDebug("MainSpectrumGUI::closeEvent"); diff --git a/sdrgui/mainspectrum/mainspectrumgui.h b/sdrgui/mainspectrum/mainspectrumgui.h index 9adf00a91..3373c8055 100644 --- a/sdrgui/mainspectrum/mainspectrumgui.h +++ b/sdrgui/mainspectrum/mainspectrumgui.h @@ -54,7 +54,7 @@ public: void setToolTip(const QString& tooltip); void setIndex(int index); int getIndex() const { return m_deviceSetIndex; } - void setWorkspaceIndex(int index) { m_workspaceIndex = index; } + void setWorkspaceIndex(int index); int getWorkspaceIndex() const { return m_workspaceIndex; } void setGeometryBytes(const QByteArray& blob) { m_geometryBytes = blob; } const QByteArray& getGeometryBytes() const { return m_geometryBytes; } diff --git a/sdrgui/mainwindow.cpp b/sdrgui/mainwindow.cpp index dcdb11c3f..696fc8eba 100644 --- a/sdrgui/mainwindow.cpp +++ b/sdrgui/mainwindow.cpp @@ -1754,6 +1754,66 @@ bool MainWindow::handleMessage(const Message& cmd) deleteFeature(notif.getFeatureSetIndex(), notif.getFeatureIndex()); return true; } + else if (MainCore::MsgMoveDeviceUIToWorkspace::match(cmd)) + { + MainCore::MsgMoveDeviceUIToWorkspace& notif = (MainCore::MsgMoveDeviceUIToWorkspace&) cmd; + int deviceSetIndex = notif.getDeviceSetIndex(); + + if (deviceSetIndex < (int) m_deviceUIs.size()) + { + DeviceUISet *deviceUISet = m_deviceUIs[deviceSetIndex]; + DeviceGUI *gui = deviceUISet->m_deviceGUI; + deviceMove(gui, notif.getWorkspaceIndex()); + } + + return true; + } + else if (MainCore::MsgMoveMainSpectrumUIToWorkspace::match(cmd)) + { + MainCore::MsgMoveMainSpectrumUIToWorkspace& notif = (MainCore::MsgMoveMainSpectrumUIToWorkspace&) cmd; + int deviceSetIndex = notif.getDeviceSetIndex(); + + if (deviceSetIndex < (int) m_deviceUIs.size()) + { + DeviceUISet *deviceUISet = m_deviceUIs[deviceSetIndex]; + MainSpectrumGUI *gui = deviceUISet->m_mainSpectrumGUI; + mainSpectrumMove(gui, notif.getWorkspaceIndex()); + } + + return true; + } + else if (MainCore::MsgMoveFeatureUIToWorkspace::match(cmd)) + { + MainCore::MsgMoveFeatureUIToWorkspace& notif = (MainCore::MsgMoveFeatureUIToWorkspace&) cmd; + int featureIndex = notif.getFeatureIndex(); + + if (featureIndex < (int) m_featureUIs[0]->getNumberOfFeatures()) + { + FeatureGUI *gui = m_featureUIs[0]->getFeatureGuiAt(featureIndex); + featureMove(gui, notif.getWorkspaceIndex()); + } + + return true; + } + else if (MainCore::MsgMoveChannelUIToWorkspace::match(cmd)) + { + MainCore::MsgMoveChannelUIToWorkspace& notif = (MainCore::MsgMoveChannelUIToWorkspace&) cmd; + int deviceSetIndex = notif.getDeviceSetIndex(); + + if (deviceSetIndex < (int) m_deviceUIs.size()) + { + int channelIndex = notif.getChannelIndex(); + DeviceUISet *deviceUISet = m_deviceUIs[deviceSetIndex]; + + if (channelIndex < deviceUISet->getNumberOfChannels()) + { + ChannelGUI *gui = deviceUISet->getChannelGUIAt(channelIndex); + channelMove(gui, notif.getWorkspaceIndex()); + } + } + + return true; + } else if (MainCore::MsgApplySettings::match(cmd)) { applySettings(); diff --git a/swagger/sdrangel/api/swagger/swagger.yaml b/swagger/sdrangel/api/swagger/swagger.yaml index cbb350a9b..318f68e15 100644 --- a/swagger/sdrangel/api/swagger/swagger.yaml +++ b/swagger/sdrangel/api/swagger/swagger.yaml @@ -1584,6 +1584,63 @@ paths: "501": $ref: "#/responses/Response_501" + /sdrangel/deviceset/{deviceSetIndex}/spectrum/workspace: + x-swagger-router-controller: deviceset + get: + description: Get main spectrum UI workspace information (GUI) + operationId: devicesetSpectrumWorkspaceGet + tags: + - DeviceSet + parameters: + - in: path + name: deviceSetIndex + type: integer + required: true + description: Index of device set in the device set list + responses: + "200": + description: On success return main spectrum UI workspace index + schema: + $ref: "#/definitions/WorkspaceInfo" + "404": + description: Invalid index + schema: + $ref: "#/definitions/ErrorResponse" + "500": + $ref: "#/responses/Response_500" + "501": + $ref: "#/responses/Response_501" + put: + description: move main spectrum UI to workspace (GUI) + operationId: devicesetSpectrumWorkspacePut + tags: + - DeviceSet + parameters: + - in: path + name: deviceSetIndex + type: integer + required: true + description: Index of device set in the device set list + - name: body + in: body + description: Destination workspace information + required: true + schema: + $ref: "#/definitions/WorkspaceInfo" + responses: + "202": + description: Message to perform action was sent successfully + schema: + $ref: "#/definitions/SuccessResponse" + "400": + description: Invalid device set or workspace index + schema: + $ref: "#/definitions/ErrorResponse" + "500": + $ref: "#/responses/Response_500" + "501": + $ref: "#/responses/Response_501" + /sdrangel/deviceset/{deviceSetIndex}/device: x-swagger-router-controller: deviceset put: @@ -1605,7 +1662,7 @@ paths: $ref: "#/definitions/DeviceListItem" responses: "202": - description: On successful semdomg of the message it returns the details of the device being set + description: On successful semding of the message it returns the details of the device being set schema: $ref: "#/definitions/DeviceListItem" "400": @@ -2021,6 +2078,63 @@ paths: "501": $ref: "#/responses/Response_501" + /sdrangel/deviceset/{deviceSetIndex}/device/workspace: + x-swagger-router-controller: deviceset + get: + description: get device UI workspace information (GUI) + operationId: devicesetDeviceWorkspaceGet + tags: + - DeviceSet + parameters: + - in: path + name: deviceSetIndex + type: integer + required: true + description: Index of device set in the device set list + responses: + "200": + description: On success return workspace information + schema: + $ref: "#/definitions/WorkspaceInfo" + "400": + description: Invalid device set index + schema: + $ref: "#/definitions/ErrorResponse" + "500": + $ref: "#/responses/Response_500" + "501": + $ref: "#/responses/Response_501" + put: + description: move device UI to workspace (GUI) + operationId: devicesetDeviceWorkspacePut + tags: + - DeviceSet + parameters: + - in: path + name: deviceSetIndex + type: integer + required: true + description: Index of device set in the device set list + - name: body + in: body + description: Destination workspace information + required: true + schema: + $ref: "#/definitions/WorkspaceInfo" + responses: + "202": + description: Message to perform action was sent successfully + schema: + $ref: "#/definitions/SuccessResponse" + "400": + description: Invalid device set or workspace index + schema: + $ref: "#/definitions/ErrorResponse" + "500": + $ref: "#/responses/Response_500" + "501": + $ref: "#/responses/Response_501" + /sdrangel/deviceset/{deviceSetIndex}/channel: x-swagger-router-controller: deviceset post: @@ -2286,6 +2400,73 @@ paths: "501": $ref: "#/responses/Response_501" + /sdrangel/deviceset/{deviceSetIndex}/channel/{channelIndex}/workspace: + x-swagger-router-controller: deviceset + get: + description: get channel UI workspace information (GUI) + operationId: devicesetChannelWorkspaceGet + tags: + - DeviceSet + parameters: + - in: path + name: deviceSetIndex + type: integer + required: true + description: Index of device set in the device set list + - in: path + name: channelIndex + type: integer + required: true + description: Index of the channel in the channels list for this device set + responses: + "200": + description: On success return workspace information + schema: + $ref: "#/definitions/WorkspaceInfo" + "400": + description: Invalid device set index + schema: + $ref: "#/definitions/ErrorResponse" + "500": + $ref: "#/responses/Response_500" + "501": + $ref: "#/responses/Response_501" + put: + description: move channel UI to workspace (GUI) + operationId: devicesetChannelWorkspacePut + tags: + - DeviceSet + parameters: + - in: path + name: deviceSetIndex + type: integer + required: true + description: Index of device set in the device set list + - in: path + name: channelIndex + type: integer + required: true + description: Index of the channel in the channels list for this device set + - name: body + in: body + description: Destination workspace information + required: true + schema: + $ref: "#/definitions/WorkspaceInfo" + responses: + "202": + description: Message to perform action was sent successfully + schema: + $ref: "#/definitions/SuccessResponse" + "400": + description: Invalid device set or workspace index + schema: + $ref: "#/definitions/ErrorResponse" + "500": + $ref: "#/responses/Response_500" + "501": + $ref: "#/responses/Response_501" + /sdrangel/featureset: x-swagger-router-controller: featureset get: @@ -2708,6 +2889,63 @@ paths: "501": $ref: "#/responses/Response_501" + /sdrangel/featureset/feature/{featureIndex}/workspace: + x-swagger-router-controller: featureset + get: + description: get feature UI workspace information (GUI) + operationId: featuresetFeatureWorkspaceGet + tags: + - FeatureSet + parameters: + - in: path + name: featureIndex + type: integer + required: true + description: Index of feature in the feature set + responses: + "200": + description: On success return workspace information + schema: + $ref: "#/definitions/WorkspaceInfo" + "400": + description: Invalid feature set index + schema: + $ref: "#/definitions/ErrorResponse" + "500": + $ref: "#/responses/Response_500" + "501": + $ref: "#/responses/Response_501" + put: + description: move feature UI to workspace (GUI) + operationId: featuresetFeatureWorkspacePut + tags: + - FeatureSet + parameters: + - in: path + name: featureIndex + type: integer + required: true + description: Index of feature in the feature set + - name: body + in: body + description: Destination workspace information + required: true + schema: + $ref: "#/definitions/WorkspaceInfo" + responses: + "202": + description: Message to perform action was sent successfully + schema: + $ref: "#/definitions/SuccessResponse" + "400": + description: Invalid device set or workspace index + schema: + $ref: "#/definitions/ErrorResponse" + "500": + $ref: "#/responses/Response_500" + "501": + $ref: "#/responses/Response_501" + /swagger: x-swagger-pipe: swagger_raw @@ -3116,6 +3354,14 @@ definitions: items: $ref: "#/definitions/Channel" + WorkspaceInfo: + description: "Workspace information" + required: + - index + properties: + index: + description: index of workspace in the list of workspaces + type: integer AudioDevices: description: "List of audio devices available in the system" diff --git a/swagger/sdrangel/code/html2/index.html b/swagger/sdrangel/code/html2/index.html index b685023c3..ef261f77e 100644 --- a/swagger/sdrangel/code/html2/index.html +++ b/swagger/sdrangel/code/html2/index.html @@ -14039,6 +14039,16 @@ margin-bottom: 20px; } }, "description" : "WFMMod" +}; + defs.WorkspaceInfo = { + "required" : [ "index" ], + "properties" : { + "index" : { + "type" : "integer", + "description" : "index of workspace in the list of workspaces" + } + }, + "description" : "Workspace information" }; defs.XtrxInputReport = { "properties" : { @@ -14491,6 +14501,12 @@ margin-bottom: 20px;
  • devicesetChannelSettingsPut
  • +
  • + devicesetChannelWorkspaceGet +
  • +
  • + devicesetChannelWorkspacePut +
  • devicesetChannelsReportGet
  • @@ -14530,6 +14546,12 @@ margin-bottom: 20px;
  • devicesetDeviceSubsystemRunPost
  • +
  • + devicesetDeviceWorkspaceGet +
  • +
  • + devicesetDeviceWorkspacePut +
  • devicesetFocusPatch
  • @@ -14554,6 +14576,12 @@ margin-bottom: 20px;
  • devicesetSpectrumSettingsPut
  • +
  • + devicesetSpectrumWorkspaceGet +
  • +
  • + devicesetSpectrumWorkspacePut +
  • instanceDeviceSetDelete
  • @@ -14591,6 +14619,12 @@ margin-bottom: 20px;
  • featuresetFeatureSettingsPatch
  • +
  • + featuresetFeatureWorkspaceGet +
  • +
  • + featuresetFeatureWorkspacePut +
  • featuresetGet
  • @@ -18427,6 +18461,960 @@ $(document).ready(function() {
    +
    +
    +
    +

    devicesetChannelWorkspaceGet

    +

    +
    +
    +
    +

    +

    get channel UI workspace information (GUI)

    +

    +
    +
    /sdrangel/deviceset/{deviceSetIndex}/channel/{channelIndex}/workspace
    +

    +

    Usage and SDK Samples

    +

    + + +
    +
    +
    curl -X GET "http://localhost/sdrangel/deviceset/{deviceSetIndex}/channel/{channelIndex}/workspace"
    +
    +
    +
    import SWGSDRangel.*;
    +import SWGSDRangel.auth.*;
    +import SWGSDRangel.model.*;
    +import SWGSDRangel.api.DeviceSetApi;
    +
    +import java.io.File;
    +import java.util.*;
    +
    +public class DeviceSetApiExample {
    +
    +    public static void main(String[] args) {
    +        
    +        DeviceSetApi apiInstance = new DeviceSetApi();
    +        Integer deviceSetIndex = 56; // Integer | Index of device set in the device set list
    +        Integer channelIndex = 56; // Integer | Index of the channel in the channels list for this device set
    +        try {
    +            WorkspaceInfo result = apiInstance.devicesetChannelWorkspaceGet(deviceSetIndex, channelIndex);
    +            System.out.println(result);
    +        } catch (ApiException e) {
    +            System.err.println("Exception when calling DeviceSetApi#devicesetChannelWorkspaceGet");
    +            e.printStackTrace();
    +        }
    +    }
    +}
    +
    + +
    +
    import SWGSDRangel.api.DeviceSetApi;
    +
    +public class DeviceSetApiExample {
    +
    +    public static void main(String[] args) {
    +        DeviceSetApi apiInstance = new DeviceSetApi();
    +        Integer deviceSetIndex = 56; // Integer | Index of device set in the device set list
    +        Integer channelIndex = 56; // Integer | Index of the channel in the channels list for this device set
    +        try {
    +            WorkspaceInfo result = apiInstance.devicesetChannelWorkspaceGet(deviceSetIndex, channelIndex);
    +            System.out.println(result);
    +        } catch (ApiException e) {
    +            System.err.println("Exception when calling DeviceSetApi#devicesetChannelWorkspaceGet");
    +            e.printStackTrace();
    +        }
    +    }
    +}
    +
    + +
    +
    Integer *deviceSetIndex = 56; // Index of device set in the device set list
    +Integer *channelIndex = 56; // Index of the channel in the channels list for this device set
    +
    +DeviceSetApi *apiInstance = [[DeviceSetApi alloc] init];
    +
    +[apiInstance devicesetChannelWorkspaceGetWith:deviceSetIndex
    +    channelIndex:channelIndex
    +              completionHandler: ^(WorkspaceInfo output, NSError* error) {
    +                            if (output) {
    +                                NSLog(@"%@", output);
    +                            }
    +                            if (error) {
    +                                NSLog(@"Error: %@", error);
    +                            }
    +                        }];
    +
    +
    + +
    +
    var SdRangel = require('sd_rangel');
    +
    +var api = new SdRangel.DeviceSetApi()
    +
    +var deviceSetIndex = 56; // {Integer} Index of device set in the device set list
    +
    +var channelIndex = 56; // {Integer} Index of the channel in the channels list for this device set
    +
    +
    +var callback = function(error, data, response) {
    +  if (error) {
    +    console.error(error);
    +  } else {
    +    console.log('API called successfully. Returned data: ' + data);
    +  }
    +};
    +api.devicesetChannelWorkspaceGet(deviceSetIndex, channelIndex, callback);
    +
    +
    + + +
    +
    using System;
    +using System.Diagnostics;
    +using SWGSDRangel.Api;
    +using SWGSDRangel.Client;
    +using SWGSDRangel.Model;
    +
    +namespace Example
    +{
    +    public class devicesetChannelWorkspaceGetExample
    +    {
    +        public void main()
    +        {
    +            
    +            var apiInstance = new DeviceSetApi();
    +            var deviceSetIndex = 56;  // Integer | Index of device set in the device set list
    +            var channelIndex = 56;  // Integer | Index of the channel in the channels list for this device set
    +
    +            try
    +            {
    +                WorkspaceInfo result = apiInstance.devicesetChannelWorkspaceGet(deviceSetIndex, channelIndex);
    +                Debug.WriteLine(result);
    +            }
    +            catch (Exception e)
    +            {
    +                Debug.Print("Exception when calling DeviceSetApi.devicesetChannelWorkspaceGet: " + e.Message );
    +            }
    +        }
    +    }
    +}
    +
    +
    + +
    +
    <?php
    +require_once(__DIR__ . '/vendor/autoload.php');
    +
    +$api_instance = new Swagger\Client\Api\DeviceSetApi();
    +$deviceSetIndex = 56; // Integer | Index of device set in the device set list
    +$channelIndex = 56; // Integer | Index of the channel in the channels list for this device set
    +
    +try {
    +    $result = $api_instance->devicesetChannelWorkspaceGet($deviceSetIndex, $channelIndex);
    +    print_r($result);
    +} catch (Exception $e) {
    +    echo 'Exception when calling DeviceSetApi->devicesetChannelWorkspaceGet: ', $e->getMessage(), PHP_EOL;
    +}
    +?>
    +
    + +
    +
    use Data::Dumper;
    +use SWGSDRangel::Configuration;
    +use SWGSDRangel::DeviceSetApi;
    +
    +my $api_instance = SWGSDRangel::DeviceSetApi->new();
    +my $deviceSetIndex = 56; # Integer | Index of device set in the device set list
    +my $channelIndex = 56; # Integer | Index of the channel in the channels list for this device set
    +
    +eval { 
    +    my $result = $api_instance->devicesetChannelWorkspaceGet(deviceSetIndex => $deviceSetIndex, channelIndex => $channelIndex);
    +    print Dumper($result);
    +};
    +if ($@) {
    +    warn "Exception when calling DeviceSetApi->devicesetChannelWorkspaceGet: $@\n";
    +}
    +
    + +
    +
    from __future__ import print_statement
    +import time
    +import swagger_sdrangel
    +from swagger_sdrangel.rest import ApiException
    +from pprint import pprint
    +
    +# create an instance of the API class
    +api_instance = swagger_sdrangel.DeviceSetApi()
    +deviceSetIndex = 56 # Integer | Index of device set in the device set list
    +channelIndex = 56 # Integer | Index of the channel in the channels list for this device set
    +
    +try: 
    +    api_response = api_instance.deviceset_channel_workspace_get(deviceSetIndex, channelIndex)
    +    pprint(api_response)
    +except ApiException as e:
    +    print("Exception when calling DeviceSetApi->devicesetChannelWorkspaceGet: %s\n" % e)
    +
    +
    + +

    Parameters

    + +
    Path parameters
    + + + + + + + + + + + + + +
    NameDescription
    deviceSetIndex* + + +
    +
    +
    + + Integer + + +
    + Index of device set in the device set list +
    +
    +
    + Required +
    +
    +
    +
    channelIndex* + + +
    +
    +
    + + Integer + + +
    + Index of the channel in the channels list for this device set +
    +
    +
    + Required +
    +
    +
    +
    + + + + + +

    Responses

    +

    Status: 200 - On success return workspace information

    + + + +
    +
    +
    + +
    + +
    +
    + +

    Status: 400 - Invalid device set index

    + + + +
    +
    +
    + +
    + +
    +
    + +

    Status: 500 - Error

    + + + +
    +
    +
    + +
    + +
    +
    + +

    Status: 501 - Function not implemented

    + + + +
    +
    +
    + +
    + +
    +
    + +
    +
    +
    +
    +
    +
    +

    devicesetChannelWorkspacePut

    +

    +
    +
    +
    +

    +

    move channel UI to workspace (GUI)

    +

    +
    +
    /sdrangel/deviceset/{deviceSetIndex}/channel/{channelIndex}/workspace
    +

    +

    Usage and SDK Samples

    +

    + + +
    +
    +
    curl -X PUT "http://localhost/sdrangel/deviceset/{deviceSetIndex}/channel/{channelIndex}/workspace"
    +
    +
    +
    import SWGSDRangel.*;
    +import SWGSDRangel.auth.*;
    +import SWGSDRangel.model.*;
    +import SWGSDRangel.api.DeviceSetApi;
    +
    +import java.io.File;
    +import java.util.*;
    +
    +public class DeviceSetApiExample {
    +
    +    public static void main(String[] args) {
    +        
    +        DeviceSetApi apiInstance = new DeviceSetApi();
    +        Integer deviceSetIndex = 56; // Integer | Index of device set in the device set list
    +        Integer channelIndex = 56; // Integer | Index of the channel in the channels list for this device set
    +        WorkspaceInfo body = ; // WorkspaceInfo | Destination workspace information
    +        try {
    +            SuccessResponse result = apiInstance.devicesetChannelWorkspacePut(deviceSetIndex, channelIndex, body);
    +            System.out.println(result);
    +        } catch (ApiException e) {
    +            System.err.println("Exception when calling DeviceSetApi#devicesetChannelWorkspacePut");
    +            e.printStackTrace();
    +        }
    +    }
    +}
    +
    + +
    +
    import SWGSDRangel.api.DeviceSetApi;
    +
    +public class DeviceSetApiExample {
    +
    +    public static void main(String[] args) {
    +        DeviceSetApi apiInstance = new DeviceSetApi();
    +        Integer deviceSetIndex = 56; // Integer | Index of device set in the device set list
    +        Integer channelIndex = 56; // Integer | Index of the channel in the channels list for this device set
    +        WorkspaceInfo body = ; // WorkspaceInfo | Destination workspace information
    +        try {
    +            SuccessResponse result = apiInstance.devicesetChannelWorkspacePut(deviceSetIndex, channelIndex, body);
    +            System.out.println(result);
    +        } catch (ApiException e) {
    +            System.err.println("Exception when calling DeviceSetApi#devicesetChannelWorkspacePut");
    +            e.printStackTrace();
    +        }
    +    }
    +}
    +
    + +
    +
    Integer *deviceSetIndex = 56; // Index of device set in the device set list
    +Integer *channelIndex = 56; // Index of the channel in the channels list for this device set
    +WorkspaceInfo *body = ; // Destination workspace information
    +
    +DeviceSetApi *apiInstance = [[DeviceSetApi alloc] init];
    +
    +[apiInstance devicesetChannelWorkspacePutWith:deviceSetIndex
    +    channelIndex:channelIndex
    +    body:body
    +              completionHandler: ^(SuccessResponse output, NSError* error) {
    +                            if (output) {
    +                                NSLog(@"%@", output);
    +                            }
    +                            if (error) {
    +                                NSLog(@"Error: %@", error);
    +                            }
    +                        }];
    +
    +
    + +
    +
    var SdRangel = require('sd_rangel');
    +
    +var api = new SdRangel.DeviceSetApi()
    +
    +var deviceSetIndex = 56; // {Integer} Index of device set in the device set list
    +
    +var channelIndex = 56; // {Integer} Index of the channel in the channels list for this device set
    +
    +var body = ; // {WorkspaceInfo} Destination workspace information
    +
    +
    +var callback = function(error, data, response) {
    +  if (error) {
    +    console.error(error);
    +  } else {
    +    console.log('API called successfully. Returned data: ' + data);
    +  }
    +};
    +api.devicesetChannelWorkspacePut(deviceSetIndex, channelIndex, body, callback);
    +
    +
    + + +
    +
    using System;
    +using System.Diagnostics;
    +using SWGSDRangel.Api;
    +using SWGSDRangel.Client;
    +using SWGSDRangel.Model;
    +
    +namespace Example
    +{
    +    public class devicesetChannelWorkspacePutExample
    +    {
    +        public void main()
    +        {
    +            
    +            var apiInstance = new DeviceSetApi();
    +            var deviceSetIndex = 56;  // Integer | Index of device set in the device set list
    +            var channelIndex = 56;  // Integer | Index of the channel in the channels list for this device set
    +            var body = new WorkspaceInfo(); // WorkspaceInfo | Destination workspace information
    +
    +            try
    +            {
    +                SuccessResponse result = apiInstance.devicesetChannelWorkspacePut(deviceSetIndex, channelIndex, body);
    +                Debug.WriteLine(result);
    +            }
    +            catch (Exception e)
    +            {
    +                Debug.Print("Exception when calling DeviceSetApi.devicesetChannelWorkspacePut: " + e.Message );
    +            }
    +        }
    +    }
    +}
    +
    +
    + +
    +
    <?php
    +require_once(__DIR__ . '/vendor/autoload.php');
    +
    +$api_instance = new Swagger\Client\Api\DeviceSetApi();
    +$deviceSetIndex = 56; // Integer | Index of device set in the device set list
    +$channelIndex = 56; // Integer | Index of the channel in the channels list for this device set
    +$body = ; // WorkspaceInfo | Destination workspace information
    +
    +try {
    +    $result = $api_instance->devicesetChannelWorkspacePut($deviceSetIndex, $channelIndex, $body);
    +    print_r($result);
    +} catch (Exception $e) {
    +    echo 'Exception when calling DeviceSetApi->devicesetChannelWorkspacePut: ', $e->getMessage(), PHP_EOL;
    +}
    +?>
    +
    + +
    +
    use Data::Dumper;
    +use SWGSDRangel::Configuration;
    +use SWGSDRangel::DeviceSetApi;
    +
    +my $api_instance = SWGSDRangel::DeviceSetApi->new();
    +my $deviceSetIndex = 56; # Integer | Index of device set in the device set list
    +my $channelIndex = 56; # Integer | Index of the channel in the channels list for this device set
    +my $body = SWGSDRangel::Object::WorkspaceInfo->new(); # WorkspaceInfo | Destination workspace information
    +
    +eval { 
    +    my $result = $api_instance->devicesetChannelWorkspacePut(deviceSetIndex => $deviceSetIndex, channelIndex => $channelIndex, body => $body);
    +    print Dumper($result);
    +};
    +if ($@) {
    +    warn "Exception when calling DeviceSetApi->devicesetChannelWorkspacePut: $@\n";
    +}
    +
    + +
    +
    from __future__ import print_statement
    +import time
    +import swagger_sdrangel
    +from swagger_sdrangel.rest import ApiException
    +from pprint import pprint
    +
    +# create an instance of the API class
    +api_instance = swagger_sdrangel.DeviceSetApi()
    +deviceSetIndex = 56 # Integer | Index of device set in the device set list
    +channelIndex = 56 # Integer | Index of the channel in the channels list for this device set
    +body =  # WorkspaceInfo | Destination workspace information
    +
    +try: 
    +    api_response = api_instance.deviceset_channel_workspace_put(deviceSetIndex, channelIndex, body)
    +    pprint(api_response)
    +except ApiException as e:
    +    print("Exception when calling DeviceSetApi->devicesetChannelWorkspacePut: %s\n" % e)
    +
    +
    + +

    Parameters

    + +
    Path parameters
    + + + + + + + + + + + + + +
    NameDescription
    deviceSetIndex* + + +
    +
    +
    + + Integer + + +
    + Index of device set in the device set list +
    +
    +
    + Required +
    +
    +
    +
    channelIndex* + + +
    +
    +
    + + Integer + + +
    + Index of the channel in the channels list for this device set +
    +
    +
    + Required +
    +
    +
    +
    + + +
    Body parameters
    + + + + + + + + + +
    NameDescription
    body * + + + +
    +
    + + + +

    Responses

    +

    Status: 202 - Message to perform action was sent successfully

    + + + +
    +
    +
    + +
    + +
    +
    + +

    Status: 400 - Invalid device set or workspace index

    + + + +
    +
    +
    + +
    + +
    +
    + +

    Status: 500 - Error

    + + + +
    +
    +
    + +
    + +
    +
    + +

    Status: 501 - Function not implemented

    + + + +
    +
    +
    + +
    + +
    +
    + +
    +
    +
    @@ -19696,7 +20684,7 @@ $(document).ready(function() {

    Responses

    -

    Status: 202 - On successful semdomg of the message it returns the details of the device being set

    +

    Status: 202 - On successful semding of the message it returns the details of the device being set

    + +
    + + +

    Status: 400 - Invalid device set index

    + + + +
    +
    +
    + +
    + +
    +
    + +

    Status: 500 - Error

    + + + +
    +
    +
    + +
    + +
    +
    + +

    Status: 501 - Function not implemented

    + + + +
    +
    +
    + +
    + +
    +
    + + + +
    +
    +
    +
    +

    devicesetDeviceWorkspacePut

    +

    +
    +
    +
    +

    +

    move device UI to workspace (GUI)

    +

    +
    +
    /sdrangel/deviceset/{deviceSetIndex}/device/workspace
    +

    +

    Usage and SDK Samples

    +

    + + +
    +
    +
    curl -X PUT "http://localhost/sdrangel/deviceset/{deviceSetIndex}/device/workspace"
    +
    +
    +
    import SWGSDRangel.*;
    +import SWGSDRangel.auth.*;
    +import SWGSDRangel.model.*;
    +import SWGSDRangel.api.DeviceSetApi;
    +
    +import java.io.File;
    +import java.util.*;
    +
    +public class DeviceSetApiExample {
    +
    +    public static void main(String[] args) {
    +        
    +        DeviceSetApi apiInstance = new DeviceSetApi();
    +        Integer deviceSetIndex = 56; // Integer | Index of device set in the device set list
    +        WorkspaceInfo body = ; // WorkspaceInfo | Destination workspace information
    +        try {
    +            SuccessResponse result = apiInstance.devicesetDeviceWorkspacePut(deviceSetIndex, body);
    +            System.out.println(result);
    +        } catch (ApiException e) {
    +            System.err.println("Exception when calling DeviceSetApi#devicesetDeviceWorkspacePut");
    +            e.printStackTrace();
    +        }
    +    }
    +}
    +
    + +
    +
    import SWGSDRangel.api.DeviceSetApi;
    +
    +public class DeviceSetApiExample {
    +
    +    public static void main(String[] args) {
    +        DeviceSetApi apiInstance = new DeviceSetApi();
    +        Integer deviceSetIndex = 56; // Integer | Index of device set in the device set list
    +        WorkspaceInfo body = ; // WorkspaceInfo | Destination workspace information
    +        try {
    +            SuccessResponse result = apiInstance.devicesetDeviceWorkspacePut(deviceSetIndex, body);
    +            System.out.println(result);
    +        } catch (ApiException e) {
    +            System.err.println("Exception when calling DeviceSetApi#devicesetDeviceWorkspacePut");
    +            e.printStackTrace();
    +        }
    +    }
    +}
    +
    + +
    +
    Integer *deviceSetIndex = 56; // Index of device set in the device set list
    +WorkspaceInfo *body = ; // Destination workspace information
    +
    +DeviceSetApi *apiInstance = [[DeviceSetApi alloc] init];
    +
    +[apiInstance devicesetDeviceWorkspacePutWith:deviceSetIndex
    +    body:body
    +              completionHandler: ^(SuccessResponse output, NSError* error) {
    +                            if (output) {
    +                                NSLog(@"%@", output);
    +                            }
    +                            if (error) {
    +                                NSLog(@"Error: %@", error);
    +                            }
    +                        }];
    +
    +
    + +
    +
    var SdRangel = require('sd_rangel');
    +
    +var api = new SdRangel.DeviceSetApi()
    +
    +var deviceSetIndex = 56; // {Integer} Index of device set in the device set list
    +
    +var body = ; // {WorkspaceInfo} Destination workspace information
    +
    +
    +var callback = function(error, data, response) {
    +  if (error) {
    +    console.error(error);
    +  } else {
    +    console.log('API called successfully. Returned data: ' + data);
    +  }
    +};
    +api.devicesetDeviceWorkspacePut(deviceSetIndex, body, callback);
    +
    +
    + + +
    +
    using System;
    +using System.Diagnostics;
    +using SWGSDRangel.Api;
    +using SWGSDRangel.Client;
    +using SWGSDRangel.Model;
    +
    +namespace Example
    +{
    +    public class devicesetDeviceWorkspacePutExample
    +    {
    +        public void main()
    +        {
    +            
    +            var apiInstance = new DeviceSetApi();
    +            var deviceSetIndex = 56;  // Integer | Index of device set in the device set list
    +            var body = new WorkspaceInfo(); // WorkspaceInfo | Destination workspace information
    +
    +            try
    +            {
    +                SuccessResponse result = apiInstance.devicesetDeviceWorkspacePut(deviceSetIndex, body);
    +                Debug.WriteLine(result);
    +            }
    +            catch (Exception e)
    +            {
    +                Debug.Print("Exception when calling DeviceSetApi.devicesetDeviceWorkspacePut: " + e.Message );
    +            }
    +        }
    +    }
    +}
    +
    +
    + +
    +
    <?php
    +require_once(__DIR__ . '/vendor/autoload.php');
    +
    +$api_instance = new Swagger\Client\Api\DeviceSetApi();
    +$deviceSetIndex = 56; // Integer | Index of device set in the device set list
    +$body = ; // WorkspaceInfo | Destination workspace information
    +
    +try {
    +    $result = $api_instance->devicesetDeviceWorkspacePut($deviceSetIndex, $body);
    +    print_r($result);
    +} catch (Exception $e) {
    +    echo 'Exception when calling DeviceSetApi->devicesetDeviceWorkspacePut: ', $e->getMessage(), PHP_EOL;
    +}
    +?>
    +
    + +
    +
    use Data::Dumper;
    +use SWGSDRangel::Configuration;
    +use SWGSDRangel::DeviceSetApi;
    +
    +my $api_instance = SWGSDRangel::DeviceSetApi->new();
    +my $deviceSetIndex = 56; # Integer | Index of device set in the device set list
    +my $body = SWGSDRangel::Object::WorkspaceInfo->new(); # WorkspaceInfo | Destination workspace information
    +
    +eval { 
    +    my $result = $api_instance->devicesetDeviceWorkspacePut(deviceSetIndex => $deviceSetIndex, body => $body);
    +    print Dumper($result);
    +};
    +if ($@) {
    +    warn "Exception when calling DeviceSetApi->devicesetDeviceWorkspacePut: $@\n";
    +}
    +
    + +
    +
    from __future__ import print_statement
    +import time
    +import swagger_sdrangel
    +from swagger_sdrangel.rest import ApiException
    +from pprint import pprint
    +
    +# create an instance of the API class
    +api_instance = swagger_sdrangel.DeviceSetApi()
    +deviceSetIndex = 56 # Integer | Index of device set in the device set list
    +body =  # WorkspaceInfo | Destination workspace information
    +
    +try: 
    +    api_response = api_instance.deviceset_device_workspace_put(deviceSetIndex, body)
    +    pprint(api_response)
    +except ApiException as e:
    +    print("Exception when calling DeviceSetApi->devicesetDeviceWorkspacePut: %s\n" % e)
    +
    +
    + +

    Parameters

    + +
    Path parameters
    + + + + + + + + + +
    NameDescription
    deviceSetIndex* + + +
    +
    +
    + + Integer + + +
    + Index of device set in the device set list +
    +
    +
    + Required +
    +
    +
    +
    + + +
    Body parameters
    + + + + + + + + + +
    NameDescription
    body * + + + +
    +
    + + + +

    Responses

    +

    Status: 202 - Message to perform action was sent successfully

    + + + +
    +
    +
    + +
    + +
    +
    + +

    Status: 400 - Invalid device set or workspace index

    + + + +
    +
    +
    + +
    + +
    +
    + +

    Status: 500 - Error

    + + + +
    +
    +
    + +
    + +
    +
    + +

    Status: 501 - Function not implemented

    + + + +
    +
    +
    + +
    + +
    +
    + +
    +
    +
    @@ -28387,6 +30263,894 @@ $(document).ready(function() {

    +
    +
    +
    +

    devicesetSpectrumWorkspaceGet

    +

    +
    +
    +
    +

    +

    Get main spectrum UI workspace information (GUI)

    +

    +
    +
    /sdrangel/deviceset/{deviceSetIndex}/spectrum/workspace
    +

    +

    Usage and SDK Samples

    +

    + + +
    +
    +
    curl -X GET "http://localhost/sdrangel/deviceset/{deviceSetIndex}/spectrum/workspace"
    +
    +
    +
    import SWGSDRangel.*;
    +import SWGSDRangel.auth.*;
    +import SWGSDRangel.model.*;
    +import SWGSDRangel.api.DeviceSetApi;
    +
    +import java.io.File;
    +import java.util.*;
    +
    +public class DeviceSetApiExample {
    +
    +    public static void main(String[] args) {
    +        
    +        DeviceSetApi apiInstance = new DeviceSetApi();
    +        Integer deviceSetIndex = 56; // Integer | Index of device set in the device set list
    +        try {
    +            WorkspaceInfo result = apiInstance.devicesetSpectrumWorkspaceGet(deviceSetIndex);
    +            System.out.println(result);
    +        } catch (ApiException e) {
    +            System.err.println("Exception when calling DeviceSetApi#devicesetSpectrumWorkspaceGet");
    +            e.printStackTrace();
    +        }
    +    }
    +}
    +
    + +
    +
    import SWGSDRangel.api.DeviceSetApi;
    +
    +public class DeviceSetApiExample {
    +
    +    public static void main(String[] args) {
    +        DeviceSetApi apiInstance = new DeviceSetApi();
    +        Integer deviceSetIndex = 56; // Integer | Index of device set in the device set list
    +        try {
    +            WorkspaceInfo result = apiInstance.devicesetSpectrumWorkspaceGet(deviceSetIndex);
    +            System.out.println(result);
    +        } catch (ApiException e) {
    +            System.err.println("Exception when calling DeviceSetApi#devicesetSpectrumWorkspaceGet");
    +            e.printStackTrace();
    +        }
    +    }
    +}
    +
    + +
    +
    Integer *deviceSetIndex = 56; // Index of device set in the device set list
    +
    +DeviceSetApi *apiInstance = [[DeviceSetApi alloc] init];
    +
    +[apiInstance devicesetSpectrumWorkspaceGetWith:deviceSetIndex
    +              completionHandler: ^(WorkspaceInfo output, NSError* error) {
    +                            if (output) {
    +                                NSLog(@"%@", output);
    +                            }
    +                            if (error) {
    +                                NSLog(@"Error: %@", error);
    +                            }
    +                        }];
    +
    +
    + +
    +
    var SdRangel = require('sd_rangel');
    +
    +var api = new SdRangel.DeviceSetApi()
    +
    +var deviceSetIndex = 56; // {Integer} Index of device set in the device set list
    +
    +
    +var callback = function(error, data, response) {
    +  if (error) {
    +    console.error(error);
    +  } else {
    +    console.log('API called successfully. Returned data: ' + data);
    +  }
    +};
    +api.devicesetSpectrumWorkspaceGet(deviceSetIndex, callback);
    +
    +
    + + +
    +
    using System;
    +using System.Diagnostics;
    +using SWGSDRangel.Api;
    +using SWGSDRangel.Client;
    +using SWGSDRangel.Model;
    +
    +namespace Example
    +{
    +    public class devicesetSpectrumWorkspaceGetExample
    +    {
    +        public void main()
    +        {
    +            
    +            var apiInstance = new DeviceSetApi();
    +            var deviceSetIndex = 56;  // Integer | Index of device set in the device set list
    +
    +            try
    +            {
    +                WorkspaceInfo result = apiInstance.devicesetSpectrumWorkspaceGet(deviceSetIndex);
    +                Debug.WriteLine(result);
    +            }
    +            catch (Exception e)
    +            {
    +                Debug.Print("Exception when calling DeviceSetApi.devicesetSpectrumWorkspaceGet: " + e.Message );
    +            }
    +        }
    +    }
    +}
    +
    +
    + +
    +
    <?php
    +require_once(__DIR__ . '/vendor/autoload.php');
    +
    +$api_instance = new Swagger\Client\Api\DeviceSetApi();
    +$deviceSetIndex = 56; // Integer | Index of device set in the device set list
    +
    +try {
    +    $result = $api_instance->devicesetSpectrumWorkspaceGet($deviceSetIndex);
    +    print_r($result);
    +} catch (Exception $e) {
    +    echo 'Exception when calling DeviceSetApi->devicesetSpectrumWorkspaceGet: ', $e->getMessage(), PHP_EOL;
    +}
    +?>
    +
    + +
    +
    use Data::Dumper;
    +use SWGSDRangel::Configuration;
    +use SWGSDRangel::DeviceSetApi;
    +
    +my $api_instance = SWGSDRangel::DeviceSetApi->new();
    +my $deviceSetIndex = 56; # Integer | Index of device set in the device set list
    +
    +eval { 
    +    my $result = $api_instance->devicesetSpectrumWorkspaceGet(deviceSetIndex => $deviceSetIndex);
    +    print Dumper($result);
    +};
    +if ($@) {
    +    warn "Exception when calling DeviceSetApi->devicesetSpectrumWorkspaceGet: $@\n";
    +}
    +
    + +
    +
    from __future__ import print_statement
    +import time
    +import swagger_sdrangel
    +from swagger_sdrangel.rest import ApiException
    +from pprint import pprint
    +
    +# create an instance of the API class
    +api_instance = swagger_sdrangel.DeviceSetApi()
    +deviceSetIndex = 56 # Integer | Index of device set in the device set list
    +
    +try: 
    +    api_response = api_instance.deviceset_spectrum_workspace_get(deviceSetIndex)
    +    pprint(api_response)
    +except ApiException as e:
    +    print("Exception when calling DeviceSetApi->devicesetSpectrumWorkspaceGet: %s\n" % e)
    +
    +
    + +

    Parameters

    + +
    Path parameters
    + + + + + + + + + +
    NameDescription
    deviceSetIndex* + + +
    +
    +
    + + Integer + + +
    + Index of device set in the device set list +
    +
    +
    + Required +
    +
    +
    +
    + + + + + +

    Responses

    +

    Status: 200 - On success return main spectrum UI workspace index

    + + + +
    +
    +
    + +
    + +
    +
    + +

    Status: 404 - Invalid index

    + + + +
    +
    +
    + +
    + +
    +
    + +

    Status: 500 - Error

    + + + +
    +
    +
    + +
    + +
    +
    + +

    Status: 501 - Function not implemented

    + + + +
    +
    +
    + +
    + +
    +
    + +
    +
    +
    +
    +
    +
    +

    devicesetSpectrumWorkspacePut

    +

    +
    +
    +
    +

    +

    move main spectrum UI to workspace (GUI)

    +

    +
    +
    /sdrangel/deviceset/{deviceSetIndex}/spectrum/workspace
    +

    +

    Usage and SDK Samples

    +

    + + +
    +
    +
    curl -X PUT "http://localhost/sdrangel/deviceset/{deviceSetIndex}/spectrum/workspace"
    +
    +
    +
    import SWGSDRangel.*;
    +import SWGSDRangel.auth.*;
    +import SWGSDRangel.model.*;
    +import SWGSDRangel.api.DeviceSetApi;
    +
    +import java.io.File;
    +import java.util.*;
    +
    +public class DeviceSetApiExample {
    +
    +    public static void main(String[] args) {
    +        
    +        DeviceSetApi apiInstance = new DeviceSetApi();
    +        Integer deviceSetIndex = 56; // Integer | Index of device set in the device set list
    +        WorkspaceInfo body = ; // WorkspaceInfo | Destination workspace information
    +        try {
    +            SuccessResponse result = apiInstance.devicesetSpectrumWorkspacePut(deviceSetIndex, body);
    +            System.out.println(result);
    +        } catch (ApiException e) {
    +            System.err.println("Exception when calling DeviceSetApi#devicesetSpectrumWorkspacePut");
    +            e.printStackTrace();
    +        }
    +    }
    +}
    +
    + +
    +
    import SWGSDRangel.api.DeviceSetApi;
    +
    +public class DeviceSetApiExample {
    +
    +    public static void main(String[] args) {
    +        DeviceSetApi apiInstance = new DeviceSetApi();
    +        Integer deviceSetIndex = 56; // Integer | Index of device set in the device set list
    +        WorkspaceInfo body = ; // WorkspaceInfo | Destination workspace information
    +        try {
    +            SuccessResponse result = apiInstance.devicesetSpectrumWorkspacePut(deviceSetIndex, body);
    +            System.out.println(result);
    +        } catch (ApiException e) {
    +            System.err.println("Exception when calling DeviceSetApi#devicesetSpectrumWorkspacePut");
    +            e.printStackTrace();
    +        }
    +    }
    +}
    +
    + +
    +
    Integer *deviceSetIndex = 56; // Index of device set in the device set list
    +WorkspaceInfo *body = ; // Destination workspace information
    +
    +DeviceSetApi *apiInstance = [[DeviceSetApi alloc] init];
    +
    +[apiInstance devicesetSpectrumWorkspacePutWith:deviceSetIndex
    +    body:body
    +              completionHandler: ^(SuccessResponse output, NSError* error) {
    +                            if (output) {
    +                                NSLog(@"%@", output);
    +                            }
    +                            if (error) {
    +                                NSLog(@"Error: %@", error);
    +                            }
    +                        }];
    +
    +
    + +
    +
    var SdRangel = require('sd_rangel');
    +
    +var api = new SdRangel.DeviceSetApi()
    +
    +var deviceSetIndex = 56; // {Integer} Index of device set in the device set list
    +
    +var body = ; // {WorkspaceInfo} Destination workspace information
    +
    +
    +var callback = function(error, data, response) {
    +  if (error) {
    +    console.error(error);
    +  } else {
    +    console.log('API called successfully. Returned data: ' + data);
    +  }
    +};
    +api.devicesetSpectrumWorkspacePut(deviceSetIndex, body, callback);
    +
    +
    + + +
    +
    using System;
    +using System.Diagnostics;
    +using SWGSDRangel.Api;
    +using SWGSDRangel.Client;
    +using SWGSDRangel.Model;
    +
    +namespace Example
    +{
    +    public class devicesetSpectrumWorkspacePutExample
    +    {
    +        public void main()
    +        {
    +            
    +            var apiInstance = new DeviceSetApi();
    +            var deviceSetIndex = 56;  // Integer | Index of device set in the device set list
    +            var body = new WorkspaceInfo(); // WorkspaceInfo | Destination workspace information
    +
    +            try
    +            {
    +                SuccessResponse result = apiInstance.devicesetSpectrumWorkspacePut(deviceSetIndex, body);
    +                Debug.WriteLine(result);
    +            }
    +            catch (Exception e)
    +            {
    +                Debug.Print("Exception when calling DeviceSetApi.devicesetSpectrumWorkspacePut: " + e.Message );
    +            }
    +        }
    +    }
    +}
    +
    +
    + +
    +
    <?php
    +require_once(__DIR__ . '/vendor/autoload.php');
    +
    +$api_instance = new Swagger\Client\Api\DeviceSetApi();
    +$deviceSetIndex = 56; // Integer | Index of device set in the device set list
    +$body = ; // WorkspaceInfo | Destination workspace information
    +
    +try {
    +    $result = $api_instance->devicesetSpectrumWorkspacePut($deviceSetIndex, $body);
    +    print_r($result);
    +} catch (Exception $e) {
    +    echo 'Exception when calling DeviceSetApi->devicesetSpectrumWorkspacePut: ', $e->getMessage(), PHP_EOL;
    +}
    +?>
    +
    + +
    +
    use Data::Dumper;
    +use SWGSDRangel::Configuration;
    +use SWGSDRangel::DeviceSetApi;
    +
    +my $api_instance = SWGSDRangel::DeviceSetApi->new();
    +my $deviceSetIndex = 56; # Integer | Index of device set in the device set list
    +my $body = SWGSDRangel::Object::WorkspaceInfo->new(); # WorkspaceInfo | Destination workspace information
    +
    +eval { 
    +    my $result = $api_instance->devicesetSpectrumWorkspacePut(deviceSetIndex => $deviceSetIndex, body => $body);
    +    print Dumper($result);
    +};
    +if ($@) {
    +    warn "Exception when calling DeviceSetApi->devicesetSpectrumWorkspacePut: $@\n";
    +}
    +
    + +
    +
    from __future__ import print_statement
    +import time
    +import swagger_sdrangel
    +from swagger_sdrangel.rest import ApiException
    +from pprint import pprint
    +
    +# create an instance of the API class
    +api_instance = swagger_sdrangel.DeviceSetApi()
    +deviceSetIndex = 56 # Integer | Index of device set in the device set list
    +body =  # WorkspaceInfo | Destination workspace information
    +
    +try: 
    +    api_response = api_instance.deviceset_spectrum_workspace_put(deviceSetIndex, body)
    +    pprint(api_response)
    +except ApiException as e:
    +    print("Exception when calling DeviceSetApi->devicesetSpectrumWorkspacePut: %s\n" % e)
    +
    +
    + +

    Parameters

    + +
    Path parameters
    + + + + + + + + + +
    NameDescription
    deviceSetIndex* + + +
    +
    +
    + + Integer + + +
    + Index of device set in the device set list +
    +
    +
    + Required +
    +
    +
    +
    + + +
    Body parameters
    + + + + + + + + + +
    NameDescription
    body * + + + +
    +
    + + + +

    Responses

    +

    Status: 202 - Message to perform action was sent successfully

    + + + +
    +
    +
    + +
    + +
    +
    + +

    Status: 400 - Invalid device set or workspace index

    + + + +
    +
    +
    + +
    + +
    +
    + +

    Status: 500 - Error

    + + + +
    +
    +
    + +
    + +
    +
    + +

    Status: 501 - Function not implemented

    + + + +
    +
    +
    + +
    + +
    +
    + +
    +
    +
    @@ -33790,6 +36554,894 @@ $(document).ready(function() {

    +
    +
    +
    +

    featuresetFeatureWorkspaceGet

    +

    +
    +
    +
    +

    +

    get feature UI workspace information (GUI)

    +

    +
    +
    /sdrangel/featureset/feature/{featureIndex}/workspace
    +

    +

    Usage and SDK Samples

    +

    + + +
    +
    +
    curl -X GET "http://localhost/sdrangel/featureset/feature/{featureIndex}/workspace"
    +
    +
    +
    import SWGSDRangel.*;
    +import SWGSDRangel.auth.*;
    +import SWGSDRangel.model.*;
    +import SWGSDRangel.api.FeatureSetApi;
    +
    +import java.io.File;
    +import java.util.*;
    +
    +public class FeatureSetApiExample {
    +
    +    public static void main(String[] args) {
    +        
    +        FeatureSetApi apiInstance = new FeatureSetApi();
    +        Integer featureIndex = 56; // Integer | Index of feature in the feature set
    +        try {
    +            WorkspaceInfo result = apiInstance.featuresetFeatureWorkspaceGet(featureIndex);
    +            System.out.println(result);
    +        } catch (ApiException e) {
    +            System.err.println("Exception when calling FeatureSetApi#featuresetFeatureWorkspaceGet");
    +            e.printStackTrace();
    +        }
    +    }
    +}
    +
    + +
    +
    import SWGSDRangel.api.FeatureSetApi;
    +
    +public class FeatureSetApiExample {
    +
    +    public static void main(String[] args) {
    +        FeatureSetApi apiInstance = new FeatureSetApi();
    +        Integer featureIndex = 56; // Integer | Index of feature in the feature set
    +        try {
    +            WorkspaceInfo result = apiInstance.featuresetFeatureWorkspaceGet(featureIndex);
    +            System.out.println(result);
    +        } catch (ApiException e) {
    +            System.err.println("Exception when calling FeatureSetApi#featuresetFeatureWorkspaceGet");
    +            e.printStackTrace();
    +        }
    +    }
    +}
    +
    + +
    +
    Integer *featureIndex = 56; // Index of feature in the feature set
    +
    +FeatureSetApi *apiInstance = [[FeatureSetApi alloc] init];
    +
    +[apiInstance featuresetFeatureWorkspaceGetWith:featureIndex
    +              completionHandler: ^(WorkspaceInfo output, NSError* error) {
    +                            if (output) {
    +                                NSLog(@"%@", output);
    +                            }
    +                            if (error) {
    +                                NSLog(@"Error: %@", error);
    +                            }
    +                        }];
    +
    +
    + +
    +
    var SdRangel = require('sd_rangel');
    +
    +var api = new SdRangel.FeatureSetApi()
    +
    +var featureIndex = 56; // {Integer} Index of feature in the feature set
    +
    +
    +var callback = function(error, data, response) {
    +  if (error) {
    +    console.error(error);
    +  } else {
    +    console.log('API called successfully. Returned data: ' + data);
    +  }
    +};
    +api.featuresetFeatureWorkspaceGet(featureIndex, callback);
    +
    +
    + + +
    +
    using System;
    +using System.Diagnostics;
    +using SWGSDRangel.Api;
    +using SWGSDRangel.Client;
    +using SWGSDRangel.Model;
    +
    +namespace Example
    +{
    +    public class featuresetFeatureWorkspaceGetExample
    +    {
    +        public void main()
    +        {
    +            
    +            var apiInstance = new FeatureSetApi();
    +            var featureIndex = 56;  // Integer | Index of feature in the feature set
    +
    +            try
    +            {
    +                WorkspaceInfo result = apiInstance.featuresetFeatureWorkspaceGet(featureIndex);
    +                Debug.WriteLine(result);
    +            }
    +            catch (Exception e)
    +            {
    +                Debug.Print("Exception when calling FeatureSetApi.featuresetFeatureWorkspaceGet: " + e.Message );
    +            }
    +        }
    +    }
    +}
    +
    +
    + +
    +
    <?php
    +require_once(__DIR__ . '/vendor/autoload.php');
    +
    +$api_instance = new Swagger\Client\Api\FeatureSetApi();
    +$featureIndex = 56; // Integer | Index of feature in the feature set
    +
    +try {
    +    $result = $api_instance->featuresetFeatureWorkspaceGet($featureIndex);
    +    print_r($result);
    +} catch (Exception $e) {
    +    echo 'Exception when calling FeatureSetApi->featuresetFeatureWorkspaceGet: ', $e->getMessage(), PHP_EOL;
    +}
    +?>
    +
    + +
    +
    use Data::Dumper;
    +use SWGSDRangel::Configuration;
    +use SWGSDRangel::FeatureSetApi;
    +
    +my $api_instance = SWGSDRangel::FeatureSetApi->new();
    +my $featureIndex = 56; # Integer | Index of feature in the feature set
    +
    +eval { 
    +    my $result = $api_instance->featuresetFeatureWorkspaceGet(featureIndex => $featureIndex);
    +    print Dumper($result);
    +};
    +if ($@) {
    +    warn "Exception when calling FeatureSetApi->featuresetFeatureWorkspaceGet: $@\n";
    +}
    +
    + +
    +
    from __future__ import print_statement
    +import time
    +import swagger_sdrangel
    +from swagger_sdrangel.rest import ApiException
    +from pprint import pprint
    +
    +# create an instance of the API class
    +api_instance = swagger_sdrangel.FeatureSetApi()
    +featureIndex = 56 # Integer | Index of feature in the feature set
    +
    +try: 
    +    api_response = api_instance.featureset_feature_workspace_get(featureIndex)
    +    pprint(api_response)
    +except ApiException as e:
    +    print("Exception when calling FeatureSetApi->featuresetFeatureWorkspaceGet: %s\n" % e)
    +
    +
    + +

    Parameters

    + +
    Path parameters
    + + + + + + + + + +
    NameDescription
    featureIndex* + + +
    +
    +
    + + Integer + + +
    + Index of feature in the feature set +
    +
    +
    + Required +
    +
    +
    +
    + + + + + +

    Responses

    +

    Status: 200 - On success return workspace information

    + + + +
    +
    +
    + +
    + +
    +
    + +

    Status: 400 - Invalid feature set index

    + + + +
    +
    +
    + +
    + +
    +
    + +

    Status: 500 - Error

    + + + +
    +
    +
    + +
    + +
    +
    + +

    Status: 501 - Function not implemented

    + + + +
    +
    +
    + +
    + +
    +
    + +
    +
    +
    +
    +
    +
    +

    featuresetFeatureWorkspacePut

    +

    +
    +
    +
    +

    +

    move feature UI to workspace (GUI)

    +

    +
    +
    /sdrangel/featureset/feature/{featureIndex}/workspace
    +

    +

    Usage and SDK Samples

    +

    + + +
    +
    +
    curl -X PUT "http://localhost/sdrangel/featureset/feature/{featureIndex}/workspace"
    +
    +
    +
    import SWGSDRangel.*;
    +import SWGSDRangel.auth.*;
    +import SWGSDRangel.model.*;
    +import SWGSDRangel.api.FeatureSetApi;
    +
    +import java.io.File;
    +import java.util.*;
    +
    +public class FeatureSetApiExample {
    +
    +    public static void main(String[] args) {
    +        
    +        FeatureSetApi apiInstance = new FeatureSetApi();
    +        Integer featureIndex = 56; // Integer | Index of feature in the feature set
    +        WorkspaceInfo body = ; // WorkspaceInfo | Destination workspace information
    +        try {
    +            SuccessResponse result = apiInstance.featuresetFeatureWorkspacePut(featureIndex, body);
    +            System.out.println(result);
    +        } catch (ApiException e) {
    +            System.err.println("Exception when calling FeatureSetApi#featuresetFeatureWorkspacePut");
    +            e.printStackTrace();
    +        }
    +    }
    +}
    +
    + +
    +
    import SWGSDRangel.api.FeatureSetApi;
    +
    +public class FeatureSetApiExample {
    +
    +    public static void main(String[] args) {
    +        FeatureSetApi apiInstance = new FeatureSetApi();
    +        Integer featureIndex = 56; // Integer | Index of feature in the feature set
    +        WorkspaceInfo body = ; // WorkspaceInfo | Destination workspace information
    +        try {
    +            SuccessResponse result = apiInstance.featuresetFeatureWorkspacePut(featureIndex, body);
    +            System.out.println(result);
    +        } catch (ApiException e) {
    +            System.err.println("Exception when calling FeatureSetApi#featuresetFeatureWorkspacePut");
    +            e.printStackTrace();
    +        }
    +    }
    +}
    +
    + +
    +
    Integer *featureIndex = 56; // Index of feature in the feature set
    +WorkspaceInfo *body = ; // Destination workspace information
    +
    +FeatureSetApi *apiInstance = [[FeatureSetApi alloc] init];
    +
    +[apiInstance featuresetFeatureWorkspacePutWith:featureIndex
    +    body:body
    +              completionHandler: ^(SuccessResponse output, NSError* error) {
    +                            if (output) {
    +                                NSLog(@"%@", output);
    +                            }
    +                            if (error) {
    +                                NSLog(@"Error: %@", error);
    +                            }
    +                        }];
    +
    +
    + +
    +
    var SdRangel = require('sd_rangel');
    +
    +var api = new SdRangel.FeatureSetApi()
    +
    +var featureIndex = 56; // {Integer} Index of feature in the feature set
    +
    +var body = ; // {WorkspaceInfo} Destination workspace information
    +
    +
    +var callback = function(error, data, response) {
    +  if (error) {
    +    console.error(error);
    +  } else {
    +    console.log('API called successfully. Returned data: ' + data);
    +  }
    +};
    +api.featuresetFeatureWorkspacePut(featureIndex, body, callback);
    +
    +
    + + +
    +
    using System;
    +using System.Diagnostics;
    +using SWGSDRangel.Api;
    +using SWGSDRangel.Client;
    +using SWGSDRangel.Model;
    +
    +namespace Example
    +{
    +    public class featuresetFeatureWorkspacePutExample
    +    {
    +        public void main()
    +        {
    +            
    +            var apiInstance = new FeatureSetApi();
    +            var featureIndex = 56;  // Integer | Index of feature in the feature set
    +            var body = new WorkspaceInfo(); // WorkspaceInfo | Destination workspace information
    +
    +            try
    +            {
    +                SuccessResponse result = apiInstance.featuresetFeatureWorkspacePut(featureIndex, body);
    +                Debug.WriteLine(result);
    +            }
    +            catch (Exception e)
    +            {
    +                Debug.Print("Exception when calling FeatureSetApi.featuresetFeatureWorkspacePut: " + e.Message );
    +            }
    +        }
    +    }
    +}
    +
    +
    + +
    +
    <?php
    +require_once(__DIR__ . '/vendor/autoload.php');
    +
    +$api_instance = new Swagger\Client\Api\FeatureSetApi();
    +$featureIndex = 56; // Integer | Index of feature in the feature set
    +$body = ; // WorkspaceInfo | Destination workspace information
    +
    +try {
    +    $result = $api_instance->featuresetFeatureWorkspacePut($featureIndex, $body);
    +    print_r($result);
    +} catch (Exception $e) {
    +    echo 'Exception when calling FeatureSetApi->featuresetFeatureWorkspacePut: ', $e->getMessage(), PHP_EOL;
    +}
    +?>
    +
    + +
    +
    use Data::Dumper;
    +use SWGSDRangel::Configuration;
    +use SWGSDRangel::FeatureSetApi;
    +
    +my $api_instance = SWGSDRangel::FeatureSetApi->new();
    +my $featureIndex = 56; # Integer | Index of feature in the feature set
    +my $body = SWGSDRangel::Object::WorkspaceInfo->new(); # WorkspaceInfo | Destination workspace information
    +
    +eval { 
    +    my $result = $api_instance->featuresetFeatureWorkspacePut(featureIndex => $featureIndex, body => $body);
    +    print Dumper($result);
    +};
    +if ($@) {
    +    warn "Exception when calling FeatureSetApi->featuresetFeatureWorkspacePut: $@\n";
    +}
    +
    + +
    +
    from __future__ import print_statement
    +import time
    +import swagger_sdrangel
    +from swagger_sdrangel.rest import ApiException
    +from pprint import pprint
    +
    +# create an instance of the API class
    +api_instance = swagger_sdrangel.FeatureSetApi()
    +featureIndex = 56 # Integer | Index of feature in the feature set
    +body =  # WorkspaceInfo | Destination workspace information
    +
    +try: 
    +    api_response = api_instance.featureset_feature_workspace_put(featureIndex, body)
    +    pprint(api_response)
    +except ApiException as e:
    +    print("Exception when calling FeatureSetApi->featuresetFeatureWorkspacePut: %s\n" % e)
    +
    +
    + +

    Parameters

    + +
    Path parameters
    + + + + + + + + + +
    NameDescription
    featureIndex* + + +
    +
    +
    + + Integer + + +
    + Index of feature in the feature set +
    +
    +
    + Required +
    +
    +
    +
    + + +
    Body parameters
    + + + + + + + + + +
    NameDescription
    body * + + + +
    +
    + + + +

    Responses

    +

    Status: 202 - Message to perform action was sent successfully

    + + + +
    +
    +
    + +
    + +
    +
    + +

    Status: 400 - Invalid device set or workspace index

    + + + +
    +
    +
    + +
    + +
    +
    + +

    Status: 500 - Error

    + + + +
    +
    +
    + +
    + +
    +
    + +

    Status: 501 - Function not implemented

    + + + +
    +
    +
    + +
    + +
    +
    + +
    +
    +
    @@ -56503,7 +60155,7 @@ except ApiException as e:
    - Generated 2022-05-09T01:10:06.823+02:00 + Generated 2022-05-13T21:51:44.838+02:00
    diff --git a/swagger/sdrangel/code/qt5/client/SWGDeviceSetApi.cpp b/swagger/sdrangel/code/qt5/client/SWGDeviceSetApi.cpp index 65fca7131..5b63d957a 100644 --- a/swagger/sdrangel/code/qt5/client/SWGDeviceSetApi.cpp +++ b/swagger/sdrangel/code/qt5/client/SWGDeviceSetApi.cpp @@ -430,6 +430,121 @@ SWGDeviceSetApi::devicesetChannelSettingsPutCallback(SWGHttpRequestWorker * work } } +void +SWGDeviceSetApi::devicesetChannelWorkspaceGet(qint32 device_set_index, qint32 channel_index) { + QString fullPath; + fullPath.append(this->host).append(this->basePath).append("/sdrangel/deviceset/{deviceSetIndex}/channel/{channelIndex}/workspace"); + + QString device_set_indexPathParam("{"); device_set_indexPathParam.append("deviceSetIndex").append("}"); + fullPath.replace(device_set_indexPathParam, stringValue(device_set_index)); + QString channel_indexPathParam("{"); channel_indexPathParam.append("channelIndex").append("}"); + fullPath.replace(channel_indexPathParam, stringValue(channel_index)); + + + SWGHttpRequestWorker *worker = new SWGHttpRequestWorker(); + SWGHttpRequestInput input(fullPath, "GET"); + + + + + + foreach(QString key, this->defaultHeaders.keys()) { + input.headers.insert(key, this->defaultHeaders.value(key)); + } + + connect(worker, + &SWGHttpRequestWorker::on_execution_finished, + this, + &SWGDeviceSetApi::devicesetChannelWorkspaceGetCallback); + + worker->execute(&input); +} + +void +SWGDeviceSetApi::devicesetChannelWorkspaceGetCallback(SWGHttpRequestWorker * worker) { + QString msg; + QString error_str = worker->error_str; + QNetworkReply::NetworkError error_type = worker->error_type; + + if (worker->error_type == QNetworkReply::NoError) { + msg = QString("Success! %1 bytes").arg(worker->response.length()); + } + else { + msg = "Error: " + worker->error_str; + } + + + QString json(worker->response); + SWGWorkspaceInfo* output = static_cast(create(json, QString("SWGWorkspaceInfo"))); + worker->deleteLater(); + + if (worker->error_type == QNetworkReply::NoError) { + emit devicesetChannelWorkspaceGetSignal(output); + } else { + emit devicesetChannelWorkspaceGetSignalE(output, error_type, error_str); + emit devicesetChannelWorkspaceGetSignalEFull(worker, error_type, error_str); + } +} + +void +SWGDeviceSetApi::devicesetChannelWorkspacePut(qint32 device_set_index, qint32 channel_index, SWGWorkspaceInfo& body) { + QString fullPath; + fullPath.append(this->host).append(this->basePath).append("/sdrangel/deviceset/{deviceSetIndex}/channel/{channelIndex}/workspace"); + + QString device_set_indexPathParam("{"); device_set_indexPathParam.append("deviceSetIndex").append("}"); + fullPath.replace(device_set_indexPathParam, stringValue(device_set_index)); + QString channel_indexPathParam("{"); channel_indexPathParam.append("channelIndex").append("}"); + fullPath.replace(channel_indexPathParam, stringValue(channel_index)); + + + SWGHttpRequestWorker *worker = new SWGHttpRequestWorker(); + SWGHttpRequestInput input(fullPath, "PUT"); + + + + QString output = body.asJson(); + input.request_body.append(output.toUtf8()); + + + + foreach(QString key, this->defaultHeaders.keys()) { + input.headers.insert(key, this->defaultHeaders.value(key)); + } + + connect(worker, + &SWGHttpRequestWorker::on_execution_finished, + this, + &SWGDeviceSetApi::devicesetChannelWorkspacePutCallback); + + worker->execute(&input); +} + +void +SWGDeviceSetApi::devicesetChannelWorkspacePutCallback(SWGHttpRequestWorker * worker) { + QString msg; + QString error_str = worker->error_str; + QNetworkReply::NetworkError error_type = worker->error_type; + + if (worker->error_type == QNetworkReply::NoError) { + msg = QString("Success! %1 bytes").arg(worker->response.length()); + } + else { + msg = "Error: " + worker->error_str; + } + + + QString json(worker->response); + SWGSuccessResponse* output = static_cast(create(json, QString("SWGSuccessResponse"))); + worker->deleteLater(); + + if (worker->error_type == QNetworkReply::NoError) { + emit devicesetChannelWorkspacePutSignal(output); + } else { + emit devicesetChannelWorkspacePutSignalE(output, error_type, error_str); + emit devicesetChannelWorkspacePutSignalEFull(worker, error_type, error_str); + } +} + void SWGDeviceSetApi::devicesetChannelsReportGet(qint32 device_set_index) { QString fullPath; @@ -1162,6 +1277,117 @@ SWGDeviceSetApi::devicesetDeviceSubsystemRunPostCallback(SWGHttpRequestWorker * } } +void +SWGDeviceSetApi::devicesetDeviceWorkspaceGet(qint32 device_set_index) { + QString fullPath; + fullPath.append(this->host).append(this->basePath).append("/sdrangel/deviceset/{deviceSetIndex}/device/workspace"); + + QString device_set_indexPathParam("{"); device_set_indexPathParam.append("deviceSetIndex").append("}"); + fullPath.replace(device_set_indexPathParam, stringValue(device_set_index)); + + + SWGHttpRequestWorker *worker = new SWGHttpRequestWorker(); + SWGHttpRequestInput input(fullPath, "GET"); + + + + + + foreach(QString key, this->defaultHeaders.keys()) { + input.headers.insert(key, this->defaultHeaders.value(key)); + } + + connect(worker, + &SWGHttpRequestWorker::on_execution_finished, + this, + &SWGDeviceSetApi::devicesetDeviceWorkspaceGetCallback); + + worker->execute(&input); +} + +void +SWGDeviceSetApi::devicesetDeviceWorkspaceGetCallback(SWGHttpRequestWorker * worker) { + QString msg; + QString error_str = worker->error_str; + QNetworkReply::NetworkError error_type = worker->error_type; + + if (worker->error_type == QNetworkReply::NoError) { + msg = QString("Success! %1 bytes").arg(worker->response.length()); + } + else { + msg = "Error: " + worker->error_str; + } + + + QString json(worker->response); + SWGWorkspaceInfo* output = static_cast(create(json, QString("SWGWorkspaceInfo"))); + worker->deleteLater(); + + if (worker->error_type == QNetworkReply::NoError) { + emit devicesetDeviceWorkspaceGetSignal(output); + } else { + emit devicesetDeviceWorkspaceGetSignalE(output, error_type, error_str); + emit devicesetDeviceWorkspaceGetSignalEFull(worker, error_type, error_str); + } +} + +void +SWGDeviceSetApi::devicesetDeviceWorkspacePut(qint32 device_set_index, SWGWorkspaceInfo& body) { + QString fullPath; + fullPath.append(this->host).append(this->basePath).append("/sdrangel/deviceset/{deviceSetIndex}/device/workspace"); + + QString device_set_indexPathParam("{"); device_set_indexPathParam.append("deviceSetIndex").append("}"); + fullPath.replace(device_set_indexPathParam, stringValue(device_set_index)); + + + SWGHttpRequestWorker *worker = new SWGHttpRequestWorker(); + SWGHttpRequestInput input(fullPath, "PUT"); + + + + QString output = body.asJson(); + input.request_body.append(output.toUtf8()); + + + + foreach(QString key, this->defaultHeaders.keys()) { + input.headers.insert(key, this->defaultHeaders.value(key)); + } + + connect(worker, + &SWGHttpRequestWorker::on_execution_finished, + this, + &SWGDeviceSetApi::devicesetDeviceWorkspacePutCallback); + + worker->execute(&input); +} + +void +SWGDeviceSetApi::devicesetDeviceWorkspacePutCallback(SWGHttpRequestWorker * worker) { + QString msg; + QString error_str = worker->error_str; + QNetworkReply::NetworkError error_type = worker->error_type; + + if (worker->error_type == QNetworkReply::NoError) { + msg = QString("Success! %1 bytes").arg(worker->response.length()); + } + else { + msg = "Error: " + worker->error_str; + } + + + QString json(worker->response); + SWGSuccessResponse* output = static_cast(create(json, QString("SWGSuccessResponse"))); + worker->deleteLater(); + + if (worker->error_type == QNetworkReply::NoError) { + emit devicesetDeviceWorkspacePutSignal(output); + } else { + emit devicesetDeviceWorkspacePutSignalE(output, error_type, error_str); + emit devicesetDeviceWorkspacePutSignalEFull(worker, error_type, error_str); + } +} + void SWGDeviceSetApi::devicesetFocusPatch(qint32 device_set_index) { QString fullPath; @@ -1600,6 +1826,117 @@ SWGDeviceSetApi::devicesetSpectrumSettingsPutCallback(SWGHttpRequestWorker * wor } } +void +SWGDeviceSetApi::devicesetSpectrumWorkspaceGet(qint32 device_set_index) { + QString fullPath; + fullPath.append(this->host).append(this->basePath).append("/sdrangel/deviceset/{deviceSetIndex}/spectrum/workspace"); + + QString device_set_indexPathParam("{"); device_set_indexPathParam.append("deviceSetIndex").append("}"); + fullPath.replace(device_set_indexPathParam, stringValue(device_set_index)); + + + SWGHttpRequestWorker *worker = new SWGHttpRequestWorker(); + SWGHttpRequestInput input(fullPath, "GET"); + + + + + + foreach(QString key, this->defaultHeaders.keys()) { + input.headers.insert(key, this->defaultHeaders.value(key)); + } + + connect(worker, + &SWGHttpRequestWorker::on_execution_finished, + this, + &SWGDeviceSetApi::devicesetSpectrumWorkspaceGetCallback); + + worker->execute(&input); +} + +void +SWGDeviceSetApi::devicesetSpectrumWorkspaceGetCallback(SWGHttpRequestWorker * worker) { + QString msg; + QString error_str = worker->error_str; + QNetworkReply::NetworkError error_type = worker->error_type; + + if (worker->error_type == QNetworkReply::NoError) { + msg = QString("Success! %1 bytes").arg(worker->response.length()); + } + else { + msg = "Error: " + worker->error_str; + } + + + QString json(worker->response); + SWGWorkspaceInfo* output = static_cast(create(json, QString("SWGWorkspaceInfo"))); + worker->deleteLater(); + + if (worker->error_type == QNetworkReply::NoError) { + emit devicesetSpectrumWorkspaceGetSignal(output); + } else { + emit devicesetSpectrumWorkspaceGetSignalE(output, error_type, error_str); + emit devicesetSpectrumWorkspaceGetSignalEFull(worker, error_type, error_str); + } +} + +void +SWGDeviceSetApi::devicesetSpectrumWorkspacePut(qint32 device_set_index, SWGWorkspaceInfo& body) { + QString fullPath; + fullPath.append(this->host).append(this->basePath).append("/sdrangel/deviceset/{deviceSetIndex}/spectrum/workspace"); + + QString device_set_indexPathParam("{"); device_set_indexPathParam.append("deviceSetIndex").append("}"); + fullPath.replace(device_set_indexPathParam, stringValue(device_set_index)); + + + SWGHttpRequestWorker *worker = new SWGHttpRequestWorker(); + SWGHttpRequestInput input(fullPath, "PUT"); + + + + QString output = body.asJson(); + input.request_body.append(output.toUtf8()); + + + + foreach(QString key, this->defaultHeaders.keys()) { + input.headers.insert(key, this->defaultHeaders.value(key)); + } + + connect(worker, + &SWGHttpRequestWorker::on_execution_finished, + this, + &SWGDeviceSetApi::devicesetSpectrumWorkspacePutCallback); + + worker->execute(&input); +} + +void +SWGDeviceSetApi::devicesetSpectrumWorkspacePutCallback(SWGHttpRequestWorker * worker) { + QString msg; + QString error_str = worker->error_str; + QNetworkReply::NetworkError error_type = worker->error_type; + + if (worker->error_type == QNetworkReply::NoError) { + msg = QString("Success! %1 bytes").arg(worker->response.length()); + } + else { + msg = "Error: " + worker->error_str; + } + + + QString json(worker->response); + SWGSuccessResponse* output = static_cast(create(json, QString("SWGSuccessResponse"))); + worker->deleteLater(); + + if (worker->error_type == QNetworkReply::NoError) { + emit devicesetSpectrumWorkspacePutSignal(output); + } else { + emit devicesetSpectrumWorkspacePutSignalE(output, error_type, error_str); + emit devicesetSpectrumWorkspacePutSignalEFull(worker, error_type, error_str); + } +} + void SWGDeviceSetApi::instanceDeviceSetDelete() { QString fullPath; diff --git a/swagger/sdrangel/code/qt5/client/SWGDeviceSetApi.h b/swagger/sdrangel/code/qt5/client/SWGDeviceSetApi.h index d0cf0a1d8..808cb02cc 100644 --- a/swagger/sdrangel/code/qt5/client/SWGDeviceSetApi.h +++ b/swagger/sdrangel/code/qt5/client/SWGDeviceSetApi.h @@ -29,6 +29,7 @@ #include "SWGGLSpectrum.h" #include "SWGSpectrumServer.h" #include "SWGSuccessResponse.h" +#include "SWGWorkspaceInfo.h" #include @@ -53,6 +54,8 @@ public: void devicesetChannelSettingsGet(qint32 device_set_index, qint32 channel_index); void devicesetChannelSettingsPatch(qint32 device_set_index, qint32 channel_index, SWGChannelSettings& body); void devicesetChannelSettingsPut(qint32 device_set_index, qint32 channel_index, SWGChannelSettings& body); + void devicesetChannelWorkspaceGet(qint32 device_set_index, qint32 channel_index); + void devicesetChannelWorkspacePut(qint32 device_set_index, qint32 channel_index, SWGWorkspaceInfo& body); void devicesetChannelsReportGet(qint32 device_set_index); void devicesetDeviceActionsPost(qint32 device_set_index, SWGDeviceActions& body); void devicesetDevicePut(qint32 device_set_index, SWGDeviceListItem& body); @@ -66,6 +69,8 @@ public: void devicesetDeviceSubsystemRunDelete(qint32 device_set_index, qint32 subsystem_index, SWGDeviceSettings& body); void devicesetDeviceSubsystemRunGet(qint32 device_set_index, qint32 subsystem_index); void devicesetDeviceSubsystemRunPost(qint32 device_set_index, qint32 subsystem_index, SWGDeviceSettings& body); + void devicesetDeviceWorkspaceGet(qint32 device_set_index); + void devicesetDeviceWorkspacePut(qint32 device_set_index, SWGWorkspaceInfo& body); void devicesetFocusPatch(qint32 device_set_index); void devicesetGet(qint32 device_set_index); void devicesetSpectrumServerDelete(qint32 device_set_index); @@ -74,6 +79,8 @@ public: void devicesetSpectrumSettingsGet(qint32 device_set_index); void devicesetSpectrumSettingsPatch(qint32 device_set_index, SWGGLSpectrum& body); void devicesetSpectrumSettingsPut(qint32 device_set_index, SWGGLSpectrum& body); + void devicesetSpectrumWorkspaceGet(qint32 device_set_index); + void devicesetSpectrumWorkspacePut(qint32 device_set_index, SWGWorkspaceInfo& body); void instanceDeviceSetDelete(); void instanceDeviceSetPost(qint32 direction); @@ -85,6 +92,8 @@ private: void devicesetChannelSettingsGetCallback (SWGHttpRequestWorker * worker); void devicesetChannelSettingsPatchCallback (SWGHttpRequestWorker * worker); void devicesetChannelSettingsPutCallback (SWGHttpRequestWorker * worker); + void devicesetChannelWorkspaceGetCallback (SWGHttpRequestWorker * worker); + void devicesetChannelWorkspacePutCallback (SWGHttpRequestWorker * worker); void devicesetChannelsReportGetCallback (SWGHttpRequestWorker * worker); void devicesetDeviceActionsPostCallback (SWGHttpRequestWorker * worker); void devicesetDevicePutCallback (SWGHttpRequestWorker * worker); @@ -98,6 +107,8 @@ private: void devicesetDeviceSubsystemRunDeleteCallback (SWGHttpRequestWorker * worker); void devicesetDeviceSubsystemRunGetCallback (SWGHttpRequestWorker * worker); void devicesetDeviceSubsystemRunPostCallback (SWGHttpRequestWorker * worker); + void devicesetDeviceWorkspaceGetCallback (SWGHttpRequestWorker * worker); + void devicesetDeviceWorkspacePutCallback (SWGHttpRequestWorker * worker); void devicesetFocusPatchCallback (SWGHttpRequestWorker * worker); void devicesetGetCallback (SWGHttpRequestWorker * worker); void devicesetSpectrumServerDeleteCallback (SWGHttpRequestWorker * worker); @@ -106,6 +117,8 @@ private: void devicesetSpectrumSettingsGetCallback (SWGHttpRequestWorker * worker); void devicesetSpectrumSettingsPatchCallback (SWGHttpRequestWorker * worker); void devicesetSpectrumSettingsPutCallback (SWGHttpRequestWorker * worker); + void devicesetSpectrumWorkspaceGetCallback (SWGHttpRequestWorker * worker); + void devicesetSpectrumWorkspacePutCallback (SWGHttpRequestWorker * worker); void instanceDeviceSetDeleteCallback (SWGHttpRequestWorker * worker); void instanceDeviceSetPostCallback (SWGHttpRequestWorker * worker); @@ -117,6 +130,8 @@ signals: void devicesetChannelSettingsGetSignal(SWGChannelSettings* summary); void devicesetChannelSettingsPatchSignal(SWGChannelSettings* summary); void devicesetChannelSettingsPutSignal(SWGChannelSettings* summary); + void devicesetChannelWorkspaceGetSignal(SWGWorkspaceInfo* summary); + void devicesetChannelWorkspacePutSignal(SWGSuccessResponse* summary); void devicesetChannelsReportGetSignal(SWGChannelsDetail* summary); void devicesetDeviceActionsPostSignal(SWGSuccessResponse* summary); void devicesetDevicePutSignal(SWGDeviceListItem* summary); @@ -130,6 +145,8 @@ signals: void devicesetDeviceSubsystemRunDeleteSignal(SWGDeviceState* summary); void devicesetDeviceSubsystemRunGetSignal(SWGDeviceState* summary); void devicesetDeviceSubsystemRunPostSignal(SWGDeviceState* summary); + void devicesetDeviceWorkspaceGetSignal(SWGWorkspaceInfo* summary); + void devicesetDeviceWorkspacePutSignal(SWGSuccessResponse* summary); void devicesetFocusPatchSignal(SWGSuccessResponse* summary); void devicesetGetSignal(SWGDeviceSet* summary); void devicesetSpectrumServerDeleteSignal(SWGSuccessResponse* summary); @@ -138,6 +155,8 @@ signals: void devicesetSpectrumSettingsGetSignal(SWGGLSpectrum* summary); void devicesetSpectrumSettingsPatchSignal(SWGGLSpectrum* summary); void devicesetSpectrumSettingsPutSignal(SWGGLSpectrum* summary); + void devicesetSpectrumWorkspaceGetSignal(SWGWorkspaceInfo* summary); + void devicesetSpectrumWorkspacePutSignal(SWGSuccessResponse* summary); void instanceDeviceSetDeleteSignal(SWGSuccessResponse* summary); void instanceDeviceSetPostSignal(SWGSuccessResponse* summary); @@ -148,6 +167,8 @@ signals: void devicesetChannelSettingsGetSignalE(SWGChannelSettings* summary, QNetworkReply::NetworkError error_type, QString& error_str); void devicesetChannelSettingsPatchSignalE(SWGChannelSettings* summary, QNetworkReply::NetworkError error_type, QString& error_str); void devicesetChannelSettingsPutSignalE(SWGChannelSettings* summary, QNetworkReply::NetworkError error_type, QString& error_str); + void devicesetChannelWorkspaceGetSignalE(SWGWorkspaceInfo* summary, QNetworkReply::NetworkError error_type, QString& error_str); + void devicesetChannelWorkspacePutSignalE(SWGSuccessResponse* summary, QNetworkReply::NetworkError error_type, QString& error_str); void devicesetChannelsReportGetSignalE(SWGChannelsDetail* summary, QNetworkReply::NetworkError error_type, QString& error_str); void devicesetDeviceActionsPostSignalE(SWGSuccessResponse* summary, QNetworkReply::NetworkError error_type, QString& error_str); void devicesetDevicePutSignalE(SWGDeviceListItem* summary, QNetworkReply::NetworkError error_type, QString& error_str); @@ -161,6 +182,8 @@ signals: void devicesetDeviceSubsystemRunDeleteSignalE(SWGDeviceState* summary, QNetworkReply::NetworkError error_type, QString& error_str); void devicesetDeviceSubsystemRunGetSignalE(SWGDeviceState* summary, QNetworkReply::NetworkError error_type, QString& error_str); void devicesetDeviceSubsystemRunPostSignalE(SWGDeviceState* summary, QNetworkReply::NetworkError error_type, QString& error_str); + void devicesetDeviceWorkspaceGetSignalE(SWGWorkspaceInfo* summary, QNetworkReply::NetworkError error_type, QString& error_str); + void devicesetDeviceWorkspacePutSignalE(SWGSuccessResponse* summary, QNetworkReply::NetworkError error_type, QString& error_str); void devicesetFocusPatchSignalE(SWGSuccessResponse* summary, QNetworkReply::NetworkError error_type, QString& error_str); void devicesetGetSignalE(SWGDeviceSet* summary, QNetworkReply::NetworkError error_type, QString& error_str); void devicesetSpectrumServerDeleteSignalE(SWGSuccessResponse* summary, QNetworkReply::NetworkError error_type, QString& error_str); @@ -169,6 +192,8 @@ signals: void devicesetSpectrumSettingsGetSignalE(SWGGLSpectrum* summary, QNetworkReply::NetworkError error_type, QString& error_str); void devicesetSpectrumSettingsPatchSignalE(SWGGLSpectrum* summary, QNetworkReply::NetworkError error_type, QString& error_str); void devicesetSpectrumSettingsPutSignalE(SWGGLSpectrum* summary, QNetworkReply::NetworkError error_type, QString& error_str); + void devicesetSpectrumWorkspaceGetSignalE(SWGWorkspaceInfo* summary, QNetworkReply::NetworkError error_type, QString& error_str); + void devicesetSpectrumWorkspacePutSignalE(SWGSuccessResponse* summary, QNetworkReply::NetworkError error_type, QString& error_str); void instanceDeviceSetDeleteSignalE(SWGSuccessResponse* summary, QNetworkReply::NetworkError error_type, QString& error_str); void instanceDeviceSetPostSignalE(SWGSuccessResponse* summary, QNetworkReply::NetworkError error_type, QString& error_str); @@ -179,6 +204,8 @@ signals: void devicesetChannelSettingsGetSignalEFull(SWGHttpRequestWorker* worker, QNetworkReply::NetworkError error_type, QString& error_str); void devicesetChannelSettingsPatchSignalEFull(SWGHttpRequestWorker* worker, QNetworkReply::NetworkError error_type, QString& error_str); void devicesetChannelSettingsPutSignalEFull(SWGHttpRequestWorker* worker, QNetworkReply::NetworkError error_type, QString& error_str); + void devicesetChannelWorkspaceGetSignalEFull(SWGHttpRequestWorker* worker, QNetworkReply::NetworkError error_type, QString& error_str); + void devicesetChannelWorkspacePutSignalEFull(SWGHttpRequestWorker* worker, QNetworkReply::NetworkError error_type, QString& error_str); void devicesetChannelsReportGetSignalEFull(SWGHttpRequestWorker* worker, QNetworkReply::NetworkError error_type, QString& error_str); void devicesetDeviceActionsPostSignalEFull(SWGHttpRequestWorker* worker, QNetworkReply::NetworkError error_type, QString& error_str); void devicesetDevicePutSignalEFull(SWGHttpRequestWorker* worker, QNetworkReply::NetworkError error_type, QString& error_str); @@ -192,6 +219,8 @@ signals: void devicesetDeviceSubsystemRunDeleteSignalEFull(SWGHttpRequestWorker* worker, QNetworkReply::NetworkError error_type, QString& error_str); void devicesetDeviceSubsystemRunGetSignalEFull(SWGHttpRequestWorker* worker, QNetworkReply::NetworkError error_type, QString& error_str); void devicesetDeviceSubsystemRunPostSignalEFull(SWGHttpRequestWorker* worker, QNetworkReply::NetworkError error_type, QString& error_str); + void devicesetDeviceWorkspaceGetSignalEFull(SWGHttpRequestWorker* worker, QNetworkReply::NetworkError error_type, QString& error_str); + void devicesetDeviceWorkspacePutSignalEFull(SWGHttpRequestWorker* worker, QNetworkReply::NetworkError error_type, QString& error_str); void devicesetFocusPatchSignalEFull(SWGHttpRequestWorker* worker, QNetworkReply::NetworkError error_type, QString& error_str); void devicesetGetSignalEFull(SWGHttpRequestWorker* worker, QNetworkReply::NetworkError error_type, QString& error_str); void devicesetSpectrumServerDeleteSignalEFull(SWGHttpRequestWorker* worker, QNetworkReply::NetworkError error_type, QString& error_str); @@ -200,6 +229,8 @@ signals: void devicesetSpectrumSettingsGetSignalEFull(SWGHttpRequestWorker* worker, QNetworkReply::NetworkError error_type, QString& error_str); void devicesetSpectrumSettingsPatchSignalEFull(SWGHttpRequestWorker* worker, QNetworkReply::NetworkError error_type, QString& error_str); void devicesetSpectrumSettingsPutSignalEFull(SWGHttpRequestWorker* worker, QNetworkReply::NetworkError error_type, QString& error_str); + void devicesetSpectrumWorkspaceGetSignalEFull(SWGHttpRequestWorker* worker, QNetworkReply::NetworkError error_type, QString& error_str); + void devicesetSpectrumWorkspacePutSignalEFull(SWGHttpRequestWorker* worker, QNetworkReply::NetworkError error_type, QString& error_str); void instanceDeviceSetDeleteSignalEFull(SWGHttpRequestWorker* worker, QNetworkReply::NetworkError error_type, QString& error_str); void instanceDeviceSetPostSignalEFull(SWGHttpRequestWorker* worker, QNetworkReply::NetworkError error_type, QString& error_str); diff --git a/swagger/sdrangel/code/qt5/client/SWGFeatureSetApi.cpp b/swagger/sdrangel/code/qt5/client/SWGFeatureSetApi.cpp index c35ae5cd4..9f66210ba 100644 --- a/swagger/sdrangel/code/qt5/client/SWGFeatureSetApi.cpp +++ b/swagger/sdrangel/code/qt5/client/SWGFeatureSetApi.cpp @@ -578,6 +578,117 @@ SWGFeatureSetApi::featuresetFeatureSettingsPatchCallback(SWGHttpRequestWorker * } } +void +SWGFeatureSetApi::featuresetFeatureWorkspaceGet(qint32 feature_index) { + QString fullPath; + fullPath.append(this->host).append(this->basePath).append("/sdrangel/featureset/feature/{featureIndex}/workspace"); + + QString feature_indexPathParam("{"); feature_indexPathParam.append("featureIndex").append("}"); + fullPath.replace(feature_indexPathParam, stringValue(feature_index)); + + + SWGHttpRequestWorker *worker = new SWGHttpRequestWorker(); + SWGHttpRequestInput input(fullPath, "GET"); + + + + + + foreach(QString key, this->defaultHeaders.keys()) { + input.headers.insert(key, this->defaultHeaders.value(key)); + } + + connect(worker, + &SWGHttpRequestWorker::on_execution_finished, + this, + &SWGFeatureSetApi::featuresetFeatureWorkspaceGetCallback); + + worker->execute(&input); +} + +void +SWGFeatureSetApi::featuresetFeatureWorkspaceGetCallback(SWGHttpRequestWorker * worker) { + QString msg; + QString error_str = worker->error_str; + QNetworkReply::NetworkError error_type = worker->error_type; + + if (worker->error_type == QNetworkReply::NoError) { + msg = QString("Success! %1 bytes").arg(worker->response.length()); + } + else { + msg = "Error: " + worker->error_str; + } + + + QString json(worker->response); + SWGWorkspaceInfo* output = static_cast(create(json, QString("SWGWorkspaceInfo"))); + worker->deleteLater(); + + if (worker->error_type == QNetworkReply::NoError) { + emit featuresetFeatureWorkspaceGetSignal(output); + } else { + emit featuresetFeatureWorkspaceGetSignalE(output, error_type, error_str); + emit featuresetFeatureWorkspaceGetSignalEFull(worker, error_type, error_str); + } +} + +void +SWGFeatureSetApi::featuresetFeatureWorkspacePut(qint32 feature_index, SWGWorkspaceInfo& body) { + QString fullPath; + fullPath.append(this->host).append(this->basePath).append("/sdrangel/featureset/feature/{featureIndex}/workspace"); + + QString feature_indexPathParam("{"); feature_indexPathParam.append("featureIndex").append("}"); + fullPath.replace(feature_indexPathParam, stringValue(feature_index)); + + + SWGHttpRequestWorker *worker = new SWGHttpRequestWorker(); + SWGHttpRequestInput input(fullPath, "PUT"); + + + + QString output = body.asJson(); + input.request_body.append(output.toUtf8()); + + + + foreach(QString key, this->defaultHeaders.keys()) { + input.headers.insert(key, this->defaultHeaders.value(key)); + } + + connect(worker, + &SWGHttpRequestWorker::on_execution_finished, + this, + &SWGFeatureSetApi::featuresetFeatureWorkspacePutCallback); + + worker->execute(&input); +} + +void +SWGFeatureSetApi::featuresetFeatureWorkspacePutCallback(SWGHttpRequestWorker * worker) { + QString msg; + QString error_str = worker->error_str; + QNetworkReply::NetworkError error_type = worker->error_type; + + if (worker->error_type == QNetworkReply::NoError) { + msg = QString("Success! %1 bytes").arg(worker->response.length()); + } + else { + msg = "Error: " + worker->error_str; + } + + + QString json(worker->response); + SWGSuccessResponse* output = static_cast(create(json, QString("SWGSuccessResponse"))); + worker->deleteLater(); + + if (worker->error_type == QNetworkReply::NoError) { + emit featuresetFeatureWorkspacePutSignal(output); + } else { + emit featuresetFeatureWorkspacePutSignalE(output, error_type, error_str); + emit featuresetFeatureWorkspacePutSignalEFull(worker, error_type, error_str); + } +} + void SWGFeatureSetApi::featuresetGet() { QString fullPath; diff --git a/swagger/sdrangel/code/qt5/client/SWGFeatureSetApi.h b/swagger/sdrangel/code/qt5/client/SWGFeatureSetApi.h index 712d54f44..69356b9de 100644 --- a/swagger/sdrangel/code/qt5/client/SWGFeatureSetApi.h +++ b/swagger/sdrangel/code/qt5/client/SWGFeatureSetApi.h @@ -24,6 +24,7 @@ #include "SWGFeatureSettings.h" #include "SWGPresetIdentifier.h" #include "SWGSuccessResponse.h" +#include "SWGWorkspaceInfo.h" #include @@ -51,6 +52,8 @@ public: void featuresetFeatureRunPost(qint32 feature_index); void featuresetFeatureSettingsGet(qint32 feature_index); void featuresetFeatureSettingsPatch(qint32 feature_index, SWGFeatureSettings& body); + void featuresetFeatureWorkspaceGet(qint32 feature_index); + void featuresetFeatureWorkspacePut(qint32 feature_index, SWGWorkspaceInfo& body); void featuresetGet(); void featuresetPresetPatch(SWGFeaturePresetIdentifier& body); void featuresetPresetPost(SWGFeaturePresetIdentifier& body); @@ -67,6 +70,8 @@ private: void featuresetFeatureRunPostCallback (SWGHttpRequestWorker * worker); void featuresetFeatureSettingsGetCallback (SWGHttpRequestWorker * worker); void featuresetFeatureSettingsPatchCallback (SWGHttpRequestWorker * worker); + void featuresetFeatureWorkspaceGetCallback (SWGHttpRequestWorker * worker); + void featuresetFeatureWorkspacePutCallback (SWGHttpRequestWorker * worker); void featuresetGetCallback (SWGHttpRequestWorker * worker); void featuresetPresetPatchCallback (SWGHttpRequestWorker * worker); void featuresetPresetPostCallback (SWGHttpRequestWorker * worker); @@ -83,6 +88,8 @@ signals: void featuresetFeatureRunPostSignal(SWGDeviceState* summary); void featuresetFeatureSettingsGetSignal(SWGFeatureSettings* summary); void featuresetFeatureSettingsPatchSignal(SWGFeatureSettings* summary); + void featuresetFeatureWorkspaceGetSignal(SWGWorkspaceInfo* summary); + void featuresetFeatureWorkspacePutSignal(SWGSuccessResponse* summary); void featuresetGetSignal(SWGFeatureSet* summary); void featuresetPresetPatchSignal(SWGFeaturePresetIdentifier* summary); void featuresetPresetPostSignal(SWGPresetIdentifier* summary); @@ -98,6 +105,8 @@ signals: void featuresetFeatureRunPostSignalE(SWGDeviceState* summary, QNetworkReply::NetworkError error_type, QString& error_str); void featuresetFeatureSettingsGetSignalE(SWGFeatureSettings* summary, QNetworkReply::NetworkError error_type, QString& error_str); void featuresetFeatureSettingsPatchSignalE(SWGFeatureSettings* summary, QNetworkReply::NetworkError error_type, QString& error_str); + void featuresetFeatureWorkspaceGetSignalE(SWGWorkspaceInfo* summary, QNetworkReply::NetworkError error_type, QString& error_str); + void featuresetFeatureWorkspacePutSignalE(SWGSuccessResponse* summary, QNetworkReply::NetworkError error_type, QString& error_str); void featuresetGetSignalE(SWGFeatureSet* summary, QNetworkReply::NetworkError error_type, QString& error_str); void featuresetPresetPatchSignalE(SWGFeaturePresetIdentifier* summary, QNetworkReply::NetworkError error_type, QString& error_str); void featuresetPresetPostSignalE(SWGPresetIdentifier* summary, QNetworkReply::NetworkError error_type, QString& error_str); @@ -113,6 +122,8 @@ signals: void featuresetFeatureRunPostSignalEFull(SWGHttpRequestWorker* worker, QNetworkReply::NetworkError error_type, QString& error_str); void featuresetFeatureSettingsGetSignalEFull(SWGHttpRequestWorker* worker, QNetworkReply::NetworkError error_type, QString& error_str); void featuresetFeatureSettingsPatchSignalEFull(SWGHttpRequestWorker* worker, QNetworkReply::NetworkError error_type, QString& error_str); + void featuresetFeatureWorkspaceGetSignalEFull(SWGHttpRequestWorker* worker, QNetworkReply::NetworkError error_type, QString& error_str); + void featuresetFeatureWorkspacePutSignalEFull(SWGHttpRequestWorker* worker, QNetworkReply::NetworkError error_type, QString& error_str); void featuresetGetSignalEFull(SWGHttpRequestWorker* worker, QNetworkReply::NetworkError error_type, QString& error_str); void featuresetPresetPatchSignalEFull(SWGHttpRequestWorker* worker, QNetworkReply::NetworkError error_type, QString& error_str); void featuresetPresetPostSignalEFull(SWGHttpRequestWorker* worker, QNetworkReply::NetworkError error_type, QString& error_str); diff --git a/swagger/sdrangel/code/qt5/client/SWGModelFactory.h b/swagger/sdrangel/code/qt5/client/SWGModelFactory.h index 3009f2864..6cc164b54 100644 --- a/swagger/sdrangel/code/qt5/client/SWGModelFactory.h +++ b/swagger/sdrangel/code/qt5/client/SWGModelFactory.h @@ -317,6 +317,7 @@ #include "SWGWFMDemodSettings.h" #include "SWGWFMModReport.h" #include "SWGWFMModSettings.h" +#include "SWGWorkspaceInfo.h" #include "SWGXtrxInputReport.h" #include "SWGXtrxInputSettings.h" #include "SWGXtrxMIMOReport.h" @@ -1842,6 +1843,11 @@ namespace SWGSDRangel { obj->init(); return obj; } + if(QString("SWGWorkspaceInfo").compare(type) == 0) { + SWGWorkspaceInfo *obj = new SWGWorkspaceInfo(); + obj->init(); + return obj; + } if(QString("SWGXtrxInputReport").compare(type) == 0) { SWGXtrxInputReport *obj = new SWGXtrxInputReport(); obj->init(); diff --git a/swagger/sdrangel/code/qt5/client/SWGWorkspaceInfo.cpp b/swagger/sdrangel/code/qt5/client/SWGWorkspaceInfo.cpp new file mode 100644 index 000000000..80b97d873 --- /dev/null +++ b/swagger/sdrangel/code/qt5/client/SWGWorkspaceInfo.cpp @@ -0,0 +1,108 @@ +/** + * SDRangel + * This is the web REST/JSON API of SDRangel SDR software. SDRangel is an Open Source Qt5/OpenGL 3.0+ (4.3+ in Windows) GUI and server Software Defined Radio and signal analyzer in software. It supports Airspy, BladeRF, HackRF, LimeSDR, PlutoSDR, RTL-SDR, SDRplay RSP1 and FunCube --- Limitations and specifcities: * In SDRangel GUI the first Rx device set cannot be deleted. Conversely the server starts with no device sets and its number of device sets can be reduced to zero by as many calls as necessary to /sdrangel/deviceset with DELETE method. * Preset import and export from/to file is a server only feature. * Device set focus is a GUI only feature. * The following channels are not implemented (status 501 is returned): ATV and DATV demodulators, Channel Analyzer NG, LoRa demodulator * The device settings and report structures contains only the sub-structure corresponding to the device type. The DeviceSettings and DeviceReport structures documented here shows all of them but only one will be or should be present at a time * The channel settings and report structures contains only the sub-structure corresponding to the channel type. The ChannelSettings and ChannelReport structures documented here shows all of them but only one will be or should be present at a time --- + * + * OpenAPI spec version: 6.0.0 + * Contact: f4exb06@gmail.com + * + * NOTE: This class is auto generated by the swagger code generator program. + * https://github.com/swagger-api/swagger-codegen.git + * Do not edit the class manually. + */ + + +#include "SWGWorkspaceInfo.h" + +#include "SWGHelpers.h" + +#include +#include +#include +#include + +namespace SWGSDRangel { + +SWGWorkspaceInfo::SWGWorkspaceInfo(QString* json) { + init(); + this->fromJson(*json); +} + +SWGWorkspaceInfo::SWGWorkspaceInfo() { + index = 0; + m_index_isSet = false; +} + +SWGWorkspaceInfo::~SWGWorkspaceInfo() { + this->cleanup(); +} + +void +SWGWorkspaceInfo::init() { + index = 0; + m_index_isSet = false; +} + +void +SWGWorkspaceInfo::cleanup() { + +} + +SWGWorkspaceInfo* +SWGWorkspaceInfo::fromJson(QString &json) { + QByteArray array (json.toStdString().c_str()); + QJsonDocument doc = QJsonDocument::fromJson(array); + QJsonObject jsonObject = doc.object(); + this->fromJsonObject(jsonObject); + return this; +} + +void +SWGWorkspaceInfo::fromJsonObject(QJsonObject &pJson) { + ::SWGSDRangel::setValue(&index, pJson["index"], "qint32", ""); + +} + +QString +SWGWorkspaceInfo::asJson () +{ + QJsonObject* obj = this->asJsonObject(); + + QJsonDocument doc(*obj); + QByteArray bytes = doc.toJson(); + delete obj; + return QString(bytes); +} + +QJsonObject* +SWGWorkspaceInfo::asJsonObject() { + QJsonObject* obj = new QJsonObject(); + if(m_index_isSet){ + obj->insert("index", QJsonValue(index)); + } + + return obj; +} + +qint32 +SWGWorkspaceInfo::getIndex() { + return index; +} +void +SWGWorkspaceInfo::setIndex(qint32 index) { + this->index = index; + this->m_index_isSet = true; +} + + +bool +SWGWorkspaceInfo::isSet(){ + bool isObjectUpdated = false; + do{ + if(m_index_isSet){ + isObjectUpdated = true; break; + } + }while(false); + return isObjectUpdated; +} +} + diff --git a/swagger/sdrangel/code/qt5/client/SWGWorkspaceInfo.h b/swagger/sdrangel/code/qt5/client/SWGWorkspaceInfo.h new file mode 100644 index 000000000..c49005842 --- /dev/null +++ b/swagger/sdrangel/code/qt5/client/SWGWorkspaceInfo.h @@ -0,0 +1,58 @@ +/** + * SDRangel + * This is the web REST/JSON API of SDRangel SDR software. SDRangel is an Open Source Qt5/OpenGL 3.0+ (4.3+ in Windows) GUI and server Software Defined Radio and signal analyzer in software. It supports Airspy, BladeRF, HackRF, LimeSDR, PlutoSDR, RTL-SDR, SDRplay RSP1 and FunCube --- Limitations and specifcities: * In SDRangel GUI the first Rx device set cannot be deleted. Conversely the server starts with no device sets and its number of device sets can be reduced to zero by as many calls as necessary to /sdrangel/deviceset with DELETE method. * Preset import and export from/to file is a server only feature. * Device set focus is a GUI only feature. * The following channels are not implemented (status 501 is returned): ATV and DATV demodulators, Channel Analyzer NG, LoRa demodulator * The device settings and report structures contains only the sub-structure corresponding to the device type. The DeviceSettings and DeviceReport structures documented here shows all of them but only one will be or should be present at a time * The channel settings and report structures contains only the sub-structure corresponding to the channel type. The ChannelSettings and ChannelReport structures documented here shows all of them but only one will be or should be present at a time --- + * + * OpenAPI spec version: 6.0.0 + * Contact: f4exb06@gmail.com + * + * NOTE: This class is auto generated by the swagger code generator program. + * https://github.com/swagger-api/swagger-codegen.git + * Do not edit the class manually. + */ + +/* + * SWGWorkspaceInfo.h + * + * Workspace information + */ + +#ifndef SWGWorkspaceInfo_H_ +#define SWGWorkspaceInfo_H_ + +#include + + + +#include "SWGObject.h" +#include "export.h" + +namespace SWGSDRangel { + +class SWG_API SWGWorkspaceInfo: public SWGObject { +public: + SWGWorkspaceInfo(); + SWGWorkspaceInfo(QString* json); + virtual ~SWGWorkspaceInfo(); + void init(); + void cleanup(); + + virtual QString asJson () override; + virtual QJsonObject* asJsonObject() override; + virtual void fromJsonObject(QJsonObject &json) override; + virtual SWGWorkspaceInfo* fromJson(QString &jsonString) override; + + qint32 getIndex(); + void setIndex(qint32 index); + + + virtual bool isSet() override; + +private: + qint32 index; + bool m_index_isSet; + +}; + +} + +#endif /* SWGWorkspaceInfo_H_ */ From 82084740f5a88bc9b2092202edca7226e6275765 Mon Sep 17 00:00:00 2001 From: f4exb Date: Fri, 13 May 2022 22:34:01 +0200 Subject: [PATCH 111/115] Fixed soapysdr handling in Windows. Fixes #1243 --- sdrbase/plugin/pluginmanager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdrbase/plugin/pluginmanager.cpp b/sdrbase/plugin/pluginmanager.cpp index 64229029f..0f0a48fc9 100644 --- a/sdrbase/plugin/pluginmanager.cpp +++ b/sdrbase/plugin/pluginmanager.cpp @@ -217,7 +217,7 @@ void PluginManager::loadPluginsDir(const QDir& dir) { if (QLibrary::isLibrary(fileName)) { - if (!m_enableSoapy && (fileName.contains("libinputsoapysdr") || (fileName.contains("liboutputsoapysdr")))) + if (!m_enableSoapy && fileName.contains("soapysdr")) { qInfo("PluginManager::loadPluginsDir: Soapy SDR disabled skipping %s", qPrintable(fileName)); continue; From f55f830a1212e7d00fcf526556ec4becb46ccfc1 Mon Sep 17 00:00:00 2001 From: f4exb Date: Sat, 14 May 2022 06:17:32 +0200 Subject: [PATCH 112/115] Remved log2 definition to fix MSVC build --- sdrbase/dsp/spectrumvis.cpp | 7 ------- 1 file changed, 7 deletions(-) diff --git a/sdrbase/dsp/spectrumvis.cpp b/sdrbase/dsp/spectrumvis.cpp index 6b2f094a1..b5705f8cf 100644 --- a/sdrbase/dsp/spectrumvis.cpp +++ b/sdrbase/dsp/spectrumvis.cpp @@ -27,13 +27,6 @@ #include "spectrumvis.h" -#ifndef LINUX -inline double log2(double n) -{ - return log(n) / log(2.0); -} -#endif - MESSAGE_CLASS_DEFINITION(SpectrumVis::MsgConfigureSpectrumVis, Message) MESSAGE_CLASS_DEFINITION(SpectrumVis::MsgConfigureScalingFactor, Message) MESSAGE_CLASS_DEFINITION(SpectrumVis::MsgConfigureWSpectrumOpenClose, Message) From 63edbe94fa69407ed663b8a36b70407a44c51406 Mon Sep 17 00:00:00 2001 From: f4exb Date: Sat, 14 May 2022 07:07:45 +0200 Subject: [PATCH 113/115] Updated version --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 78b3cfd94..2f0a2fcde 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,7 +17,7 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON) set(sdrangel_VERSION_MAJOR "7") set(sdrangel_VERSION_MINOR "0") set(sdrangel_VERSION_PATCH "0") -set(sdrangel_VERSION_SUFFIX "alpha.3") +set(sdrangel_VERSION_SUFFIX "alpha.4") # SDRAngel cmake options option(DEBUG_OUTPUT "Print debug messages" OFF) From 11ce179f02a990898a01d3d58b3c1ea8440d0aa1 Mon Sep 17 00:00:00 2001 From: f4exb Date: Sat, 14 May 2022 11:18:09 +0200 Subject: [PATCH 114/115] Fixed device enumerator in-use indicator not reset on device set delete --- sdrgui/mainwindow.cpp | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/sdrgui/mainwindow.cpp b/sdrgui/mainwindow.cpp index 696fc8eba..000469bc4 100644 --- a/sdrgui/mainwindow.cpp +++ b/sdrgui/mainwindow.cpp @@ -342,7 +342,8 @@ void MainWindow::sampleSourceCreate( deviceAPI->setSamplingDeviceDisplayName(samplingDevice->displayedName); deviceAPI->setSamplingDevicePluginInterface(DeviceEnumerator::instance()->getRxPluginInterface(deviceIndex)); - qDebug() << "MainWindow::sampleSourceImplement:" + qDebug() << "MainWindow::sampleSourceCreate:" + << "deviceSetIndex:" << deviceSetIndex << "deviceIndex:" << deviceIndex << "hardwareId:" << samplingDevice->hardwareId << "sequence:" << samplingDevice->sequence @@ -352,7 +353,7 @@ void MainWindow::sampleSourceCreate( if (deviceAPI->getSamplingDeviceId().size() == 0) // non existent device => replace by default { - qDebug("MainWindow::sampleSourceAdd: non existent device replaced by File Input"); + qDebug("MainWindow::sampleSourceCreate: non existent device replaced by File Input"); int fileInputDeviceIndex = DeviceEnumerator::instance()->getFileInputDeviceIndex(); selectedDeviceIndex = fileInputDeviceIndex; samplingDevice = DeviceEnumerator::instance()->getRxSamplingDevice(fileInputDeviceIndex); @@ -557,7 +558,8 @@ void MainWindow::sampleSinkCreate( deviceAPI->setSamplingDeviceDisplayName(samplingDevice->displayedName); deviceAPI->setSamplingDevicePluginInterface(DeviceEnumerator::instance()->getTxPluginInterface(deviceIndex)); - qDebug() << "MainWindow::sampleSinkImplement:" + qDebug() << "MainWindow::sampleSinkCreate:" + << "deviceSetIndex:" << deviceSetIndex << "newDeviceIndex:" << deviceIndex << "hardwareId:" << samplingDevice->hardwareId << "sequence:" << samplingDevice->sequence @@ -567,7 +569,7 @@ void MainWindow::sampleSinkCreate( if (deviceAPI->getSamplingDeviceId().size() == 0) // non existent device => replace by default { - qDebug("MainWindow::sampleSinkImplement: non existent device replaced by File Sink"); + qDebug("MainWindow::sampleSinkCreate: non existent device replaced by File Sink"); int fileSinkDeviceIndex = DeviceEnumerator::instance()->getFileOutputDeviceIndex(); selectedDeviceIndex = fileSinkDeviceIndex; const PluginInterface::SamplingDevice *samplingDevice = DeviceEnumerator::instance()->getTxSamplingDevice(fileSinkDeviceIndex); @@ -780,7 +782,8 @@ void MainWindow::sampleMIMOCreate( deviceAPI->setSamplingDeviceDisplayName(samplingDevice->displayedName); deviceAPI->setSamplingDevicePluginInterface(DeviceEnumerator::instance()->getMIMOPluginInterface(deviceIndex)); - qDebug() << "MainWindow::sampleMIMOImplement:" + qDebug() << "MainWindow::sampleMIMOCreate:" + << "deviceSetIndex" << deviceSetIndex << "newDeviceIndex:" << deviceIndex << "hardwareId:" << samplingDevice->hardwareId << "sequence:" << samplingDevice->sequence @@ -790,7 +793,7 @@ void MainWindow::sampleMIMOCreate( if (deviceAPI->getSamplingDeviceId().size() == 0) // non existent device => replace by default { - qDebug("MainWindow::sampleMIMOImplement: non existent device replaced by Test MIMO"); + qDebug("MainWindow::sampleMIMOCreate: non existent device replaced by Test MIMO"); int testMIMODeviceIndex = DeviceEnumerator::instance()->getTestMIMODeviceIndex(); selectedDeviceIndex = testMIMODeviceIndex; const PluginInterface::SamplingDevice *samplingDevice = DeviceEnumerator::instance()->getMIMOSamplingDevice(testMIMODeviceIndex); @@ -916,6 +919,7 @@ void MainWindow::removeDeviceSet(int deviceSetIndex) deviceEngine->stop(); m_dspEngine->removeDeviceEngineAt(deviceSetIndex); + DeviceEnumerator::instance()->removeRxSelection(deviceSetIndex); delete sourceAPI; } @@ -939,6 +943,7 @@ void MainWindow::removeDeviceSet(int deviceSetIndex) deviceEngine->stop(); m_dspEngine->removeDeviceEngineAt(deviceSetIndex); + DeviceEnumerator::instance()->removeTxSelection(deviceSetIndex); delete sinkAPI; } @@ -962,6 +967,7 @@ void MainWindow::removeDeviceSet(int deviceSetIndex) deviceEngine->stop(); m_dspEngine->removeDeviceEngineAt(deviceSetIndex); + DeviceEnumerator::instance()->removeMIMOSelection(deviceSetIndex); delete mimoAPI; } From 6e7774d9f77535dd7f6619c6426e8f1b2e927fa0 Mon Sep 17 00:00:00 2001 From: f4exb Date: Sat, 14 May 2022 11:20:37 +0200 Subject: [PATCH 115/115] USRP enumeration: use default name and serial if not available. Fixes #1239 --- devices/usrp/deviceusrp.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/devices/usrp/deviceusrp.cpp b/devices/usrp/deviceusrp.cpp index 843027958..a0ec4edd9 100644 --- a/devices/usrp/deviceusrp.cpp +++ b/devices/usrp/deviceusrp.cpp @@ -45,8 +45,8 @@ void DeviceUSRP::enumOriginDevices(const QString& hardwareId, PluginInterface::O for(unsigned i = 0; i != dev_addrs.size(); i++) { QString id = QString::fromStdString(dev_addrs[i].to_string()); - QString name = QString::fromStdString(dev_addrs[i].get("name")); - QString serial = QString::fromStdString(dev_addrs[i].get("serial")); + QString name = QString::fromStdString(dev_addrs[i].get("name", "N/A")); + QString serial = QString::fromStdString(dev_addrs[i].get("serial", "N/A")); QString displayedName(QString("%1[%2:$1] %3").arg(name).arg(i).arg(serial)); qDebug() << "DeviceUSRP::enumOriginDevices: found USRP device " << displayedName;
  • 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 literal 0 HcmV?d00001 diff --git a/doc/img/Configurations.xcf b/doc/img/Configurations.xcf new file mode 100644 index 0000000000000000000000000000000000000000..c3fbab3ad6435ae2b2f687e0028e0a60da65ea62 GIT binary patch literal 94059 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 z z(9qRtacI$^f|&0KGrnk#`>bI<6_q2tO~imEad!eAyF59oN*~G zBrbdA+{}p~6H;O=LW+PPHcS+v4U3I13rSDBQjmLbaR{X!ck=GtJy9XwKX`C&Ye>lF zx9{IxXBnae@}9xk@$h$qb{za2t{n@1hiS(U7w4gx*vK<3&heUvec!-H>dt(VVEh*^ z%&IW(TQrH&TwHogEt)cR>2w$8NX_^$D_nX;YA0`T=^4TQ4cATre^}APEiSR)qEKx( z)HpO(ak(J>>grJFjF!;)Ee5U6S(mqQbj<49jnJBwAv6s-5IQC<)FfCNnYB?Ie0Sl3 z;QLF1p`~~BMg@O<=ic3o!NF^8-@pE`WpM8JEiR$_2!)ykYv+?F)8P9+d*S!s@N++h zYo`Q{Jry}OXG-vj%y5g~A|M_SCWBn1OAt zy|(KQwzX?_r3C^iNht^eMr4!nE6(T?2MJNL0HOjh3sF>}W+8)!M%c!-78JL!^26>{ zpZD%tT9H2>v56tP$#b9Qy=(8@_r2TQ?HxfH3pjNSd1KnEb6iU_u1UKcjxEEERVV+r z`o8148wFR#uJ6-3lA$(3Knl1hs>a)8_s9XQeWdZAJ<(W`4%+RzdbIY`rrWO{J2Baw zTtR&gIBbr992QuA?^h`A^oI_u9O}udN>n*;agCt+%CuZ3eHS zK$IO`SE1PR)`i>2a}A;x-zwJs(BJ>LOFVSt%D{e6+~AxanMbSLO@{e(DW9uht)Z-KY{y!h`q+J(dVaK`oKF49RJd+Qg`uo!wBA?2uCEZU zTwk=L>x-7uWFAefr6%8Kx^VmEuRB?jXNXCOXWbW-?u*GiH(Pw3ShD5LHa3D3_VHHP zIn<)H4s6_Djc>J5SNl%7t^4{0`rox$cVCYGwzkNcs^<+hE2>|5z1ChArC%?RpFQuwbWCZ>PVIY`;+`T#q-n({+N-)N8qRBwQejoj65{ z#Adya3h)L|*{`S*gM%hP$|Elc0vs@{X{pgnpkV9SR~|!7XxMJpdRCsH$mEab zB0$qa$o zUe430Y~$)$;m7a`__d;1$$XbaqlS$UlMOGEhn3I;Lof=PVJmEh9k3I2!y(mL4i>`_ zSPFSq30*J*qp%sa!gkmJJ7G5*QuT7M7?!|N$iqtLf*}}%&9D`=!w%R9yWx8CD| zjrP|H6lXm*e!D00=D(RUn8c| zcQX4dLe7!&Ly~#CKTj!L)Bh-$Hvsn0Y~lPna$2u$S#N@63!>g)}KISbB)bKqR4zJzfSjwh)j!%5}Xj^j&H(?5GGnQ#`I4d=kQaGoM9=t<9> ge)kUb$$0N=>^l#`cJ=PY?r{GOos*rH2f8!;4a?VwEC2ui literal 0 HcmV?d00001 diff --git a/doc/img/MainWindow.png b/doc/img/MainWindow.png new file mode 100644 index 0000000000000000000000000000000000000000..cac8a5e992e56bda5177a73b0a7c048b94b85a1d GIT binary patch literal 17455 zcmeIaXHb-B+b!CT0R$0Dpps+^2oe-TB+p<#K|qO;1tllRnKpn32#kWDL{Sj|2};fi zA{j(La?a4?9Q&-E@9aAJ$En)CchykuJ2GvD=ee)6uC-Q=?@a|6I+~+21OkEX`nAhS z1j6=70^yIh)PLX+9__^I_+$6|YZ`V00(~0g|J%kqBMS+HV}$FM|5kC1Bz3vGX}0TM zA-8mCO<7uV?@JE7)?koyR`$nH**zMv`rKR^G?$*8ZCiduUm8MsA?+xnDRZ4mQ}yz? zZFBO}-1@*ucl~CUTT+B!&4gf3xzv{o@pM+qbuOqxuTk+=FPxYuu`&@RrJJ% zn%df~Jj3e5=A;q#tyQt1B9$ zxN3ruTAVB$^99n+*n#cz;%7(ZMdJ7_Nbn|Gb>$gHI~YxhIdn&uQtQIda z5dO?EW{n96QDSgBaWbCK>(M_Uk&%&elbtqW30%d$dI?*>QSMb-m6esbxw-FT0(1+U zZ56plBUHs?9?AE{BNd$X*Q6qra0owo$P$NWYS_ zY_}FZ{a|!5aUgrTLO$u{JA+JD(<2&M7m_>khLf7oI8J(nu;)uid2Jl)e@IpnGHcqy z;l4hZZC$vi)*t#>e>lfxKu#goqOB!nYod{%q{F(<1CgLNb-b2qJES18n88V2%6=Q1 ztW)S(*&ipz$Zy$EWchd(-HR755>-<}M_2B)v)OG-2c@beIhOeynxm!p{Yh8CvMQsc z{MmJFXJzMx>Hy)+pUGO;r{u#e+B3#Bm!+INY9@B>lqyOXbv7uM+UTQynW&=s#ksO4 z#?^I(?y;btU>J|C>j0hbiBl$>?<-B?quO(>56lf!r)p(ewKtij7R{L{DAZXkmtJAg zvzPWcAWglwzNnXOBu~vDaeUx&IotL6idU;+HTgb0g(eMeOMK6$rgVO^dB;%l;-nlS z-|ZY@HU)DY;9({aAi>| zx_MzmvY9!HKl06+V^aKa2HrhF)t@>9l5HP%zPI(}KSUs({QXITRr;fPYPafkDi)`W zS)6i@6~91Db+w!4!n|*c7RzsU(f2= zW&$2J3JUnuwdOe_F%I=@?-ZZW8(chhmtK74 z!Qfy>DZLbFrj(wR!=gK5tjnp#ZL+y#Zq9Kn?9CaqPZjm`iV6y-PgyfFGb<~rKB<>y z)QfC~WaYz2J#X|&8A>{H?S!p5LssGwl;evRe|PkF&RbF0-h%Hv8e?&vjSYm7CX$diWxvU^GS!)ZIRiCKBB_xz7+nmVZ_5S_)b_X+F?UiCy za#eNe;Ls4ScG`>&iJjX>?UQbyCUvZCq3i2If|sbt;TJa3>y3OEqM28<8v=V$JvXaf zp7Ca=3ghp|CiXB$E3M4_<5}^|hmkAL# zF-9q=A>t#Q%_wtC*>9iDJzf_1WIp+${Df6PT zzc4ARmj4!-_gFtMqZj8m+1}%^tb@alA~w9e;?Kw{y3+f2IU!ro$jHdn*4ERLOE2-& zixUq0=ekE7?;n|o4if=_XH;qRa3R1CFTs@VaZI;qc&K!AuD))dh)B@5AbLdI5++Xc*(79 z)l;;|8M9%BT;%Y=3A>SI%Xv4<`a-x2^ZxPZFFz1*!BR6-x3F-zTg>b1?7M!>0Y&lI z@89XSN)D|QCqCQMu}%3-@9RRF`m$&8ZPmi&>{!Bemx!$jp|bSTwx&9R{nXDpKs8w%B%IF{3Ks^%uH7B>{qgo)clvo7wKU(53d_bW&jM2j?pfe zcnHy7%ykT2;z~&1@Ot#i4nrFp*GgIv~HP_V%uZL4(cy8NM80{Qb&qSo@-w-(EuEK55_ zN5EcR<$>x@UbpN$JzTrL5)1`+rIKw?sgy$o(2U{5gx3JI{*Lu zy0KCitST7n* z%2MlHr~F7gGoOu9R=Go=%iV6)C0@G^oVwvW5Fq@7o?W-tb5o#BtI%~ZKE`tmuh{CE z_FPWdJJ%v;RPzcS|7Dc-olKDA*2;j>1&^j>Ap%2c(-%e2dlmmw26HkMo|KUAs0!gK z?ll>%i^_Ljox}1|FjsZPTR7T%u5zF}h}`>_UUGFXSns}0T)2RtY#5)8fPeswlTq5F z?aR;;I%Wh|2%p6u{qAZ2w|a^=4nfcDaB*&!uIpHX3_Bf%%Ez0rGC^#_)oQ&wr|Ia3 zi2RNt^rEA0LPA2)>qIA7*pDL*G9^Y=UbJUh_vG4*oJq;)Co+YFx8ut128x{G<+c3w zXs1(`L(6b&#InijyXAor7zngtMQX{KyqXz@CEcAH92`b}_5~3)?rLgkIyx>zId$hc z&-UMXceTr=oHgCJo=-icv-R`c80UUpCS7L^ubIJMMM?w=iRtH@raG|>;msAJM1{P5 zqT9CU5ew4>v~X+2b&(_zsb_P_NnO{)5C=UsKW|(gy;*TQ2yNE^txGG*LWGo7N*Sue zt&Q)01iyH(i=HhqG}N)0H`{)^DFL7#)0M*upQ}%5{TwCgfCn4VnJ0cHYhg<{clSrZ z*4hMz7n<|ZRF{~bVEUUkcLpn-5Bvy_Bl$htPIbZb*9kE(H#}rrv}%-)|0fsMnBSR{ zi)*9r=I7_LE!yS~CeN+a5jT6LgPDfap`6jqZ|z3vurw;-Ffb;rK09pF?J}~6w-*-9 z)+_eJ{&+)Ck+|IDg#D(JsTUlTwTF26te@URnNy_}{kOz~XgS!gXP$ zue2k}QY&5|>ZDv~r(=gDo>N>o%#=Riv+U5KlZ_5C-khYKsFI+aYrEV-Bm)8y6I8bo z4$jl+vG-(}{~N-ou6&%y8TAwv^!{kRJ_IX4Cx7W772B(oMS9{&>9Zq(MdYpZlHK7K zEZbA{ikYnoW)a(g9`e?5U#U<0hYt!dl7%kw!yH~Xc8}GeFy9y~^eWj_U9Sa=Lp>%} zS63V36|E5<>R5C!z)aMXCLo8BvhpQrEOQD4Cv$M-#jC>+w0t>l5K*_u%$C)W7A|fmc)QWx0sz!HaqmlTt*-KfXgf03`uYEluT5?>&6B zx1P9Vhq^RtPQ<$II$tX+g`PWteuITd^3K->_zQ0a%G;w}9j7{1^CnUzI?<;!&) z0wj?_So!lt-_F-dk*$gAS{f;Tv}X&{puIPYLuJq_>QtArG$a{+FJSR*AtZDs{Y})9p=M8 z=_KFB=8p&(oj3o@iwqPa|9?Dj8M>73$bGZ+dZP{A;0DL+dTN3swV! zyI)06e)4fb%8C*WfYdml+Qk;Vn8U4D2PnJ>Ly6y^GeyXTOY{=wg@uiwk-{0_DG>9zwv0q9~W%vy}uk-WW@dm&__`#w=+x{ z@MIbq8foh3VzTGofB%hW;ieW#X!#EwJZOPR!j+e$9P4S@ab+1n;@6g==D-C+SQjOz zCjBmUnWsXQ$nNuQS({$!+pOe_0Rh3~b=Ic;d*M=h5>j7EAFQ3>XR4OwJ#6ARt&6j) zqV}gR$g8QT)kjMpAEH-)a+8}ARVjUjEnp1yU5vtFN4Z}PXUgY3b>>VGavP0SVMDXX zeHE!GAAWu;+&ty__3MU7WMo8Yxzo;fnX|2}%{$$q?aN63SFigeyE*Mge{!5W83*#>`0?W+BDk3< z4zH4=i~B#{9#46N28yf}MH|O0A3CEZj;JKr9m6jD1gERt)69!v@dIlwTV4m@Z6}A5SaIMGM!VyXLl{Vv; z&3=FWCk#cKOC4_0g~DF<@n-1%Q8isDX)ag!40LO`6)_mF2r8)iMIK1{Ikt}RoL3o(b3T$V7GVBS0(mtDt7n#ef8eUMxTY*2!!Gsy_L0< zAj!4SXM%Otv@L$(NQ*qyw=#xKZ0*WG-U5E`=lUW0S`w5wy>2F{C2Lvb#z}kAaCnV1 z#_Qexw$nE_6{Fn>g#_vj&}8kvZ%H(VMg_6S{OKaCVowhPQwoix@52hF4?U|UQe>W# zUcZ;)-o1N~j_sxgI2Cwu|DkjkoqWg9pRr6j_6=L>)5Yj_T}1NcOKw#f4jzp!Cv09w zdffzu$2jt0Si)RG;n#%=C83jIJ6gj_F!k{mqx5O?D0C*{+VEyfJ9KR{5OKgS{0pCh zI^ck5@7IUEw6#op+AC*t-WDPPXoqMyA{<`dFDi8I!s<@3cN+g;@7% zh69)J=oN|28to$Vw#?AR1Q9XqQ5`XWX$&QRVQeVrn5;-j2cE0uCu0{rz*+1cF6J}^ zxOu^%HSKZ|Hbrh5y3&QQM*gCe{_jlH?iic_%)*|?OJaL_{EgSPl_$dv>ZX0hK8k8* zwXNXrCJKn*#ao-}Qu8%}zKfEm8!@dW(2x{F(I_vhgBbvr6F&u|?yDT8y~p=xEPr?3KEOYXd=2l6_tWwlK^oRH`=+5yFEeCyudLyfMVzqU$yZ znkqCZO2Xj#)19=8UJSBRenC=OIp7OWU{$rXn|(g)|CWmJyXw(Ev8mCP)E)pFX=+*~ zKAr-v52OPjuIiz#l33|t(pK|5oXPVsWR|mcz80YZkyU`3S1N?B^sHUeRb?q8{)+(C#yU^vjy z3^C%9=SER@N$TG_@VE1ut+Uu2*DcHO+>odVGbkpKqDc1scLUr0`}Mzb_%8|mD~A6% z!G9g`|L$=>ZoAF>zq|ndO|}1~+J95+zp3`$RQqqL{U4`V@EYt~Pj$hT@aL1hF!E}? zkBb9mYzab~LIi-3q_8-U0YG#@N5ZsB;<`CB*Wd=%Ztvq}7 zjPeZZ%bUXy*3#6Rk@rjRLASvzfn*=1JU*V3S|y-<4umxO+QQhp6nS2Xzi>gRF91}l z6hyaQjg25g8sQF`_W_Mm+0g-B2OO#u?k`Nw(IZJRz$a%;H*iENFSwB zOU5pab-EiAH}-C4(&uu}v9~Pxp~*mD&ji&E>aIGBpIyw6G?7{yy%N8D@5MJ@MzN9e zh;Hg_&4q!*7#tbl^z;BJL=(^7lwsP)ap?(tByneqkkSns0W)MGy!2Z*7oVV({ zI4h1MY6LSEaq&**+Ed=@TOXU^4z;&!eo2=Hjj8SG>e?saHWnA=2@~J-;J}D$50n+tm6{#|qt+6=Y=-;e9C&Wc8tAb{vUvIUo5 zGIY2;hHTC3VrDj;reAtBh;8!+vo(wR%s1j%V+@6%pX|tHQn(McFw>-=|CbzpZCzc_ zcw*8nC?B9{;0DPilDAs4?P|eODu@h)ATJjS=Ig4ft2-98%hb9c*l?>RBAs$Z1L7lX zWswFbn_sAqK8j>Al{4U3w~q_~EfR?USC!8AIIK0U6Uo_Nx$Kcz6i#a98&&bzcoO8m z(7caq60S`tI_nhS8!1O|3+y?3zg7tHJ{#VY9@2T5uTvjb>s7FxDNxoI78XD#JAukH ziScHj^b}Css8MIUJ1mY64X+jCNzGLp=kN+v5H*EsqAc+Y=@go5~&W zTxv7c(9pOtauh)~$PO`^>HS8ruz>f5|I4{W$0-WDUA_4HjfUM&GC%qA<&8d@GL&d8t06s7GRUmt9P z6k{CY;i#te8||$V4}%3Gxtc=CN`g1F=+5ZrCh#r~&IFx5WYJ~!L6j*jZOJO#r@RN)`d zo@-;iyODnsst?_SXgskgVlO^p2R*14nOZJ&EINHhX!gY}3^cQ;{NP4WPz);{d1LwpfiO>$7 z7vjZtuGhHdF!8Iz*)pbJPQ~st{Y%t+P)jMg;w5VI%?&B?YW?NA0jy$6C=W{G>>Jr5 z7LoJ{0)9=56gee-iraMn?$9N$wVb-t2dusP+S-;*DQFlP88IF__>4uw7Ca;(KLL>o zH!rzPYAhy_UnI$)PlzYy|BuoDy_LUV7Bw@4>wyRjX89t99-3P1t8)WTd=%X;&LAK< zE<-PUy*p9A^CR`eFcA6grKA!l?^{+@Q;=L?iQO-J0f(OuA5Fty0b3ASU=p5XBs`>-Xif!n8wuc%XJ^N2(yv)z$IkhS8w&kQ9k) z$|xI(?*FA;NWa7jmdGjG6*MA>sDvi&PnQXo0Df`Or@Ik(5i=kFymSww=ru6-foK66 zE3-ZHjZ zT@Wmm>0q^Wp5=Z-(_bdF&7;io7k<1?WXKxr66Rf<8Aq-DS5&&1Cs(t_aA zsAr_dcHB6cs5spFq&6cy1 zk+d(@o_0;%8vWey!(2UCD@|DLl$3s1#b}fTN{ca}8|VcTaoXGZ6^8#NGVG9BCbwyJ>VsnLmh$aVEf9Fl z!p+5}lZS41mW#_2LA;{3)BmWLqXo2-qnK8tCDbHE(@9ddu8V}|0XV~t`$mOKAb$}B zxIoyYD{5;kTT;3xiY#UhWPxNz^c-GzA-hwj4F=#io(#bYIRIQBASeh{e@R`>WAPC! z-{JEnH_2O@aw+FxfrikjbEfhpD02=;fFb&&bq5r+Vj3wSNeqAksX0JvT6y-SedmFi z_|@X#>Qrx32C=>f4raz1K`h>P{Hi!bmsCzG`@rSkhL5BG#Q12}@lLz?LMUuhXf{9{ z4ew-%5eLB0(Vk*rLXsA425}0xJ&pR9&xePkQBjJQ#SYh&Z$I`TCWg{E{uNV0E2^Np zMnRB*8?(JlO(jXq63!BQZ@_$csw-YG#E{rM&@`8S|}1fMb9r zPcJ!6pjzQrX@)+sg&|Xi4l@=X!*>)8DU>2b6N4=1fw8u>G^v%MoonzI7C@WI#<k`R>9Es{!}D&EG~)G0WGoxk7v z6~+#BUpYO$1U(F&#R*Eo#)U3T6UlA>lYa6uz~`Q@QJ^zhp-_R}fg34R3P{cBvCsg; z_lCI2{^c&pDAd3x%+msdA+GfKU^LfIa4H-YXWYwg-+)USMS@IxvggPzj8h18FwfW; z2YpnUB#jkxDDIW}p9(|yo)S3kbrW*}D}+060Fva4Yf9ezrT-a@lNP42dzR%Y<>ewT z=M0CNM?;Szuf~waV0h6OZZBh$K*s2g{98*Hss+drr>y0YsldaO9R*k57nK|XU6s_9 z?v%y8ziNwS#oq+;88^LNq8AlJ<(UHeBgM^NjXs<^5*-m53K!6Oikyuee=@!XmLhmM zv;_`J=Zt-b*dN0MEa3?xDBOAfQvdN53htYAnF653O{$}|DPsFKba~{^LFwDbcVw(I zHDzys#~ItjVQFWiBjxZJq=B1Yr99v>FkQ56_3Qn0V2Dn5HDm)h&zzY+*o#q6Q>a+D zvxa|bSxaCEliSo@fsSi}X$3`p|2tk+ULyqsg~~@fA}v-FhvmtWLl3s^g<7Ylso9>4 z)TU^S`6}<4CTnYJB}O9cP@4OtU%>%_A58G76=_>6H{JG)@)5$;Y*4ik$(Wp33@Vg>Yr}U;t!^S z2eoa=4rO;aYQbY6rz`ajOg_M(?pRDHiuHviSgS%<#sQOPeMB+ozKaz29^gE+buktr zfa0_uHB%NrTv9dac9G*>Q(zJEIfKigTbPj zs*4daiS08LP!KyHMcdwjyvZY43uhJ($1LKF-W1_0SPoGB8(?%5SjMua$OB2UooeqM zx4F)?Hd!Z$rmk?n8Y~0S)VOGTpbR>PY!r`8{i*IhhlhtL_2#>|ae&m&n5$waWq%N> z#To`01+-Dz4GvAW@?Uuv2en4gxfwrRHC>j|)$N9~DgmQNhgJUM*X{HL1A$_{f!Z;I zrd?B_0%BWt`oyt)khPjaKS98P-wHBzx#>H}Jp1(N6EFhBPG|g%qP#X#FVD}-DL@NP z<@d4wI52S`burD5bA-9D#&Nx{tjS$|DlRHxr@?ABE!^=D+_k%&1 zA5Krcld?uowg7xd1GpM!#i>st^71Zk2DtcwgM%+oqxX3m^U(&MgAoRe;a?+6cyzSx zClwk)V`EB#h9TF5S|49F!63P~@bi*?)FCA36t;Kx)mpLZ;9`6jvaGr^7lzW|{5eU# zd2;0F#rt>A;iReg;nc2peq3&^5v~F&JN!&2T>QA;tAzWVj^U^calX zivz=GxlDzBoUp0m5fNDhDsqKelw#JxS_C}h3J1a!DP6rxGgX)il$DhH7HtrHzak4J zJHWR?b&{Y`0Ix&i2K0a#OJXRAh4Mi#-B34WSq8W`%2=JR6Z5wmMl;6xhA*aRl5P;u zCJ!((!}Ch=#Om+sl#Kleh>6r)=+?i4t>;u;`rsv3pgtcqSCc-2)ZF24cqH%^p{@>U z=McyOEXoJMo`!IXiHY&^Ps1CFu&=C5r1l_sl-}j$_z92{@i30Gx!T8S4|ZXAXei3I ziVGe(%27_Evi$jra(L>abUsn;w|()Z|5UIvzP#e?B-0>cO0CZ{y)13I?h+K@E=oRc zC}MxG8Lk_U27xRh0yc5wd`A3>`|>B=-Gq6iGH#N@u|Upr8a^j4ZplJqls%*R{$>Kr zqs%|m8Ge2|w#f7JN$F{ZgrkRUKBL-kS^nmZ5a+kIEJ7{!6BcQzebj=Y^(}~RSU%y{@qcyr+9jkW<~C&+ugr5 zod0+@qukoj5p-0DMl1BmlP4u5CCF)kAFuJtQD$bk;KOj9Vehtu^9;6&w5+V|A`dq; zHAcTry_pa9kvO*zXte%#pp;-?Veu>=KuBwKdAi5H%wXgB+tY=M58i$R=(D*P3ClH) z#Xr?U(yMNqERU+DO0d9Zrira`Qhb}*7o+dt;B2BD$t-_ z%En!NC*rY|p~8KLddF$Q05(fY%bX{KuOsu@{yN6+^zvIw^QDCadq+ofemgX~1tqMx zrq@dlMMU%Y>;Te!1&)#THa!*`9U7ETzFB+?lN zuwLS<`}+D8%-7b}>6tIowCa2vqT1h!88^l5E*y58XPKVR$?MjL?4o(S07 zke3fAE+%4Jarxm#T2Int?)&fG7mghx5H7ORVQrHT7eDL|leo+=h8t0$hX{Xc-%jyJ zlG+62lY-@cXRVRUnIQ$Bczm9?v(;i0?y=iAR1O@An% zho~l~aKEg&ykiRO5)0FGODcO=Rz{Ix&oT`P5mj-b<&oKHWOw(rjOS7T?rO8f4?pgA~rNBAHC zU(L+R&xdK0%WrOWb~iQkUv(@4Ple3A52qTDoMN>&e$b!|UKAE?>F8i4bar*&JZg3g z$Y)$Teei9v(2tsfcY`NMq_j3+lEhuc?TyXN=yKmyR#rsphA&>cct5wZx3?F7@0Ih( zr=ztrEAw|m0-{r%_|>Qr9*l34=($OuBTt_N!`0e)o1dTmLPS?vn+qtg5c{1YK~#if zH7)$8sCc#T7M^eAVVqc*9RM)p=jMhTH}~?Nx00H%0V=LY&% zD%qtx#j$i@D%`B|^YYs9`iA3R#lF~^*XYmx`2IZux&2z;Bu>7&dkb3>$Wl5wI>LQS zd@$dUQpE1tTx%Y;dXJ3luouMp;3`sg5*GFble%b$iHQj@Si(UBJbg;tdG}d1?e5*Lf$?MNr%9v&Oc*p3 zM9tsdUrtu`gF;l*$o!D)DmK0*GohGz#Tz#k=I7HhGE`0P}5g7Fx6k09*LIy(iEHR6>{8glg?8 z@eT|OB+Ot}0ts+8&f3}8*}$OxF%>=B{WGih0vo=qU~glCfl>?z{`|O1US8h)IvPT2 zu3da;YO43YKWa%=tW8OilYmt2?(X<#WFLwrUWMC+iucKy zS8joj+Dl8zEO76sTGGqvp=&PBF3ZHl#a*~?A;(qV^|M)>;;=AvS65NlE%|A{v51U} z^LlzcSy@@|NJfe|1?qAmz+GJuyu5UTtAQ-J7!4~c&a|}t@86#Y@lMOkJhZv?%&t7` zD0QQ7oVscd)&tbzUQ}^XQWAzVSgX^z*hrU#swv+;W?~A%>46i)7*jf!sb9J)_GW8m zr?!ShE{K{}uMQ`xxsCmbL$FVTV(b(;OsHvS9AUc!@ckri^l`9_C;}q5*VAIhv)a8^ z8}+H%ZU3S_te0zhlAmAk#QxZ(Y;;6XN78LW!?@4;I+)MgbI$DE;QJAN0dRVCex#$V z4Xr#R^LpyBcNk{d>(sXso*4O64+&g25@dI`u-xR_0yh_<|43cb1hj@`GedzkOrf7& zjcVA5;3^^`S?-=WCOIipC1%R5v*UXZCj&K6l#!9KB5TgM06g3iZ*P5Gl3t}=kM8qp zDmQO396929LW0QhEXaQc>U;a)D$|*5a|6C9PyRZ#@eP0?G$iEM;!MqJL41B{x%yJi2_oE<1fx-Gj7r z4e$0|&%4%pKkxnJ`S8pd20mPC*m3OR{2k{GRZ@6Oa`WCz2!crdk(N}3ARKA%H-Zos ze1;Exz5u^&I7n+bK@eHR-yfWH-?SzOdU)#}NpV&8w5=HrUDZP@eE0Z;xq4EhDX!{C z0)g-@+oKhkrWbn<-bjBgf z9%Y`#x~-*JD%Ow&Dj_h>oCxMWMv`+GRZuK45DQ3%1b&nH7{WbHxc4dR`u@0$K*D24 z>01bwRic@n#_cIRTB?#eJ=5c(# zUC5ij?G^(AgKi0qSh4EC_T@!v>6+s;g6mPVaJ%Uf$KU1|9z)exRPSsX^I3xZgL1B4 zBUIv(=Y1?l8hTBR_RiRQNvT((el&OP#HYv4kao+9e)8NHl|OH7y!ha%SrjWNkv(DT zF{I|K9QDDrMl;B6b~^({;;U{sE5GVAl^6+LiwH3fQ44OtJu!Onz@Q+UmYD`O*07s| zgoH#y=bjtt;q6xTyjBNAgSoGN#&-$7sGq8_wI2TmNpeq3)BSj|dV{2p;5tF{-B=pR z`;X#f5G5s?Y3K^SIp5sDPEp~vm``=oOWxkzGcz-q5^P=FTxkssyDd1#_R4o7C2J^n zH^0u2KF@JSBf8hiZGEVv<)LS~@9f;F<8AcpQKS+}oy-1cNjw6YPk+1G< zDvzyQYev258vP4A1Y3*^1B+4P2_YWS=9G-{9P4tb&H`tF3IS4 z`7p(HAkN?aYH;K5C`7ljwY7B>UF~0B8QxwpVl`sr^1b^8c>0U>*Uz*P0RaIY0s=V9 zoNR4Nb+a);`Q77=CYgJ0uH2TWqo`jKTGr$(o66pic?>D>&DcDnlxOCWJvumet5t(S zX+Mb-9lO*rFrdR;_xinD9Q}66L)dL=`SYeXStLQKaW7-&MNBBx$DpJS{%V*Q>TC!m zkdmuOj`ZyH5sar}BG)@LVrX|B8Wd&|3OipKYL}&a6-f&xg>rHj>kWB{TjihWJk2cC zG5D02$>%ZjDf{InQgEu}eVfLu+Xnws!PA61O2p`so>^1L4+jMW(Uo2lDQF0~JG6ZW z+HY{%fjt#?vbDwsJ@>;d#xT8>4U>h$9#L`WU%2kTd>C_cYW$EUr+-#%+~9gFFN5nJ zt>Nd*bS?-PYSE6t->(Lg4q99xSK(@EBRt7pCXBs(yhTJsapw0AGYuNuDWD9G)nu5@ zxe#*ufdWybZDu|;qA%hol0b)^{O#4n&ldC-v%t@A=CC{g8M%OXQ(MvRu@Nruf~0<( z+{E3L0^~gDE_GDl?Oen(5VW$g0>(d0XI1UlLWGxhtN5G6?%v^>;yB3RbZ7;Utbs%S z*i_?kT41W>aX;)hO}Q9?id9jDgSb%E`~qBl4(3yDICrtNnzbv=)qIkpn@vu5jg5>9 z%3|pn>t_}`j=FS*b>H&JORu$^s+pb^hLqIQ?$A&!*ClDob97ho$urB#{GKUvv$V9d zQ`p`!>Cmp(w#_bP5khn5p;wmPPUWu%)JJpN8qo0dhVwZFDKR1yH%E_G&L`lw5RUpI zH6A_79`35jqK zm%I^+2&r7jKf5oH6ZoVy7v3+We$=0-P=*5hmhvCg&p0f2_pmcfv`1Bgl9!vtgiDz#&v!r{mJ`q_CA$SmmAo zA@Sz7{4=Z75iazF3}UcRXm3PA-=lptYp~Q7F@g(8WPkF)tgrvvisOIFb9uMZEF8x_ z_jOc;kOv#f^NMhTF(4@5FeJpA6ciMWXTMlGtS4b<_Zc2oC)p;hx(ukPq=e=uv9NSc zx-Fd7B8Nt2MGmjZum>^OJk-!reh=zqgSf-}l%c7@YsSZ)lEy5HCml5!A2}r+ZjCLI z8qGEPynT0iS$WRS&ku3$22s~V&Yn>09g^|=71pVTN6Mr&L?d96ba~=hYo}MJka>@S z0td>0!+5r*>vKme0vQKe-Us|XR?;};j>N&i;=cD6##h&Ra|1H|T4&yGm|}+IvD3A_ z6Z>iRb870;p|#fQsQagT9eBF5Zi2N5rC1e&X>OHI8F0eM{Y^Oyh4c=p8!V|codwrV zmUySkkfGNiO3O}sT^(J1My{F6I;gk|&XwDUuRqWppLYm0g60cf!nb55OfzAYUCV)i zi54lL9|^A>Z;dxMotyV3=PR*|mFcPIAog|-47E~y+K>}AQM7e!R@J`yy)~yqs}1!H zrMlH%YX}1+idAny22|cRAP)}@7Z(?vWSbuLa*^Xrrx{!nYkb$4=<>=6g#=4j3Xe-y zT(Qdb47-A_+xn@~T5gCWUJD$q+4n$u?I@BCN%P)6na}i39&0SH(R76lHRG?ENF-9b zVq5VAGr?5wrqc`b-MR0PYqPmkh9t=j*XI+;b%*zINrkkUzykq zIx?o*9Gv-+uO`B7n_s?sxfV7$Y6;i64Ar+UnroiG=d5?Yeh*<+*Kc;#LJZc`H>Ap~ zPN${$cpW>A^tEE1jc>o7;nJ^jKN(m!L(Z|IYJH}T2*@7(LKp0I#j^vk zcCmN$QpwJy3nSFOR86Fz76%-kQ6XP&aB#rOTP%23DQ`wfd2LM|D#EZQvrUUsM$nj|z9)34uYI{jz6X;GD5J^rZ zDiXcLu#q=cAoVfPq3yr$=s!I7|1;Eoc&PsV<_o9Qyn+H!8t#^nyPyhsdZMHsRbDOB zO)w3sC@X(&ZB5r2h+|bDeJFCWnre?y)t8g&VRA!>7cITx7$w9~9Oi~d88;8EWzWb}a)>Xj(zL+0Bb`*Sb0>-bRqM~ zCtw_Xmv?m_`=vVK@MLc(S1JF+a{Tuu|HvFnB2?rW5rYf+n=8whf{I<2LJsM z20xh(@H*ZuH9%v#xzK6cMiLel80+$;gt$0WZSAhZIgAASV)rE^t)PHiNi?{}F=G4` zeD_*s4AZ~nfS;{Y=f1ZHR@M(}q&@o5RaI3r+}<<>)Z!I7BzlXz7o>F zN2H{rEG&wAJl);vMyy0#H))`_`1te}PEP~`YKlL+j=UG2l2S6Zfn(U8Aqq5N{B`{2 zP z@xj5tW@csu`T6Z_ZF%7H$B&1io|KRz<6y*HULeTVgg{a9GmG8gyqxY()}Y13<>l7! z%DZ}*{K+P-+NKZ#zsu_44=e9Zr|X>x6*K!p`DlnUGBVWE)cSsnDghh}xJSrf-*h+% zga^MoP)RUFUmi^b5ReH?@Jf{SO&Ahg`|u`Lsy9RQe74DJI_|4U$CD>dz?dGweZ4`w zn4O))BOnDwf&`LF6(|Ke3^tPIUL;5wv;sC3Rf+_?aWanuhuGp}ymFwd#>U3HythEF zeZO?|^_#87%5F0&{K|apwyoPy{q?Er8#2Gin5p)8a*6w6O3KQWu3Op=W)6MTLFbp2 zo}QkVxF(lQSS@7UfiP+fj!#USQXzqu6*D5j!j#$K!KAdbwCo%AepFkJSy@>@E#2LS zy1H=-KAA_K!N8R%M=+#?0cJOD+<>x_Sn$andarcdIe)5AuCK-x4~j8FPZdthr9X|( zC@-*rdHh)(|7ubXYyJM6>Z#_uVYWI#jWZFi1uPCOE-vJsQ*_pF9A)i(G+{ngZcwho z0@1_aa~6C;r{5WWQKgZ%8XFsXdU{^6j*N^5+Rs+eNrZ=oFV(c-%!knVVnz^SHMUdg z<@!x-JF`H3r8>0&o5(>k;?|0%I$-7=K+LIvnJ}NTU&Sg%S_ht&m8RVV3h61IKUV^A zqEM)*T6;mYeYZIuH}EKjPR%CR;vW%r0o{>W3A=8Nr|?^U{`?sj9cF8*2`rZyLR}B- zI|K6+F3Zl!njgxS1t$*(d+bLfEwDz1Iw?hRe!G_pkm^=4HjiDLuo|}?ZAZ|UN6Dc7 z@1FfHFQzUNVG8`%ZX9fM4+K5JYE}tC2FdaG zxu@NWiq{!_QHDxN3_EXN^^vOS)9;C_ht|yH#fJiaKoK*n<*cg+(DLj8UB0nshrodN#%0~rSpeI zXY_3MeQb)3!>0+ej|Nx2tv0`&9F-L>dTD(a0cPg;M@YYdHa;$n4_G+A_2@Vmjl(gS zvDWCURN-)MGA{u{d9yw6#%+Ak`$ej3;JeMtR<~ywHs+2A7!E!%a~ty#^EzV&9tgV< z)4@d8*}G?-a&g(O59LSqa8lmX_@epd&6}^DhrbsWjjOdqEr;^r*)^K?`-I(vW!q>U z*tglyY>EfqgIJON+|l@Z;Ei|njo4@V!3aUCUo0`w2RnXOQ&!cuVvde`V3$M4==!*c z?G|zib%EyUrs_uu2QBPoP=}puQPi8kQV}#UDJcNRfnm|o(t6Fe;1EO-5Yv2$iTReF zVuGC56uj{gW2vsIE8@OuxNR1Fzf`@!ey-V7)7dL}yt%m<80*iTo=1Ver5-ag(>-_a zw;pctIu`Xh8rNA}UVg;P-2Jw)q=fB$+MCyTgBC|yQ=phW=bX|lp>6t-yA(MdeN7k! z(=S%Fn`=(@0^|2Q-tOJ9oU3;-2h}0Er)*DT1nwKHqMT$*)AA&HyF=_jCDUK1}hOT-!%wI*4|zp}rn?Mg756_wV1oD=I4f z{P~kW{ZWZIOC7YQ4(a^f{%^+Mf^1YHuoJ_t#p%XUPtIuKz#BCK-)#aU?a&^SizfOT?Q*%YdC!-|w zO{8rAM~rlYl!?Cn!;Ex^B38zjk47}1l0#uWHK#BEjk1OYFC8vIhPNTF+y8qz>p!5_ zzaQMcJ+J?*hdP4{Z%a&;fsPw8tv`!Mu(@`^Mk0H_MnHvfB&UR2fjw{|9uX+&)K3f6!vqI z^^_EWK(c_cGMhIVl=Tzvw~QBt`vrdqz4l`+zf_~d0D-C~|HsSvI?9gN5K@~E(xt)HS7;K2*AU{#l@A2{d{{XyuGjI z!?*2mPtljQ-8(Eo})JJZUI7$&Pv#>CQs@BH|HUeUruqi_zye?+7AK*7Q zIj-Bv6G>Ky+3;@MICt=i&$%o0$dtou)XdH~0f#G)gzi+i*8hqjkb{*Imf?Cb6yyZ$ zE}+M}6Dzl*jt|4KBATJ3YEs8MAqaaCSWV+XA`E1;#H=v8 zuh+GHyQ7|!x<9?Nw77;VhMYdo-Q80@LtwaLESmiD{&^{#YhjYyk0ORvspbP7`QgA% z22l@TE{ik}anyaDvz2GK@5Rc;?B~u#nsOu()sbrY@1;O1c>BUZO)Y+6x|3i!OFRI$ zfkN8zXGY(z)$8^>2+V~0HBZ@Zk8_NlBu#F5*+o1aqnii=ZTPC$xmcyd`ROJ?K zV8s)?NT2S;W`Fa{*zP-PyuPJ#S39-7h*}E?|DLm{jaNa}X-ECo$O-2>y?)Aumfyc{ z+^N!Xg1KMKoSWvek@Z{huBM54vC+>@*`s=hISGzSh!cK-j8Z-22p{9a@*`Md4u7X}6f zii(O7ogft1PuGbPva+()yY1*pOB0@&sDL24^fH*3g78}4CkzGyu(_&>eE@j=gA9au zgGCC3Z2UEUTzbg3ct}&DS3tnnnqM18C?2(bcve8gp-}{hAqE}ze_3u&-hABsIr^&F zco~y8=X)%gU>bd2$bIpI%Wiwy=j=%5a(!*O(KuAHOi9R~J|LkFy)G&u5-5=^$yog5 z%cH-@M+TaXAT@#plYH+TI=XUIHXyw|(KzWxpyKy+buk8F4n6G#r)ArGtT4`g+ha7u z_-<})UKPm7N@t;&`ELP4H*c!4jeylii)R0qjUdws?uPaRM#Mdir)B9(zN^;d` zK%KwGRUG@ENkPzAb2=tddF%H15z*Evox{n1xE*y?0qQ37_ML~zNGsgRs^kU(*>>XA zi5V|+uz7fq2lS{$+2L%M30jt&olWCh54bad3y%S(mQlqb0^ zO;9LYjQ(SIxK5pek)f-5aeS(_5@JA^3PtD*q?RS8w_mmU9+jWoNG_sy{deg znvnqm00$^49p*E6DoUZTP~hIN)>V@x&6aDCQX$Y(Fl6;?oj*RX`VAk)=dODK0;#=o zJ`MX^e(BuC&L6s3Rh}jgM@3{DA78(7XN7F8?i%3y-nq)9*NvcP-3)r>*ve&q8pxL& z%ir;t-{xcroMva^OYITsY~NqQ4-WG3K-^%+(4X})*sTF~0!Hjr{88w!kr78GWNK<^ zW+t8dFje>pxE3OXQ8O|zfvCAzjPD;25wW}LV220e1cdj41uj5M9LdV=dIMHaiRF&)mJA2Ck zy){6)K0kbooB$2^vp-~@$DG>hikZjed~bkuTWf`>IrM?Gd{N&}EG8h9w^ z}^~m8x8i^a2I&{g zuQ`AtH%3c`t*UKN^u(?qugCi+Y>tZ0UB}EbeMTKX21Nzr8UqWo-&=HO+m@_+sN@-- zU-SFMOj5Xe(vImGuF#Vc93W5F0BT{sN7g4;zj2CVTi=?cuZLep%b0f*Sdeap)t zXs|?Y`|QWV23O9Qk>CCzT7NaTsw|Y_V7zd#uzdB76aBTNo?u@hVQAt z^Rw}H*sWQQ`FQjH2{?MX|HFEWAOiQXEfk>Bcy`4ZJyKs7{mdU+8AJcPQjjc@F#Pa; E0Dy8<`v3p{ literal 0 HcmV?d00001 diff --git a/doc/img/Workspace_create_rx.png b/doc/img/Workspace_create_rx.png new file mode 100644 index 0000000000000000000000000000000000000000..168689e776387572de41f6091d8945c1847653a6 GIT binary patch literal 11689 zcmch-byQVR+b_BR1q7u9gHS-EQCeE0K}w{gq(h`bT0*2mLAs=*qy?l^1f;tpHYsd6 zHqvk&-f{2vzH{!lW1REPS!0MxH*4(~&-~S#A*#x!w*wKxZ_CgPxgBV5oAJc3<@MP~ z)rkat`dMtp$0olysX!9$HwkY^ByrKQ z(PXCBtvKl1hNfq>ggZK|m_C2b`9Y3{hsS5)X6cKuvVSyx`Ywv;SNr>r2r+1IM&A@z z$HWfoJIo2WI?nAJ5Cmm>&01}XZVe`;!9`GR-grbemZp2#Cnl6+)y%T9FIAEG2s_DS zYvOz_Tj;2D*&smJVvFOsT@>)B;-WHUn}UgQvUBh*Ui`N^WHgsg+-&(H zF|IFy5H51Dv$5eI{K4e34Pjj~6SbapJ8nrfn8t=KM$)jfIgTydFcC7VV_re#QH*X& zkc~>-lgWhK;rl{|-6b!E+T4vb``hwYZ(>YB`_Xb@sFt7~#%$Rc)(%)tv>uZLvUuBC zwYMwKP_L$>q)>63&Y#vF=_DjPc<`Xbcc ztIbgR>uxE5Dwyt<)-n0{`Jr4Sf)5@X92|`akFNcx9{={?YMPI=70U&`VOw1;-8yCl z(aB6jD&!uIz|H%PQcumyA|eB0*u-PB*d%hwzf$-to!yp?YK^23@V-)PQ2yqJQJHpD zMB!Rs2)RON*xZ~Q*7xb#0gsr1d(DJg9G_L2-%0AS`jqlI0QchA2AVGXXnegu{F#2H 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 boMGj!-0H4uXZr(ZuMv4^WvL=bBme&a?v(42 literal 0 HcmV?d00001 diff --git a/doc/img/Workspace_top.png b/doc/img/Workspace_top.png new file mode 100644 index 0000000000000000000000000000000000000000..4bfd0177049001e71524d34787dac356abc04502 GIT binary patch literal 9124 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 literal 0 HcmV?d00001 diff --git a/doc/img/Workspace_top.xcf b/doc/img/Workspace_top.xcf new file mode 100644 index 0000000000000000000000000000000000000000..6b6009514e4045ff928fe4e34e0f37b75d5486a1 GIT binary patch literal 38063 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 literal 0 HcmV?d00001 diff --git a/doc/img/Workspaces.png b/doc/img/Workspaces.png new file mode 100644 index 0000000000000000000000000000000000000000..69529319791698c6bda3e352148ee6de29b91861 GIT binary patch literal 54229 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%0AcX=4jm)J;og=sP?fGYlsnjS`ALR<$w?*XF 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 literal 0 HcmV?d00001 diff --git a/doc/img/Workspaces.xcf b/doc/img/Workspaces.xcf new file mode 100644 index 0000000000000000000000000000000000000000..f55a7928e3ae4a90d84180f8187e07af532fe655 GIT binary patch literal 137042 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 KdE~BpzCQu}!~ul> literal 0 HcmV?d00001 diff --git a/sdrbase/maincore.h b/sdrbase/maincore.h index 77a5ab322..15a8ae5cc 100644 --- a/sdrbase/maincore.h +++ b/sdrbase/maincore.h @@ -745,6 +745,7 @@ public: friend class WebAPIAdapter; friend class CommandsDialog; friend class DeviceSetPresetsDialog; + friend class ConfigurationsDialog; signals: void deviceSetAdded(int index, DeviceAPI *device); diff --git a/sdrgui/configurations.md b/sdrgui/configurations.md new file mode 100644 index 000000000..404f635b3 --- /dev/null +++ b/sdrgui/configurations.md @@ -0,0 +1,45 @@ +

    Configurations dialog

    + +Configuraitons stores the complete setup of a SDRangel instance: + + - Workspaces + - Device sets + - Features + +It also stores the geometry of all windows and workspaces so that the entire aspect of a configuraiton of the instance can be saved and retrieved. A default configuration is saved at program exit and retrieved at the next prograp start. Use the `--scratch` command line option to skip the retrieval of the default configuration and start with an empty setup. + +![Workspaces feature presets](../doc/img/Configurations.png) + +

    1: Configuration selection

    + +Move the cursor to select a configuration. Features can be organized into groups at the top level (here "Test"). When selecting a group only Edit and Delete group are available + +

    2: Add new configuration

    + +Save the current setup in a new configuration. + +

    3: Update selected configuration

    + +Update the selected configuration with the current setup + +

    4: Edit configuration

    + +Change configuration name or the configuration group to which this configuration belongs. If selection is a group the group name can be changed. + +

    5: Export configuration

    +Export selected configraton in a file that can be imported on another machine possibly with a different O/S. The configuration binary data (BLOB) is saved in Base-64 format. + +

    6: Import preset

    +This is the opposite of the previous operation. This will create a new configuration in the selected group or the same group as the configuration being selected. + +

    7: Delete configuration

    + +Delete selected configuration or selected group + +

    8: Load configuration

    + +Load configuraiton in the current instance. All components and workspaces are deleted first. + +

    9: Close dialog

    + +This button dismisses the dialog. diff --git a/sdrgui/gui/configurationsdialog.cpp b/sdrgui/gui/configurationsdialog.cpp index eb9266ded..c324f18b2 100644 --- a/sdrgui/gui/configurationsdialog.cpp +++ b/sdrgui/gui/configurationsdialog.cpp @@ -18,8 +18,10 @@ #include #include +#include #include "gui/addpresetdialog.h" +#include "maincore.h" #include "configurationsdialog.h" #include "ui_configurationsdialog.h" @@ -337,6 +339,100 @@ void ConfigurationsDialog::on_configurationLoad_clicked() emit loadConfiguration(configuration); } +void ConfigurationsDialog::on_configurationExport_clicked() +{ + QTreeWidgetItem* item = ui->configurationsTree->currentItem(); + + if (item) + { + if (item->type() == PItem) + { + const Configuration* configuration = qvariant_cast(item->data(0, Qt::UserRole)); + QString base64Str = configuration->serialize().toBase64(); + QString fileName = QFileDialog::getSaveFileName( + this, + tr("Open preset export file"), + ".", + tr("Configuration export files (*.cfgx)"), + 0, + QFileDialog::DontUseNativeDialog + ); + + if (fileName != "") + { + QFileInfo fileInfo(fileName); + + if (fileInfo.suffix() != "cfgx") { + fileName += ".cfgx"; + } + + QFile exportFile(fileName); + + if (exportFile.open(QIODevice::WriteOnly | QIODevice::Text)) + { + QTextStream outstream(&exportFile); + outstream << base64Str; + exportFile.close(); + } + else + { + QMessageBox::information(this, tr("Message"), tr("Cannot open file for writing")); + } + } + } + } +} + +void ConfigurationsDialog::on_configurationImport_clicked() +{ + QTreeWidgetItem* item = ui->configurationsTree->currentItem(); + + if (item) + { + QString group; + + if (item->type() == PGroup) { + group = item->text(0); + } else if (item->type() == PItem) { + group = item->parent()->text(0); + } else { + return; + } + + QString fileName = QFileDialog::getOpenFileName( + this, + tr("Open preset export file"), + ".", + tr("Preset export files (*.cfgx)"), + 0, + QFileDialog::DontUseNativeDialog + ); + + if (fileName != "") + { + QFile exportFile(fileName); + + if (exportFile.open(QIODevice::ReadOnly | QIODevice::Text)) + { + QByteArray base64Str; + QTextStream instream(&exportFile); + instream >> base64Str; + exportFile.close(); + + Configuration* configuration = MainCore::instance()->m_settings.newConfiguration("", ""); + configuration->deserialize(QByteArray::fromBase64(base64Str)); + configuration->setGroup(group); // override with current group + + ui->configurationsTree->setCurrentItem(addConfigurationToTree(configuration)); + } + else + { + QMessageBox::information(this, tr("Message"), tr("Cannot open file for reading")); + } + } + } +} + void ConfigurationsDialog::on_configurationTree_currentItemChanged(QTreeWidgetItem *current, QTreeWidgetItem *previous) { (void) current; diff --git a/sdrgui/gui/configurationsdialog.h b/sdrgui/gui/configurationsdialog.h index 12489e533..6ca575d77 100644 --- a/sdrgui/gui/configurationsdialog.h +++ b/sdrgui/gui/configurationsdialog.h @@ -65,6 +65,8 @@ private slots: void on_configurationSave_clicked(); void on_configurationUpdate_clicked(); void on_configurationEdit_clicked(); + void on_configurationExport_clicked(); + void on_configurationImport_clicked(); void on_configurationDelete_clicked(); void on_configurationLoad_clicked(); void on_configurationTree_currentItemChanged(QTreeWidgetItem *current, QTreeWidgetItem *previous); diff --git a/sdrgui/gui/configurationsdialog.ui b/sdrgui/gui/configurationsdialog.ui index d7938bdcc..23430dbf5 100644 --- a/sdrgui/gui/configurationsdialog.ui +++ b/sdrgui/gui/configurationsdialog.ui @@ -135,6 +135,34 @@ + + + + Export current configuration to file + + + + + + + :/export.png:/export.png + + + + + + + Import configuration from file into current group + + + + + + + :/import.png:/import.png + + + diff --git a/sdrgui/mainspectrum/mainspectrumgui.cpp b/sdrgui/mainspectrum/mainspectrumgui.cpp index a4d082977..04aab3795 100644 --- a/sdrgui/mainspectrum/mainspectrumgui.cpp +++ b/sdrgui/mainspectrum/mainspectrumgui.cpp @@ -40,6 +40,7 @@ MainSpectrumGUI::MainSpectrumGUI(GLSpectrum *spectrum, GLSpectrumGUI *spectrumGU { qDebug("MainSpectrumGUI::MainSpectrumGUI: %p", parent); setWindowFlags(windowFlags() | Qt::FramelessWindowHint); + m_helpURL = "sdrgui/mainspectrum/readme.md"; m_indexLabel = new QLabel(); m_indexLabel->setFixedSize(32, 16); @@ -199,7 +200,7 @@ void MainSpectrumGUI::showHelp() if (m_helpURL.startsWith("http")) { url = m_helpURL; } else { - url = QString("https://github.com/f4exb/sdrangel/blob/master/%1").arg(m_helpURL); // Something like "plugins/channelrx/chanalyzer/readme.md" + url = QString("https://github.com/f4exb/sdrangel/blob/v7/%1").arg(m_helpURL); // Something like "plugins/channelrx/chanalyzer/readme.md" } QDesktopServices::openUrl(QUrl(url)); diff --git a/sdrgui/mainwindow.cpp b/sdrgui/mainwindow.cpp index 93abdaa1c..84bba6c5e 100644 --- a/sdrgui/mainwindow.cpp +++ b/sdrgui/mainwindow.cpp @@ -1870,12 +1870,12 @@ void MainWindow::on_action_saveAll_triggered() void MainWindow::on_action_Quick_Start_triggered() { - QDesktopServices::openUrl(QUrl("https://github.com/f4exb/sdrangel/wiki/Quick-start")); + QDesktopServices::openUrl(QUrl("https://github.com/f4exb/sdrangel/wiki/Quick-start-v7")); } void MainWindow::on_action_Main_Window_triggered() { - QDesktopServices::openUrl(QUrl("https://github.com/f4exb/sdrangel/blob/master/sdrgui/readme.md")); + QDesktopServices::openUrl(QUrl("https://github.com/f4exb/sdrangel/blob/v7/sdrgui/readme.md")); } void MainWindow::on_action_Loaded_Plugins_triggered() diff --git a/sdrgui/readme.md b/sdrgui/readme.md index 1a21dd6b1..e289a7d25 100644 --- a/sdrgui/readme.md +++ b/sdrgui/readme.md @@ -1,77 +1,137 @@

    Main Window interface

    -

    Multi device support

    +When starting the user is presented with an empty window with a menu on the top bar. -Starting with version 2 SDRangel supports running several sampling devices simultaneously. Each concurrent device is associated to a slot with a set of tabbed windows in the UI. These tabs are marked R0, R1, R2... +![Main Window](../doc/img/MainWindow.png) -The slots are arranged in a stacked fashion so that when a new device is added with the Acquisition -> Add device set menu a new slot is allocated in the last position and when a device is removed with the Acquisition -> Remove last device set menu the slot in the last position is deleted. Slot 0 (R0) receiver slot is created at initialization and cannot be deleted with the menu. The letter "R" in the tab names indicates that the slot is for a receiver (source) device while "T" designates a transmitter (sink) device. +

    Main menu

    -The tabbed windows are: - - - Sampling devices (1) - - Spectrum display control (2) - - Channels (3) - - Spectrum from device (4) - -The combination of a sampling device and its associated channels is called a "device set". - -![Main Window multi device support](../doc/img/MainWindow_tabs.png) - -The sampling devices tab (1) acts as a master and when one of its tabs is selected all other tabs are selected accordingly i.e. all R0s, all R1s, etc... in tabs (2), (3), (4) and (5) - -In each slave tab group (2), (3), (4) and (5) an individual tab corresponding to one device can be selected without affecting the selection of the other tabs. This way you can sneak peek into another spectrum or channel group without affecting the display of other tabbed windows. - -An additional dock widget supports the feature plugins. Plugins can be grouped in logical sets and graphically presented as tab groups (similarly to channels). For now there is only one "FO" group. - -![Features dock](../doc/img/Features_dock.png) - -Details of the feature plugins dock widget are presented in section 8 - -

    Interface details

    - -![Main Window interface](../doc/img/MainWindow_general.png) - -

    1. Main menu

    - -The following items are presented hierarchically from left to right: +The menu items from left to right are: - File: - _Exit_ (shortcut Ctl-Q): Exit the program - View: - _Fullscreen_ (Shortcut F11): Toggle full screen mode - - Device sets: - - _Add source device set_: adds a new source (receiver) type device set to the device set stack (last position) - - _Add sink device set_: adds a new sink (transmitter) type device set to the device set stack (last position) - - _Remove last device set_: removes the last device set from the device set stack except the first one - - Feature sets: - - _Add feature set_: adds a new feature set - - _Remove last feature set_: removes the last feature set from the feature set stack except the first one - - Window: presents the list of dockable windows. Check to make it visible. Uncheck to hide. These windows are: - - _Sampling devices control_: control of which sampling devices is used and add channels - - _Sampling devices_: the sampling devices UIs - - _Spectrum control_: the main spectrum displays control - - _Spectrum display_: the main spectrum displays. Note this is not a dockable window but occupies the central widget - - _Presets_: the saved presets - - _Commands_: the defined commands - - _Channels_: the channels active for each device - - _Features_: the feature plugins currently instantiated + - Workspaces: + - _New_: create a new workspace. See "workspaces" paragraph for details + - _View all_: show all workspaces + - _Remove empty_: remove empty workspaces, Only workspaces without any windows can be removed. - Preferences: - - _Audio_: opens a dialog to choose the audio output device (see 1.1 below for details) - - _Logging_: opens a dialog to choose logging options (see 1.2 below for details) - - _FFT_: opens a dialog to run the `fftwf-wisdom` utility with a choice of direct and possibly reverse FFT sizes. It produces a so called wisdom file `fftw-wisdom` that speeds up FFT allocations. It is created at a default location and will be used at next invocations of SDRangel - - _AMBE_: Opens a dialog to select AMBE3000 serial devices or AMBE server addresses to use for AMBE digital voice processing. If none is selected AMBE frames decoding will be done with mbelib if available else no audio will be produced for AMBE digital voice (see 1.3 below for details) + - _Configurations_: opens a dialog to manage instance configurations. See configurations dialog documentation [here](configurations.md) + - _Audio_: opens a dialog to choose the audio output device. See the audio management documentation [here](audio.md) + - _Logging_: opens a dialog to choose logging options. See "Logging" paragraph next for details + - _FFT_: opens a dialog to run the `fftwf-wisdom` utility with a choice of direct and possibly reverse FFT sizes. It produces a so called wisdom file `fftw-wisdom` that speeds up FFT allocations. It is created at a default location and will be used at next invocations of SDRangel. See "FFT" paragraph next for details. + - _AMBE_: Opens a dialog to select AMBE3000 serial devices or AMBE server addresses to use for AMBE digital voice processing. If none is selected AMBE frames decoding will be done with mbelib if available else no audio will be produced for AMBE digital voice. See "AMBE"paragraph next for details. + - _Lime RFE_: Presemt only if LimeSuite library is available. This opens a dialog to control a LimeRFE device via USB. The details are provided [here](limerfeusbgui.md). - _My Position_: opens a dialog to enter your station ("My Position") coordinates in decimal degrees with north latitudes positive and east longitudes positive. This is used whenever positional data is to be displayed (APRS, DPRS, ...). For it now only works with D-Star $$CRC frames. See [DSD demod plugin](../plugins/channelrx/demoddsd/readme.md) for details on how to decode Digital Voice modes. - _Devices_: section to deal with devices settings - _User arguments_: opens a dialog to let the user give arguments specific to a device and its instance (sequence) in the system + - _Commands_: opens a dialog to manage commands. See "Commands" paragraph next. + - _Save all_: save all preferences - Help: - - _Loaded Plugins_: shows details about the loaded plugins (see 1.3 below for details) + - _Loaded Plugins_: shows details about the loaded plugins. See "Loaded plugins" paragraph below for details. - _About_: current version and blah blah. -

    1.1. Preferences - Audio

    +

    1: Workspaces

    -See the audio management documentation [here](audio.md). +The different component windows are arranged freely in the MDI (Multiple Document Interface) area of the workspace. Any number of workspaces can be added that will stack upon each other by default and can be put forward using the side tabs (1). The workspace index appears on the left of the workspace top bar (2): -

    1.2. Preferences - Logging

    +![Workspaces](../doc/img/Workspaces.png) + +The different component windows are: + + - [Device](device/readme.md) to interface and control a physical SDR hardware device connected to the system + - [Main Spectrum](mainspectrum/readme.md) to show the spectrum of the baseband related to the device + - [Channel](channel/readme.md) to control a channel plugin + - [Feature](feature/readme.md) to control a feature plugin + +The workspaces are attached to a docking area of the main window and can be undocked to be moved to another display in a multiple screen setup for example. + +The workspace has a top bar with the following controls: + +![Workspaces](../doc/img/Workspace_top.png) + +

    1.1: Workspace index

    + +Shows the index of the workspaces in the list of workspaces as a "W" followd by the index. + +

    1.2: Create new receiver

    + +Creates a new receiver (source device set). A dialog opens to select which receiving device to use: + +![Workspaces Rx](../doc/img/Workspace_create_rx.png) + +

    1.3: Create new transmitter

    + +Creates a new transmitter (sink device set). A dialog opens to select which transmitting device to use similarly to (2) but with a choice of transmitting devices. + +

    1.4: Create new MIMO device

    + +Creates a new MIMO device (MIMO device set). A dialog opens to select which MIMO device to use similarly to (2) but with a choice of MIMO devices. + +

    1.5: Create a new feature

    + +Creates a new feature. A dialog opens to select which feature to use: + +![Workspaces features](../doc/img/Workspace_create_feature.png) + +

    1.6: Feature presets

    + +Opens a dialog to manage feature presets: + +![Workspaces feature presets](../doc/img/Features_presets.png) + +

    1.6.1: Feature selection

    + +Move the cursor to select a feature. Features can be organized into groups at the top level (here "Test"). When selecting a group only Edit and Delete group are available + +

    1.6.2: Add new preset

    + +Save the current feature set in a new preset. + +

    1.6.3: Update selected preset

    + +Update the selected preset with the current feature set + +

    1.6.4: Save presets

    + +This button is inactive. All presets are saved at program exit or with the `Preferences`/ `Save All` main window menu. + +

    1.6.5: Edit preset

    + +Change preset name or the preset group to which this preset belongs. If selection is a group the group name can be changed. + +

    1.6.6: Delete preset

    + +Delete selected preset or selected group + +

    1.6.7: Load preset

    + +Load preset in the current feature set. The Features that were present before are dismissed. + +

    1.6.8: Close dialog

    + +This button dismisses the dialog. + +

    1.7: Cascade windows

    + +Arranges windows in the MDI area of the workspace as cascaded windows. + +

    1.8: Tile windows

    + +Arranges windows in the MDI area of the workspace as tiled windows. + +

    1.9: Dock/undock workspace

    + +use this button to dock or undock the workspace from the main window docking area. + +

    1.10: Hide workspace

    + +Use this button to hide the workspace. Use the `Workspaces` / `View all` common on the main window the show all (hidden) workspaces. + +

    2: Preferences

    + +

    2.1: Logging

    ![Main Window logging preferences](../doc/img/MainWindow_logging.png) @@ -87,7 +147,7 @@ Log message will appear as follows: - 2: Message level: `(D)`: debug, `(I)`: info, `(W)`: warning, `(C)`: critical, `(F)`: fatal - 3: Message text -
    1.2.1. Console log minimum message level
    +

    2.1.1: Console log minimum message level

    This sets the minimum message level for a message do be displayed on the console: @@ -96,7 +156,7 @@ This sets the minimum message level for a message do be displayed on the console - Warning: all messages with QtWarning level and above - Error: all messages with QtCritical level and above. Includes QtFatal. -
    1.2.2. File log minimum message level
    +

    2.1.2: File log minimum message level

    This sets the minimum message level for a message do be logged to file: @@ -105,565 +165,111 @@ This sets the minimum message level for a message do be logged to file: - Warning: all messages with QtWarning level and above - Error: all messages with QtCritical level and above. Includes QtFatal. -
    1.2.3. File log enable
    +

    2.1.3: File log enable

    Use the checkbox to enable (check) or disable (uncheck) the dual logging to file -
    1.2.4. Log file selection
    +

    2.1.4: Log file selection

    Use this button to open a file dialog to choose or create a new log file. There is a 2s delay before a file change is effective. -
    1.2.5. Log file name
    +

    2.1.5: Log file name

    The full path of the log file appears here -
    1.2.6. Confirm changes
    +

    2.1.6: Confirm changes

    Use the "OK" button to validate all changes -
    1.2.7. Dismiss changes
    +

    2.1.7: Dismiss changes

    Use the "Cancel" button to dismiss all changes -

    1.3 Preferences - FFT

    +

    2.2: FFT

    When clicking on the FFT submenu a dialog opens for running the `fftwf-wisdom` utility with a choice of direct and possibly reverse FFT sizes. It produces a so called wisdom file `fftw-wisdom` that speeds up FFT allocations. It is created at a default location and will be used at next invocations of SDRangel. ![Main Window AMBE](../doc/img/MainWindow_fft.png) -
    1.3.1. FFTW Wisdom program
    +

    2.2.1: FFTW Wisdom program

    Path to the `fftwf-wisdom` program. It looks in the `PATH` variable and the "current" directory (that is the same directory where the `sdrangel` binary resides). -
    1.3.2. Select FFTW Wisdom program
    +

    2.2.2: Select FFTW Wisdom program

    Opens a file selection dialog to look for the `fftwf-wisdom` program. -
    1.3.3. FFT maximum size
    +

    2.2.3: FFT maximum size

    Select the maximum FFT size for precomputation. Sizes are powers of two between 128 and 32k. -
    1.3.4. Reverse FFT
    +

    2.2.4: Reverse FFT

    Select this checkbox to cover reverse FFTs -
    1.3.5. Command line
    +

    2.2.5: Command line

    This is the command line that will invoke the `fftwf-wisdom` program with its options -
    1.3.6. OK: launch the FFTW wisdom program
    +

    2.2.6: OK: launch the FFTW wisdom program

    When clicking the "OK" button the `fftwf-wisdom` program is launched in the background. A message pop-up appears with the PID of the process and the dialog exits. A new pop-up will appear with the output content when the program completes. -
    1.3.7. Cancel: dismiss the dialog without execution
    +

    2.2.7: Cancel: dismiss the dialog without execution

    When clicking the "Cancel" button the dialog is dismissed without execution of the `fftwf-wisdom` program. -

    1.4 Preferences - AMBE

    +

    2.3: AMBE

    When clicking on the AMBE submenu a dialog opens to let you specify physical AMBE devices to decode AMBE frames produced by digital voice signals (using DSD decoder plugin). ![Main Window AMBE](../doc/img/MainWindow_ambe.png) -
    1.4.1 AMBE server address and port or direct input
    +

    2.3.1: AMBE server address and port or direct input

    Use this freeflow text input box to specify either the address and port of an AMBE server in the form: <IPv4 address>:<port> or any directly attached physical device address like a COM port on Windows. -
    1.4.2 Import above address or device
    +

    2.3.2: Import above address or device

    Import the address or device specified in (1) into the list of used devices. The system will try to open the device or contact the server and will add it to the list only if successful. -
    1.4.3 Remove in use device or address
    +

    2.3.3: Remove in use device or address

    When a device or address is selected in the in use list (6) push this button to remove it from the list. The corresponding resources will be released. -
    1.4.4 Refresh in use list
    +

    2.3.4: Refresh in use list

    Checks the list of devices or addresses currently in use and update the in use list (6). -
    1.4.5 Empty in use list
    +

    2.3.5: Empty in use list

    Removes all devices or addresses in use. The in use list (6) is cleared consequently. This removes all AMBE devices related resources attached to the current instance of the SDRangel program. Therefore consecutive AMBE frames decoding will be handled by the mbelib library if available or no audio will be output. -
    1.4.6 In use list
    +

    2.3.6: In use list

    List of devices or addresses currently in use for AMBE frames decoding by this instance of the SDRangel program. -
    1.4.7 Import serial device
    +

    2.3.7: Import serial device

    Imports a serial device scanned in the list of available AMBE 3000 serial devices (9) in the in use list. If this device is already in the in use list then nothing happens and this is reported in the status text (10) -
    1.4.8 Import all serial devices
    +

    2.3.8: Import all serial devices

    Imports all serial devices scanned in the list of available AMBE 3000 serial devices (9) in the in use list. If any device is already in the in use list then it is not added twice. -
    1.4.9 List of available AMBE 3000 serial devices
    +

    2.3.9: List of available AMBE 3000 serial devices

    This is the list of AMBE 3000 currently attached to the system directly. This list gets updated at every opening of the dialog. -
    1.4.10 Status text
    +

    2.3.10: Status text

    A brief text reports the result of the current action -
    1.4.11 Close button
    +

    2.3.11: Close button

    Use this button to dismiss the dialog -

    1.5 Prefernces - LimeRFE

    - -Only if LimeSuite library is available this opens a dialog to control a LimeRFE device via USB. The details are provided [here](limerfeusbgui.md). - -

    1.6 Preferences - Devices - User arguments

    - -See the devuces user arguments management documentation [here](deviceuserargs.md). - -

    1.7. Help - Loaded plugins display

    - -When clicking on Help -> Loaded Plugins from the main menu bar a dialog box appears that shows information about the plugins loaded in SDRangel: - -![Main Window loaded plugins](../doc/img/MainWindow_loadedPlugins.png) - -
    Name
    - -Plugin display name. Tells briefly what this plugin is about. - -
    Version
    - -Starting with SDRangel version 2.0.0 this is the SDRangel version when the plugin was last updated. - -
    GPL
    - -Tells if the plugin is under GPL license. - -
    Expansion
    - -The plugin entry can be expanded or collapsed using the caret on the left. When expanded it shows more information about the copyright of the author and locations on the web where the plugin can be found. In all cases this is just here. - -
    OK button
    - -Click here when done to dismiss the dialog. - -

    2: Sampling devices

    - -This is where the plugin GUI specific to the device is displayed. - -![Sampling Device common](../doc/img/SampleDevice_common.png) - -**On the top of the dockable widget you have the following controls:** - -

    2.1: Change device

    - -Use this push button to open the device selection dialog to change the sampling device. This dialog will open: - -![Main Window sampling devices dialog](../doc/img/MainWindow_SDDialog.png) - -
    2.1.1: Device selection combo
    - -Use this combo box to select the device. Only available devices will appear in the list. For devices having more than one channel (ex: LimeSDR) the channel number will appear next to the device sequence number inside the brackets. Ex: `LimeSDR[0:1] 0009060B00473419` designates the second Rx (Rx #1) of the first encountered LimeSDR which serial number is 0009060B00473419. - -
    2.1.2: Device selection confirmation
    - -Use the `OK` button to confirm your choice and exit the dialog - -
    2.1.3: Device selection cancellation
    - -Use the `Cancel` button to exit the dialog without any change - -

    2.2: Reload currently selected device

    - -This button activates a close/open sequence to recycle the device. It may be useful when the device is not streaming anymore or in an attempt to clear possible errors. Make sure the streaming is stopped first. - -

    2.3: Current device identfier

    - -The identifier as shown in cotrol (2.1.1) appears truncated to the first 40 characters. - -**For every device there are the following common controls:** - -

    2.4. Start or stop acquisition

    - -
    2.4.1 left click
    - -Left click to start or stop streaming - - - When a play icon (▶) is displayed with a grey background the device is not operational - - When a play icon (▶) is displayed with a blue background the device is ready to start - - When a stop icon (■) is displayed with a green background the device is currently running - - When a play icon (▶) is displayed with a red background there is an error and a popup displays the error message. An Error typically occurs when you try to start the same device in more than one tab. - -
    2.4.2 right click
    - -Right click to control device reverse API. This dialog opens: - -![Basic device settings](../doc/img/BasicDeviceSettings.png) - -
    2.4.2.1: Toggle reverse API feature
    - -Use this checkbox to toggle on/off the reverse API feature. With reverse API engaged the changes in the device settings are forwarded to an API endpoint given by address (2.1.2.2), port (2.1.2.3) and device index (2.1.2.4) in the same format as the SDRangel REST API device settings endpoint. With the values of the screenshot the API URL is: `http://127.0.0.1:8888/sdrangel/deviceset/0/device/settings` The JSON payload follows the same format as the SDRangel REST API device settings. For example with HachRF Rx this would be something like: - -``` -{ - "deviceHwType": "HackRF", - "hackRFInputSettings": { - "LOppmTenths": 0, - "bandwidth": 1750000, - "biasT": 0, - "centerFrequency": 435000000, - "dcBlock": 0, - "devSampleRate": 2400000, - "fcPos": 2, - "iqCorrection": 0, - "linkTxFrequency": 0, - "lnaExt": 0, - "lnaGain": 16, - "log2Decim": 0, - "vgaGain": 16 - }, - "tx": 0 -} -``` -Note that the PATCH method is used. The full set of parameters is sent only when the reverse API is toggled on or a full settings update is done. - -The start and stop actions are also forwarded with the `/sdrangel/deviceset/{deviceSetIndex}/device/run` API endpoint using POST (start) or DELETE (stop) methods. - -More details on this feature can be found on the corresponding Wiki page. - -
    2.4.2.2: API address
    - -This is the IP address of the API endpoint - -
    2.4.2.3: API port
    - -This is the IP port of the API endpoint - -
    2.4.2.4: Device index
    - -This is the targeted device index - -
    2.4.2.5: Cancel changes and exit dialog
    - -Do not make any changes and exit dialog - -
    2.4.2.6: Validate and exit dialog
    - -Validates the data (saves it in the channel marker object) and exits the dialog - -

    2.5: Device sampling rate

    - -This is the sampling rate in kS/s of the I/Q stream extracted from the device after possible decimation. The main spectrum display corresponds to this sampling rate. - -

    2.6: Center frequency

    - -This is the current center frequency in kHz with dot separated thousands (MHz, GHz). On devices for which frequency can be directly controlled (i.e. all except File Source and Remote Input) you can use the thumbwheels to set the frequency. Thumbwheels move with the mouse wheel when hovering over a digit. - -When left clicking on a digit a cursor is set on it and you can also use the arrows to move the corresponding thumbwheel. - -When right clicking on a digit the digits on the right are all set to zero. This effectively does a ceil rounding at the current position. - -

    Additional inputs

    - -Most devices will also present an interface to control automatic DC removal and I/Q imbalance and also a control of the LO correction in ppm. - - - Example1: ![Sampling Device corr 1](../doc/img/SampleDevice_corr01.png) - - Example2: ![Sampling Device corr 2](../doc/img/SampleDevice_corr02.png) - -

    3. Spectrum display control

    - -![Spectrum GUI](../doc/img/MainWindow_spectrum_gui.png) - -These are the controls of the main spectrum display in (7). The same controls are found in the plugins that feature a spectrum display. For example: - - Channel Analyzer - - Broadcast FM demodulator - - ChirpChat (LoRa) demodulator - - File sink - - FreeDV demodulator and modulator - - Frequency tracker - - SigMF file sink - - SSB demodulator and modulator - - UDP source and sink - -

    3A. Spectrum display control line 1

    - -![Spectrum GUI A](../doc/img/MainWindow_spectrum_gui_A.png) - -

    3A.1. FFT window selector

    - -Use this combo box to select which window is applied to the FFT: - - **Bart**: Bartlett - - **B-H**: Blackmann-Harris - - **FT**: Flat top - - **Ham**: Hamming - - **Han**: Hanning (default) - - **Rec**: Rectangular (no window) - - **Kai**: Kaiser with alpha = 2.15 (beta = 6.76) gives sidelobes < -70dB - -

    3A.2. FFT size

    - -Select the size of the FFT window among these values: - - 128 - - 256 - - 512 - - 1k = 1024 (default) - - 2k = 2048 - - 4k = 4096 - -

    3A.3. FFT Overlap

    - -FFT Overlap in number of samples over half of the FFT size. The percentage of overlap appears in the tooltip. Ranges from 0 (no overlap) to half the FFT size minus one (maximum overlap). - -Example with a FFT of 1k (1024) and an overlap of 128 the overlap percentage is 128 ÷ 512 = 25% - -

    3A.4. Averaging mode

    - -Use this combo to select which averaging mode is applied: - - **No**: no averaging. Disables averaging regardless of the number of averaged samples (4.6). This is the default option - - **Mov**: moving average. This is a sliding average over the amount of samples specified next (4.6). There is one complete FFT line produced at every FFT sampling period - - **Fix**: fixed average. Average is done over the amount of samples specified next (4.6) and a result is produced at the end of the corresponding period then the next block of averaged samples is processed. There is one complete FFT line produced every FFT sampling period multiplied by the number of averaged samples (4.6). The time scale on the waterfall display is updated accordingly. - - **Max**: this is not an averaging but a max hold. It will retain the maximum value over the amount of samples specified next (4.6). Similarly to the fixed average a result is produced at the end of the corresponding period which results in slowing down the waterfall display. The point of this mode is to make outlying short bursts within the "averaging" period stand out. With averaging they would only cause a modest increase and could be missed out. - -

    3A.5. Number of averaged samples

    - -Each FFT bin (squared magnitude) is averaged or max'ed over a number of samples. This combo allows selecting the number of samples between these values: 1 (no averaging), 2, 5, 10, 20, 50, 100, 200, 500, 1k (1000) for all modes and in addition 2k, 5k, 10k, 20k, 50k, 1e5 (100000), 2e5, 5e5, 1M (1000000) for "fixed" and "max" modes. Averaging reduces the noise variance and can be used to better detect weak continuous signals. The fixed averaging mode allows long time monitoring on the waterfall. The max mode helps showing short bursts that may appear during the "averaging" period. - -The resulting spectrum refresh period appears in the tooltip taking sample rate, FFT size (3A.2), average size (3A.5) and overlap (3A.3) into consideration. Averaging size adjustment is valid for fixed average and max modes only: - -Period = ((((FFT_size ÷ 2) - overlap) × 2) ÷ sample_rate) × averaging_size - -

    3A.6. Play/Pause spectrum

    - -Use this button to freeze the spectrum update. Useful when making measurements (Paragraph 6). - -

    3B. Spectrum display control line 2

    - -![Spectrum GUI B](../doc/img/MainWindow_spectrum_gui_B.png) - -

    3B.1. Autoscale

    - -Scales spectrum by setting reference level and range automatically based on maximum and minimum levels. Takes the average of FFT size ÷ 32 minima for the minimum and 10 dB over maximum for the maximum. - -

    3B.2. Reference level

    - -This is the level in dB at the top of the display range. You can select values between 0 and -110 in 1 dB steps - -

    3B.3. Range

    - -This is the range of display in dB. You can select values between 1 and 100 in 1 dB steps - -

    3B.4. FPS capping

    - -The refresh rate of the spectrum is capped by this value in FPS i.e the refresh period in seconds is 1 ÷ FPS. The default value is 20 and corresponds to general usage. You may use a lower value to limit GPU usage and power consumption. You may also use a higher value for an even more reactive display. "NL" corresponds to "No Limit". With "No Limit" the spectrum update will be triggered immediately when a new FFT is calculated. Note that actual refresh rate will be limited by other factors related to hardware and graphics drivers. - -The refresh period is limited anyway by the FFT period which is the FFT size divided by the baseband sampling rate and multiplied by the fixed average or max size (3A.5) in case these features are engaged (3A.4). Setting a resulting FFT refresh time above the refresh rate will make sure that a short burst is not missed particularly when using the max mode. - -Example with a FFT size of 1k (1024) and no overlap, a baseband rate of 48 kS/s and an averaging size of 5 the refresh period is: - -(1024 ÷ 48000) × 5 ≈ 107 ms - -Thus if the FPS capping is 20 (50 ms) the refresh period will be in fact 107 ms (≈ 9 FPS) anyway. - -

    3B.5. Logarithmic/linear scale

    - -Use this toggle button to switch between spectrum logarithmic and linear scale display. The face of the button will change to represent either a logaritmic or linear curve. - -When in linear mode the range control (4.4) has no effect because the actual range is between 0 and the reference level. The reference level in dB (4.3) still applies but is translated to a linear value e.g -40 dB is 1e-4. In linear mode the scale numbers are formatted using scientific notation so that they always occupy the same space. - -

    3B.6. Spectrum server control

    - -A websockets based server can be used to send spectrum data to clients. An example of such client can be found in the [SDRangelSpectrum](https://github.com/f4exb/sdrangelspectrum) project. - - - Left button: toggles server on/off - - Right button: opens a secondary dialog that lets you choose the server listening (local) address and port. - -The server only sends data. Control including FFT details is done via the REST API. FFT frames are formatted as follows (in bytes): - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    OffsetLengthValue
    08Center frequency in Hz as 64 bit integer
    88Effective FFT time in milliseconds as 64 bit integer
    168Unix timestamp in milliseconds as 64 bit integer
    244FFT size as 32 bit integer
    284FFT bandwidth in Hz as 32 bit integer
    324 - Indicators as 32 bit integer LSB to MSB: -
      -
    • bit 0: Linear (1) / log (0) spectrum indicator
    • -
    • bit 1: SSB (1) / DSB (0) spectrum indicator
    • -
    • bit 2: USB (1) / LSB (0) spectrum indicator
    • -
    -
    36N*4Vector of N = FFT size 32 bit floating point spectrum power values either log (dB) or linear
    - -

    3B.7. Spectrum markers dialog

    - -Opens the [spectrum markers dialog](spectrummarkers.md) - -

    3B.8. Spectrum calibration

    - -Use the toggle button to switch between relative and calibrated power readings. - -Right click to open the [calibration management dialog](spectrumcalibration.md) - -

    3C. Spectrum display control line 3

    - -![Spectrum GUI C](../doc/img/MainWindow_spectrum_gui_C.png) - -

    3C.1. Clear spectrum

    - -This resets the maximum spectrum trace and phosphor remanence - -

    3C.2. Phosphor display

    - -Toggles the phosphor display on the spectrum - -

    3C.3. Phosphor display stroke and max hold decay

    - -This controls the decay rate of the stroke when phosphor display is engaged (4.C). The histogram pixel value is diminished by this value each time a new FFT is produced. A value of zero means no decay and thus phosphor history and max hold (red line) will be kept until the clear button (4.B) is pressed. - -

    3C.4. Phosphor display stroke and max hold decay divisor

    - -When phosphor display is engaged (4.C) and stroke decay is 1 (4.7) this divides the unit decay by this value by diminishing histogram pixel value by one each time a number of FFTs equal to this number have been produced. Thus the actual decay rate is 1 over this value. This allow setting a slower decay rate than one unit for each new FFT. - -

    3C.5. Phosphor display stroke strength

    - -This controls the stroke strength when phosphor display is engaged (4.C). The histogram value is incremented by this value at each new FFT until the maximum (red) is reached. - -

    3C.6. Maximum hold trace

    - -Toggles the maximum hold trace display (red trace) on the spectrum - -

    3C.7. Current trace

    - -Toggles the current trace display (yellow trace) on the spectrum - -

    3C.8. Trace intensity

    - -This controls the intensity of the maximum (4.D) and current (4.E) spectrum trace - -

    3C.9. Waterfall/spectrum placement

    - -Toggles the spectrum on top or on bottom versus waterfall - -

    3C.10. Waterfall

    - -Toggles the waterfall display - -

    3C.11.Grid

    - -Toggles the grid display - -

    3C.12.Grid intensity

    - -Controls the intensity of the grid display - -

    5. Presets and commands

    - -The presets and commands tree view are by default stacked in tabs. The following sections describe the presets section 5A) and commands (section 5B) views successively - -

    4A. Presets

    - -This is a tree view of the saved presets. Presets record the channels setup and a copy of the settings of each sample source that has been used when saving this preset. Thus you can use the same channel arrangement with various devices having their particular setup. - -![Main Window presets view](../doc/img/MainWindow_presets_view.png) - -

    4A.1. Preset selection

    - -You select a preset or a preset group by clicking on its line in the tree view. All actions (6) will be done relative to this preset or preset group. - -

    4A.2. Group

    - -You can organize your presets into groups. Groups can be collapsed or expanded by using the caret icon on the left. - -

    4A.3. Center frequency

    - -The center frequency used in this preset is displayed here. - -

    4A.4. Rx/Tx indicator

    - -"R" is displayed for a Rx device set and "T" for a Tx device set - -

    4A.5. Preset name

    - -You can give a name to your preset. Names need not to be unique. - -

    4A.6. Preset control or actions

    - -The controls are located as icons at the bottom of the window: - -![Main Window presets](../doc/img/MainWindow_presets.png) - -
    4A.6.1. New preset
    - -Click on this icon to create a new preset with the current values in the selected sample device tab (Main window: 2). - -
    4A.6.2. Update preset
    - -Click on this icon to create a update the selected preset with the current values in the selected sample device tab (Main window: 2). Please note that this does not save the preset immediately on disk to save presets immediately you need to use the save button (4). - -
    4A.6.3. Edit preset
    - -Opens a new window where you can change the group name and description. - - - for group items you can rename the group or merge all group presets into an existing group by selecting this existing group - - for preset items you can: - - move the preset to another existing group by selecting this existing group - - assign this preset to a new group by typing in this new group - - change the description - -
    4A.6.4. Save presets
    - -Presets are saved to disk automatically at exit time you can however request to save them immediately using this icon. - -
    4A.6.5. Export preset
    - -Using the previous icon presets are saved globally in a system dependent place. Using this icon you can export a specific preset in a single file that can be imported on another machine possibly with a different O/S. The preset binary data (BLOB) is saved in Base-64 format. - -
    4A.6.6. Import preset
    - -This is the opposite of the previous operation. This will create a new preset in the selected group or the same group as the preset being selected. - -
    4A.6.7. Delete preset
    - - - on a preset item: deletes the selected preset. - - on a preset group: deletes the group and all its presets. - -
    4A.6.8. Load preset
    - -Applies the selected preset to the current device set (source and channel plugins). - -

    4B. Commands

    +

    2.4: Commands

    This is a tree view of the saved commands. Commands describe the path to an executable file, its arguments a possible link to a keystroke event that triggers the execution. Similarly to presets commands can be arranged into groups and have a description short text. @@ -673,43 +279,43 @@ Of course any binary that resides in your system can be used that way like `/bin ![Main Window presets view](../doc/img/MainWindow_commands_view.png) -

    4B.1. Command selection

    +

    2.4.1: Command selection

    You select a command or a command group by clicking on its line in the tree view. All actions (6) will be done relative to this command or command group. -

    4B.2. Group

    +

    2.4.2: Group

    You can organize your commands into groups. Groups can be collapsed or expanded by using the caret icon on the left. -

    4B.3. Description

    +

    2.4.3: Description

    Short description of a command. -

    4B.4. Key binding indicator

    +

    2.4.4: Key binding indicator

    - `-`: no key binding - `P`: key press binding - `R`: key release binding -

    4B.5. Key binding sequence

    +

    2.4.5: Key binding sequence

    -This is a descriptive text of the key sequence that is used for the key binding. +Thisis a descriptive text of the key sequence that is used for the key binding. -

    4B.6. Command control or actions

    +

    2.4.6: Command control or actions

    The controls are located as icons at the bottom of the window: ![Main Window commands](../doc/img/MainWindow_commands.png) -
    4B.6.1. Create new command
    +
    2.4.6.1: Create new command
    Click on this icon to create a new command. This opens an edit dialog see the edit section (5B.6.3) for the details of the edit dialog. -
    4B.6.2. Duplicate command
    +
    2.4.6.2: Duplicate command
    Click on this icon to duplicate the currently selected command (inactive on groups). Later you can edit the details of the copy with the edit dialog (see 5B.6.3 next) -
    4B.6.3. Edit command or command group
    +
    2.4.6.3: Edit command or command group
    Command groups @@ -723,15 +329,15 @@ You can edit the details of the command with this dialog. ![Main Window command group edit](../doc/img/MainWindow_command_edit.png) -
    4B.6.3.1. Edit group
    +
    2.4.6.3.1: Edit group
    You can select an existing group with the combo or create a new one for this command using the text edit box -
    4B.6.3.2. Edit description
    +
    2.4.6.3.2: Edit description
    You can edit the description using this text box. The description will appear in the tree view. -
    4B.6.3.3. Executable file selection
    +
    2.4.6.3.3: Executable file selection
    Clicking on this button will open a file dialog to select the executable file that will be run with this command. The file selection dialog has predefined file pattern selections: @@ -740,11 +346,11 @@ Clicking on this button will open a file dialog to select the executable file th - `*.sh` or `*.bat` for shell or batch files - `*.bin` or `*.exe` for binary files -
    4B.6.3.4. Executable file path
    +
    2.4.6.3.4: Executable file path
    This is the full path of the selected executable file. -
    4B.6.3.5. Command line arguments
    +
    2.4.6.3.5: Command line arguments
    Use the text box to edit the arguments given to the executable file as in `program arguments`. @@ -754,41 +360,41 @@ You can use special codes to insert information specific to the application cont - `%2`: the port of the web REST API - `%3`: the currently selected device set index -
    4B.6.3.6. Key binding
    +
    2.4.6.3.6: Key binding
    Use this checkbox to enable or disable the command execution binding to a key or combination of keys press or release event -
    4B.6.3.7. Key binding capture
    +
    2.4.6.3.7: Key binding capture
    Use this button to capture the key or key combination that will be used for the key binding. After pushing this button just type in the key or key combination. -
    4B.6.3.8. Key binding display
    +
    2.4.6.3.8: Key binding display
    This shows the key or combination of keys used for the key binding. -
    4B.6.3.9. Release key binding
    +
    2.4.6.3.9: Release key binding
    Use this checkbox to bind the key or combination of keys to the key release event. If unchecked the binding will be associated to the key press event. -
    4B.6.3.10. Confirm changes
    +
    2.4.6.3.10: Confirm changes
    Use the "OK" button to confirm the changes. -
    4B.6.3.11. Cancel changes
    +
    2.4.6.3.11: Cancel changes
    Use the "Cancel" button to cancel the changes. -
    4B.6.4. Run command or groups of commands
    +
    2.4.6.4: Run command or groups of commands
    This will run the currently selected command. If the selection is a group it will run all commands of the group starting them in the displayed order. Please note that commands are run in independent processes and therefore all launched commands in the group will run concurrently. -
    4B.6.5. View last command run details
    +
    2.4.6.5: View last command run details
    This dialog will show the results of the last run including the output (merged stdout and stderr). ![Main Window command output](../doc/img/MainWindow_command_output.png) -
    4B.6.5.1. Process status
    +
    2.4.6.5.1: Process status
    When the process is not running the stop icon (■) is displayed. The background color indicate different states: @@ -798,31 +404,31 @@ When the process is not running the stop icon (■) is displayed. The backgr When the process is running the play icon (▶) is displayed with an orange background. -
    4B.6.5.2. Refresh data
    +
    2.4.6.5.2: Refresh data
    Pushing this button will update the data displayed with the latest status. Please note that the log is displayed only when the process is terminated. -
    4B.6.5.3. Start time
    +
    2.4.6.5.3: Start time
    This is the timestamp of process start. It is filled with dots `...` if the process has never started during this session. -
    4B.6.5.4. End time
    +
    2.4.6.5.4: End time
    This is the timestamp of process end. It is filled with dots `...` if the process has never terminated during this session. -
    4B.6.5.3. PID
    +
    2.4.6.5.3: PID
    This is the process PID. It is 0 if the process has never run during this session. -
    4B.6.5.6. Process kill
    +
    2.4.6.5.6: Process kill
    Use this button to kill (send SIGKILL) the running process. It has no effect if the process is not running. -
    4B.6.5.7. Command line
    +
    2.4.6.5.7: Command line
    This shows the actual command line that was used to start the process -
    4B.6.5.8. Error status
    +
    2.4.6.5.8: Error status
    This is the translation of `QProcess::ProcessError`. Possible values are: @@ -834,372 +440,59 @@ This is the translation of `QProcess::ProcessError`. Possible values are: - `Read error`: an error occurred when attempting to read from the process. For example, the process may not be running. - `Unknown error`: an unknown error occurred. -
    4B.6.5.9. Exit code
    +
    2.4.6.5.9: Exit code
    This is the program exit code. When the process crashes this is the signal by which the process end was caused. For example if you kill the process with button (6) it sends the process a SIGKILL (code 9) and therefore the value is 9. -
    4B.6.5.10. Exit status
    +
    2.4.6.5.10: Exit status
    There are only two possibilities: either the program exits normally but possibly with a non zero exit code or it ends with a crash. -
    4B.6.5.11. Process log
    +
    2.4.6.5.11: Process log
    This is the log of the process (merged stdout and stderr). Please note that it is updated only on program termination. -
    4B.6.5.12. Exit
    +
    2.4.6.5.12: Exit
    By pushing the "Close" button the process output window is closed. -
    4B.6.6. Save commands
    +
    2.4.6.6: Save commands
    This will save the commands immediately. The commands will be automatically saved when the application exits normally. -
    4B.6.7. Delete commands or group of commands
    +
    2.4.6.7: Delete commands or group of commands
    This will delete the currently selected command or if selection is a group this will delete all commands in the group. -
    4B.6.8. Activate keyboard bindings
    +
    2.4.6.8: Activate keyboard bindings
    Use this button to activate the keyboard bindings. Note that you need to have this button selected (its background should be lit in beige/orange) for the key bindings to be effective. -

    5. Channels

    +

    3: Help

    -This area shows the control GUIs of the channels currently active for the device. When the preset is saved (as default at exit time or as a saved preset) the GUIs are ordered by increasing frequency. If presets share the same frequency they are ordered by their internal ID name. Thus new channel GUIs will appear ordered only when reloaded. +

    3.1: Loaded plugins

    -Details about the GUIs can be found in the channel plugins documentation which consists of a readme.md file in each of the channel plugins folder (done partially). +When clicking on Help -> Loaded Plugins from the main menu bar a dialog box appears that shows information about the plugins loaded in SDRangel: -

    5.1: Channel dock top bar - Add channels

    +![Main Window loaded plugins](../doc/img/MainWindow_loadedPlugins.png) -Channels are added by clicking on the circled "+" icon (1) on the top bar of the channels dockable widget: +

    Name

    -![Channels dock](../doc/img/Channels_dock.png) +Plugin display name. Tells briefly what this plugin is about. -This opens the following dialog: +

    Version

    -![Add Channels dialog](../doc/img/AddChannels_dialog.png) +Starting with SDRangel version 2.0.0 this is the SDRangel version when the plugin was last updated. -
    5.1.1: Channel selection
    +

    GPL

    -Use this combo to select which channel type to add +Tells if the plugin is under GPL license. -
    5.1.2: Close dialog
    +

    Expansion

    -Use this button to dismiss the dialog +The plugin entry can be expanded or collapsed using the caret on the left. When expanded it shows more information about the copyright of the author and locations on the web where the plugin can be found. In all cases this is just here. -
    5.1.3: Add channel
    +

    OK button

    -Add a new channel by clicking on the `Apply` button. You may click it several times to add more channels. The dialog can be dismissed with the `Close` button or the closing window icon `X` on the top bar. +Click here when done to dismiss the dialog. -

    5.2. Basic channel settings

    - -![Channel control 01](../doc/img/MainWindow_channel_01.png) - -With most channel types some common basic settings can be set with a popup dialog. This dialog is opened by clicking on the small grey square on the top left of the channel window. The settings are as follows: - -![Basic channel settings](../doc/img/BasicChannelSettings.png) - -
    5.1.1: Window title
    - -Changes the channel window title - -
    5.1.2: Channel color
    - -Changes the color of the window title bar and spectrum overlay. To change the color click on the color square to open a color chooser dialog. The hex rgb value is displayed next to the color square. - -
    5.1.3: Frequency scale display type
    - -When the mouse is over the channel window or over the central line in the spectrum a channel parameter is displayed on the frequency scale. This parameter can be: - - - Freq: channel absolute center frequency - - Title: channel window title - - AdSnd: UDP address and send port - - AdRcv: UDP address and receive port - -
    5.1.4: Toggle reverse API feature
    - -Use this checkbox to toggle on/off the reverse API feature. With reverse API engaged the changes in the channel settings are forwarded to an API endpoint given by address (5.1.5), port (5.1.6), device index (5.1.7) and channel index (5.1.8) in the same format as the SDRangel REST API channel settings endpoint. With the values of the screenshot the API URL is: `http://127.0.0.1:8888/sdrangel/deviceset/0/channel/0/settings` The JSON payload follows the same format as the SDRangel REST API channel settings. Using the same example this would be: - -``` -{ - "SSBDemodSettings": { - "agc": 0, - "agcClamping": 0, - "agcPowerThreshold": -40, - "agcThresholdGate": 4, - "agcTimeLog2": 7, - "audioBinaural": 0, - "audioDeviceName": "System default device", - "audioFlipChannels": 0, - "audioMute": 0, - "dsb": 0, - "inputFrequencyOffset": 0, - "lowCutoff": 300, - "rfBandwidth": 3000, - "rgbColor": -16711936, - "spanLog2": 3, - "title": "SSB Demodulator", - "volume": 3 - }, - "channelType": "SSBDemod", - "tx": 0 -} -``` -Note that the PATCH method is used. The full set of parameters is sent only when the reverse API is toggled on or a full settings update is done. - -More details on this feature can be found on the corresponding Wiki page. - -
    5.1.5: API address
    - -This is the IP address of the API endpoint - -
    5.1.6: API port
    - -This is the IP port of the API endpoint - -
    5.1.7: Device index
    - -This is the targeted device index - -
    5.1.8: Channel index
    - -This is the targeted channel index - -
    5.1.9: Cancel changes and exit dialog
    - -Do not make any changes and exit dialog - -
    5.1.10: Validate and exit dialog
    - -Validates the data (saves it in the channel marker object) and exits the dialog - -

    5.2 Device stream assignment

    - -![Channel control 02](../doc/img/MainWindow_channel_02.png) - -The bigger square next to the leftmost "c" square is the device stream assignment control. With single Rx (source device set) and single Tx devices (sink device set) this is inactive because the channel is simply connected to the single stream as shown by the "S" letter. - -This is in place for future MIMO devices and channels support (v.5). - -

    6. Spectrum from device

    - -This shows the spectrum in the passband returned from the sampling device possibly after decimation. The actual sample rate is shown in the device control at the left of the frequency display (2.3) - -The spectrum display is controlled by the display control (4). - -

    Status line

    - -![Spectrum Statuss](../doc/img/Spectrum_Status.png) - -A status line is displayed at the left of the top margin. It displays the following items from left to right: - - - if frequency zooming is active the zooming factor - - `CF:` followed by the Center Frequency of the displayed spectrum possibly with multiplier suffix (G, M, k) - - `SP:` followed by the frequency SPan of the displayed spectrum possibly with multiplier suffix (M, k) - -

    Spectrum markers

    - -![Spectrum Markers](../doc/img/Spectrum_Markers.png) - -Note that spectrum markers appear only when spectrum display is active (shoes data) - -The spectrum markers can be set either with the mouse as explained next or with the [spectrum markers dialog](spectrummarkers.md) that can be opened with the markers button (3B.7). - -Use Shift and mouse left click to set a new marker. There is a maximum of two markers with a different status: - - The first marker will display frequency (2) and power (1) or time (5) on the scale side of the view. Frequency units are the same as displayed in the frequency scale. - - The second marker will display frequency difference (3 or 6) and power difference (4) or time difference (7) from the first marker on the opposite side of the scales. Difference values may be suffixed with a multiplier character. - -Base units are Hz for frequency difference and seconds for time. Power is expressed either in dB or plain value depending on the linear or log setting for the spectrum display. - -Values may be suffixed by a multiplier character: - - **p**: pico (times 1e-12) - - **n**: nano (times 1e-9) - - **u**: micro (times 1e-6) - - **m**: milli (times 1e-3) - - no character: no change (times one) - - **k**: kilo (times 1e3) - - **M**: mega (times 1e6) - - **G**: giga (times 1e9) - -Use mouse right click anywhere in the view to remove the last entered marker. Use shift and mouse right click to remove all markers. - -

    Mouse scroll wheel

    - -![Spectrum Mousewheel](../doc/img/MainWindow_spectrum_mousewheel.png) - -
    A: Channel moving
    - -When the mouse is over the center line of a channel: - - - scrolling will move the channel by +/- 10 Hz at each scroll up/down respectively - - combined with Ctrl it will move the channel by +/- 100 Hz - - combined with Shift it will move the channel by +/- 1 kHz - -
    B: Frequency zooming
    - -When the mouse is in the spectrum or waterfall area but not over the center line of a channel it will zoom in/out along X (frequency) axis by a 0.5 step at each scroll up/down respectively between 1x (no zoom) and 10x. Note that in order to zoom on the center line of a channel you may move the mouse pointer in the top margin (center line moving is not active there but zooming is). - -When frequency zooming is active use Alt + left click to move the center frequency to the clicked point. - -
    C: Power zooming
    - -When the mouse is inside the power scale (spectrum) the power range is decreased by 2 (zoom in) or increased by 2 (zoom in) at each wheel step forward or backward respectively. The behavior of the reference level depends on where in the scale is the mouse pointer: - - - in the top third: the reference level is maintained thus the reference level at the top stays the same - - in the middle third: the reference level is decreased by 1 (zoom in) or increased by 1 (zoom out) at each wheel step forward or backward thus the level in the middle stays the same - - in the bottom third: the reference level is decreased by 2 (zoom in) or increased by 2 (zoom out) at each wheel step forward or backward thus the level at the bottom stays the same - -
    D: Time zooming
    - -When the mouse is inside the time scale (waterfall) the overlap is increased by 1 (zoom in) or decreased by 1 (zoom out) at each wheel step forward or backward respectively. Overlap is bounded by 0 and half of the FFT size minus one. - -

    7. Status

    - -![Main Window status](../doc/img/MainWindow_status.png) - -

    7.1. SDRangel version

    - -This is the current tag or the latest tag followed by the number of commits since the latest tag followed by the git commit SHA1 (8 hex characters) preceded by 'g'. Ex: `v4.5.3-29-gf5f2349d` - -

    7.2. Qt version

    - -Qt version with which this copy of SDRangel was compiled. - -

    7.3. Architecture

    - -Codename of the CPU architecture in which SDRangel is running. - -

    7.4. Operating system

    - -Pretty print of the operating system in which SDRangel is running. - -

    7.5. Local date and time

    - -Local time timestamp according to system clock. Format: `yyyy-mm-dd HH:MM:ss TZ` - -

    8. Features

    - -Feature plugins implement pieces of functionality not directly related to the I/Q stream(s) DSP processing and not part of the core functionality. It can control and interact devices and channels to achieve this specialzed piece of functionality. Examples are PTT (Push To Talk) to switchover Rx/Tx device sets as you would do with a transciever or control via rigctl protocol. - -This area shows the feature GUIs of the features currently instantiated (active). The top bar has the following controls: - -![Features dock top](../doc/img/Features_top.png) - -

    8.1 Add feature

    - -Click on this button to open a dialog to add features. The dialog function is similarl to the add channels dialog in section 5.1: - -![Features add dialog](../doc/img/Features_add.png) - -
    8.1.1: Feature selection
    - -Use this combo to select which feature type to add - -
    8.1.2: Close dialog
    - -Use this button to dismiss the dialog - -
    8.1.3: Add feature
    - -Add a new feature by clicking on the `Apply` button. You may click it several times to add more feature instances. The dialog can be dismissed with the `Close` button or the closing window icon `X` on the top bar. - -

    8.2: Basic features settings

    - -![Features top button](../doc/img/Features_basic.png) - -At the left of the top bar of every feature GUI a "c" button opens a dialog to set some parameters common to all feature plugins: - -![Basic channel settings](../doc/img/Features_basic_dialog.png) - -
    8.2.1: Window title
    - -Changes the channel window title - -
    8.2.2: Feature color
    - -Changes the color of the window title bar. To change the color click on the color square to open a color chooser dialog. The hex rgb value is displayed next to the color square. - -
    8.2.3: Toggle reverse API feature
    - -Use this checkbox to toggle on/off the reverse API feature. With reverse API engaged the changes in the feature settings are forwarded to an API endpoint given by address (8.2.4), port (8.2.5), feature set index (8.2.6) and feature index (8.2.7) in the same format as the SDRangel REST API feature settings endpoint. With the values of the screenshot the API URL is: `http://127.0.0.1:8888/sdrangel/featureeset/0/feature/0/settings` The JSON payload follows the same format as the SDRangel REST API feature settings. Using the same example this would be: - -``` -{ - "SimplePTTSettings": { - "reverseAPIAddress": "127.0.0.1", - "reverseAPIChannelIndex": 0, - "reverseAPIDeviceIndex": 0, - "reverseAPIPort": 8888, - "rgbColor": -65536, - "rx2TxDelayMs": 200, - "rxDeviceSetIndex": 0, - "title": "Simple PTT", - "tx2RxDelayMs": 200, - "txDeviceSetIndex": 1, - "useReverseAPI": 0 - }, - "featureType": "SimplePTT" -} -``` -Note that the PATCH method is used. The full set of parameters is sent with the PUT method only when the reverse API is toggled on or a full settings update is done. - -
    8.2.4: API address
    - -This is the IP address of the API endpoint - -
    8.2.5: API port
    - -This is the IP port of the API endpoint - -
    8.2.6: Feature set index
    - -This is the targeted feature set index - -
    8.2.7: Feature index
    - -This is the targeted feature index - -
    8.2.8: Cancel changes and exit dialog
    - -Do not make any changes and exit dialog - -
    8.2.9: Validate and exit dialog
    - -Validates the data and exits the dialog - -

    8.3: Presets dialog

    - -Feature sets (groups) can be saved to and retrieved from specialized feature presets - -![Features presets dialog](../doc/img/Features_presets.png) - -
    8.3.1: Feature selection
    - -Move the cursor to select a feature. Features can be organized into groups at the top level (here "Test"). When selecting a group only Edit and Delete group are available - -
    8.3.2: Add new preset
    - -Save the current feature set in a new preset. - -
    8.3.3: Update selected preset
    - -Update the selected preset with the current feature set - -
    8.3.4: Save presets
    - -This button is inactive. All presets are saved at program exit or with the Presets save button. - -
    8.3.5: Edit preset
    - -Change preset name or the preset group to which this preset belongs. If selection is a group the group name can be changed. - -
    8.3.6: Delete preset
    - -Delete selected preset or selected group - -
    8.3.7: Load preset
    - -Load preset in the current feature set. The Features that were present before are dismissed. - -
    8.3.8: Close dialog
    - -This button dismisses the dialog. From 249db03cf3b1d9fa6c866161e9b19606cb3222f3 Mon Sep 17 00:00:00 2001 From: f4exb Date: Wed, 20 Apr 2022 20:08:41 +0200 Subject: [PATCH 042/115] Massive UI revamping (v7): fixed device workspace index save/restore and removed useless geometry blobs. Fixes #1207 --- plugins/samplemimo/bladerf2mimo/bladerf2mimogui.h | 4 ---- plugins/samplemimo/bladerf2mimo/bladerf2mimosettings.cpp | 8 -------- plugins/samplemimo/bladerf2mimo/bladerf2mimosettings.h | 2 -- plugins/samplemimo/limesdrmimo/limesdrmimogui.h | 4 ---- plugins/samplemimo/limesdrmimo/limesdrmimosettings.cpp | 8 -------- plugins/samplemimo/limesdrmimo/limesdrmimosettings.h | 3 --- plugins/samplemimo/metismiso/metismisogui.h | 4 ---- plugins/samplemimo/metismiso/metismisosettings.cpp | 5 ----- plugins/samplemimo/metismiso/metismisosettings.h | 2 -- plugins/samplemimo/plutosdrmimo/plutosdrmimogui.h | 4 ---- plugins/samplemimo/plutosdrmimo/plutosdrmimosettings.cpp | 7 ------- plugins/samplemimo/plutosdrmimo/plutosdrmimosettings.h | 2 -- plugins/samplemimo/testmi/testmigui.h | 4 ---- plugins/samplemimo/testmi/testmisettings.cpp | 6 ------ plugins/samplemimo/testmi/testmisettings.h | 2 -- plugins/samplemimo/testmosync/testmosyncgui.h | 4 ---- plugins/samplemimo/testmosync/testmosyncsettings.cpp | 5 ----- plugins/samplemimo/testmosync/testmosyncsettings.h | 4 ---- plugins/samplemimo/xtrxmimo/xtrxmimogui.h | 4 ---- plugins/samplemimo/xtrxmimo/xtrxmimosettings.cpp | 5 ----- plugins/samplemimo/xtrxmimo/xtrxmimosettings.h | 3 --- plugins/samplesink/audiooutput/audiooutputgui.h | 4 ---- plugins/samplesink/audiooutput/audiooutputsettings.cpp | 5 ----- plugins/samplesink/audiooutput/audiooutputsettings.h | 2 -- plugins/samplesink/bladerf1output/bladerf1outputgui.h | 4 ---- .../samplesink/bladerf1output/bladerf1outputsettings.cpp | 5 ----- .../samplesink/bladerf1output/bladerf1outputsettings.h | 2 -- plugins/samplesink/bladerf2output/bladerf2outputgui.h | 4 ---- .../samplesink/bladerf2output/bladerf2outputsettings.cpp | 5 ----- .../samplesink/bladerf2output/bladerf2outputsettings.h | 2 -- plugins/samplesink/fileoutput/fileoutputgui.h | 4 ---- plugins/samplesink/fileoutput/fileoutputsettings.cpp | 5 ----- plugins/samplesink/fileoutput/fileoutputsettings.h | 4 ---- plugins/samplesink/hackrfoutput/hackrfoutputgui.h | 4 ---- plugins/samplesink/hackrfoutput/hackrfoutputsettings.cpp | 5 ----- plugins/samplesink/hackrfoutput/hackrfoutputsettings.h | 2 -- plugins/samplesink/limesdroutput/limesdroutputgui.h | 4 ---- .../samplesink/limesdroutput/limesdroutputsettings.cpp | 5 ----- plugins/samplesink/limesdroutput/limesdroutputsettings.h | 2 -- plugins/samplesink/localoutput/localoutputgui.h | 4 ---- plugins/samplesink/localoutput/localoutputsettings.cpp | 5 ----- plugins/samplesink/localoutput/localoutputsettings.h | 2 -- plugins/samplesink/plutosdroutput/plutosdroutputgui.h | 4 ---- .../samplesink/plutosdroutput/plutosdroutputsettings.cpp | 5 ----- .../samplesink/plutosdroutput/plutosdroutputsettings.h | 2 -- plugins/samplesink/remoteoutput/remoteoutputgui.h | 4 ---- plugins/samplesink/remoteoutput/remoteoutputsettings.cpp | 5 ----- plugins/samplesink/remoteoutput/remoteoutputsettings.h | 2 -- plugins/samplesink/soapysdroutput/soapysdroutputgui.h | 4 ---- .../samplesink/soapysdroutput/soapysdroutputsettings.cpp | 5 ----- .../samplesink/soapysdroutput/soapysdroutputsettings.h | 2 -- plugins/samplesink/testsink/testsinkgui.h | 4 ---- plugins/samplesink/testsink/testsinksettings.cpp | 5 ----- plugins/samplesink/testsink/testsinksettings.h | 2 -- plugins/samplesink/usrpoutput/usrpoutputgui.h | 4 ---- plugins/samplesink/usrpoutput/usrpoutputsettings.cpp | 5 ----- plugins/samplesink/usrpoutput/usrpoutputsettings.h | 2 -- plugins/samplesink/xtrxoutput/xtrxoutputgui.h | 4 ---- plugins/samplesink/xtrxoutput/xtrxoutputsettings.cpp | 5 ----- plugins/samplesink/xtrxoutput/xtrxoutputsettings.h | 2 -- plugins/samplesource/airspy/airspygui.h | 4 ---- plugins/samplesource/airspy/airspysettings.cpp | 5 ----- plugins/samplesource/airspy/airspysettings.h | 2 -- plugins/samplesource/airspyhf/airspyhfgui.h | 4 ---- plugins/samplesource/airspyhf/airspyhfsettings.cpp | 5 ----- plugins/samplesource/airspyhf/airspyhfsettings.h | 2 -- plugins/samplesource/audioinput/audioinputgui.h | 4 ---- plugins/samplesource/audioinput/audioinputsettings.cpp | 5 ----- plugins/samplesource/audioinput/audioinputsettings.h | 2 -- plugins/samplesource/bladerf1input/bladerf1inputgui.h | 4 ---- .../samplesource/bladerf1input/bladerf1inputsettings.cpp | 5 ----- .../samplesource/bladerf1input/bladerf1inputsettings.h | 2 -- plugins/samplesource/bladerf2input/bladerf2inputgui.h | 4 ---- .../samplesource/bladerf2input/bladerf2inputsettings.cpp | 5 ----- .../samplesource/bladerf2input/bladerf2inputsettings.h | 2 -- plugins/samplesource/fcdpro/fcdprogui.h | 4 ---- plugins/samplesource/fcdpro/fcdprosettings.cpp | 5 ----- plugins/samplesource/fcdpro/fcdprosettings.h | 2 -- plugins/samplesource/fcdproplus/fcdproplusgui.h | 4 ---- plugins/samplesource/fcdproplus/fcdproplussettings.cpp | 5 ----- plugins/samplesource/fcdproplus/fcdproplussettings.h | 2 -- plugins/samplesource/fileinput/fileinputgui.h | 4 ---- plugins/samplesource/fileinput/fileinputsettings.cpp | 5 ----- plugins/samplesource/fileinput/fileinputsettings.h | 2 -- plugins/samplesource/hackrfinput/hackrfinputgui.h | 4 ---- plugins/samplesource/hackrfinput/hackrfinputsettings.cpp | 5 ----- plugins/samplesource/hackrfinput/hackrfinputsettings.h | 2 -- plugins/samplesource/kiwisdr/kiwisdrgui.h | 4 ---- plugins/samplesource/kiwisdr/kiwisdrsettings.cpp | 6 ------ plugins/samplesource/kiwisdr/kiwisdrsettings.h | 3 --- plugins/samplesource/limesdrinput/limesdrinputgui.h | 4 ---- .../samplesource/limesdrinput/limesdrinputsettings.cpp | 5 ----- plugins/samplesource/limesdrinput/limesdrinputsettings.h | 2 -- plugins/samplesource/localinput/localinputgui.h | 4 ---- plugins/samplesource/localinput/localinputsettings.cpp | 5 ----- plugins/samplesource/localinput/localinputsettings.h | 2 -- plugins/samplesource/perseus/perseusgui.h | 4 ---- plugins/samplesource/perseus/perseussettings.cpp | 5 ----- plugins/samplesource/perseus/perseussettings.h | 2 -- plugins/samplesource/plutosdrinput/plutosdrinputgui.h | 4 ---- .../samplesource/plutosdrinput/plutosdrinputsettings.cpp | 5 ----- .../samplesource/plutosdrinput/plutosdrinputsettings.h | 2 -- plugins/samplesource/remoteinput/remoteinputgui.h | 4 ---- plugins/samplesource/remoteinput/remoteinputsettings.cpp | 5 ----- plugins/samplesource/remoteinput/remoteinputsettings.h | 2 -- plugins/samplesource/rtlsdr/rtlsdrgui.h | 4 ---- plugins/samplesource/rtlsdr/rtlsdrsettings.cpp | 5 ----- plugins/samplesource/rtlsdr/rtlsdrsettings.h | 2 -- plugins/samplesource/sdrplay/sdrplaygui.h | 4 ---- plugins/samplesource/sdrplay/sdrplaysettings.cpp | 5 ----- plugins/samplesource/sdrplay/sdrplaysettings.h | 2 -- plugins/samplesource/sdrplayv3/sdrplayv3gui.h | 4 ---- plugins/samplesource/sdrplayv3/sdrplayv3settings.cpp | 5 ----- plugins/samplesource/sdrplayv3/sdrplayv3settings.h | 2 -- plugins/samplesource/sigmffileinput/sigmffileinputgui.h | 4 ---- .../sigmffileinput/sigmffileinputsettings.cpp | 5 ----- .../samplesource/sigmffileinput/sigmffileinputsettings.h | 2 -- plugins/samplesource/soapysdrinput/soapysdrinputgui.h | 4 ---- .../samplesource/soapysdrinput/soapysdrinputsettings.cpp | 5 ----- .../samplesource/soapysdrinput/soapysdrinputsettings.h | 2 -- plugins/samplesource/testsource/testsourcegui.h | 4 ---- plugins/samplesource/testsource/testsourcesettings.cpp | 5 ----- plugins/samplesource/testsource/testsourcesettings.h | 2 -- plugins/samplesource/usrpinput/usrpinputgui.h | 4 ---- plugins/samplesource/usrpinput/usrpinputsettings.cpp | 5 ----- plugins/samplesource/usrpinput/usrpinputsettings.h | 2 -- plugins/samplesource/xtrxinput/xtrxinputgui.h | 4 ---- plugins/samplesource/xtrxinput/xtrxinputsettings.cpp | 5 ----- plugins/samplesource/xtrxinput/xtrxinputsettings.h | 2 -- sdrgui/channel/readme.md | 2 +- sdrgui/device/devicegui.h | 8 +++----- sdrgui/device/readme.md | 2 +- sdrgui/mainwindow.cpp | 5 +++-- 133 files changed, 8 insertions(+), 499 deletions(-) diff --git a/plugins/samplemimo/bladerf2mimo/bladerf2mimogui.h b/plugins/samplemimo/bladerf2mimo/bladerf2mimogui.h index 471d36a79..b8ae967f6 100644 --- a/plugins/samplemimo/bladerf2mimo/bladerf2mimogui.h +++ b/plugins/samplemimo/bladerf2mimo/bladerf2mimogui.h @@ -44,10 +44,6 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } - virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; } - virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; } - virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; } - virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; } private: Ui::BladeRF2MIMOGui* ui; diff --git a/plugins/samplemimo/bladerf2mimo/bladerf2mimosettings.cpp b/plugins/samplemimo/bladerf2mimo/bladerf2mimosettings.cpp index 0a2058d59..650dd62f1 100644 --- a/plugins/samplemimo/bladerf2mimo/bladerf2mimosettings.cpp +++ b/plugins/samplemimo/bladerf2mimo/bladerf2mimosettings.cpp @@ -58,8 +58,6 @@ void BladeRF2MIMOSettings::resetToDefaults() m_reverseAPIAddress = "127.0.0.1"; m_reverseAPIPort = 8888; m_reverseAPIDeviceIndex = 0; - - m_workspaceIndex = 0; } QByteArray BladeRF2MIMOSettings::serialize() const @@ -99,9 +97,6 @@ QByteArray BladeRF2MIMOSettings::serialize() const s.writeU32(53, m_reverseAPIPort); s.writeU32(54, m_reverseAPIDeviceIndex); - s.writeS32(55, m_workspaceIndex); - s.writeBlob(56, m_geometryBytes); - return s.final(); } @@ -163,9 +158,6 @@ bool BladeRF2MIMOSettings::deserialize(const QByteArray& data) d.readU32(54, &uintval, 0); m_reverseAPIDeviceIndex = uintval > 99 ? 99 : uintval; - d.readS32(55,&m_workspaceIndex, 0); - d.readBlob(56, &m_geometryBytes); - return true; } else diff --git a/plugins/samplemimo/bladerf2mimo/bladerf2mimosettings.h b/plugins/samplemimo/bladerf2mimo/bladerf2mimosettings.h index 98e69d66d..2f2104630 100644 --- a/plugins/samplemimo/bladerf2mimo/bladerf2mimosettings.h +++ b/plugins/samplemimo/bladerf2mimo/bladerf2mimosettings.h @@ -60,8 +60,6 @@ struct BladeRF2MIMOSettings { QString m_reverseAPIAddress; uint16_t m_reverseAPIPort; uint16_t m_reverseAPIDeviceIndex; - int m_workspaceIndex; - QByteArray m_geometryBytes; BladeRF2MIMOSettings(); void resetToDefaults(); diff --git a/plugins/samplemimo/limesdrmimo/limesdrmimogui.h b/plugins/samplemimo/limesdrmimo/limesdrmimogui.h index 666f8d3a6..2352938c7 100644 --- a/plugins/samplemimo/limesdrmimo/limesdrmimogui.h +++ b/plugins/samplemimo/limesdrmimo/limesdrmimogui.h @@ -44,10 +44,6 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } - virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; } - virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; } - virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; } - virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; } private: Ui::LimeSDRMIMOGUI* ui; diff --git a/plugins/samplemimo/limesdrmimo/limesdrmimosettings.cpp b/plugins/samplemimo/limesdrmimo/limesdrmimosettings.cpp index c25f25965..22b1a28c2 100644 --- a/plugins/samplemimo/limesdrmimo/limesdrmimosettings.cpp +++ b/plugins/samplemimo/limesdrmimo/limesdrmimosettings.cpp @@ -86,8 +86,6 @@ void LimeSDRMIMOSettings::resetToDefaults() m_lpfFIRBWTx1 = 2.5e6f; m_gainTx1 = 4; m_antennaPathTx1 = PATH_RFE_TX_NONE; - - m_workspaceIndex = 0; } QByteArray LimeSDRMIMOSettings::serialize() const @@ -155,9 +153,6 @@ QByteArray LimeSDRMIMOSettings::serialize() const s.writeU32(93, m_gainTx1); s.writeS32(94, (int) m_antennaPathTx1); - s.writeS32(95, m_workspaceIndex); - s.writeBlob(96, m_geometryBytes); - return s.final(); } @@ -253,9 +248,6 @@ bool LimeSDRMIMOSettings::deserialize(const QByteArray& data) d.readS32(94, &intval, 0); m_antennaPathTx1 = (PathTxRFE) intval; - d.readS32(95, &m_workspaceIndex, 0); - d.readBlob(96, &m_geometryBytes); - return true; } else diff --git a/plugins/samplemimo/limesdrmimo/limesdrmimosettings.h b/plugins/samplemimo/limesdrmimo/limesdrmimosettings.h index ae189e162..75049a5bd 100644 --- a/plugins/samplemimo/limesdrmimo/limesdrmimosettings.h +++ b/plugins/samplemimo/limesdrmimo/limesdrmimosettings.h @@ -119,9 +119,6 @@ struct LimeSDRMIMOSettings uint32_t m_gainTx1; //!< Tx[1] Optimally distributed gain (dB) PathTxRFE m_antennaPathTx1; //!< Tx[1] Antenna connection - int m_workspaceIndex; - QByteArray m_geometryBytes; - LimeSDRMIMOSettings(); void resetToDefaults(); QByteArray serialize() const; diff --git a/plugins/samplemimo/metismiso/metismisogui.h b/plugins/samplemimo/metismiso/metismisogui.h index b54780ef7..adaa68cd2 100644 --- a/plugins/samplemimo/metismiso/metismisogui.h +++ b/plugins/samplemimo/metismiso/metismisogui.h @@ -45,10 +45,6 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } - virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; } - virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; } - virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; } - virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; } private: Ui::MetisMISOGui* ui; diff --git a/plugins/samplemimo/metismiso/metismisosettings.cpp b/plugins/samplemimo/metismiso/metismisosettings.cpp index e636bbfd6..2e3a0e45a 100644 --- a/plugins/samplemimo/metismiso/metismisosettings.cpp +++ b/plugins/samplemimo/metismiso/metismisosettings.cpp @@ -82,7 +82,6 @@ void MetisMISOSettings::resetToDefaults() m_reverseAPIAddress = "127.0.0.1"; m_reverseAPIPort = 8888; m_reverseAPIDeviceIndex = 0; - m_workspaceIndex = 0; } QByteArray MetisMISOSettings::serialize() const @@ -113,8 +112,6 @@ QByteArray MetisMISOSettings::serialize() const s.writeU32(22, m_reverseAPIDeviceIndex); s.writeS32(23, m_streamIndex); s.writeS32(24, m_spectrumStreamIndex); - s.writeS32(25, m_workspaceIndex); - s.writeBlob(26, m_geometryBytes); for (int i = 0; i < m_maxReceivers; i++) { @@ -178,8 +175,6 @@ bool MetisMISOSettings::deserialize(const QByteArray& data) d.readS32(23, &m_streamIndex, 0); d.readS32(24, &m_spectrumStreamIndex, 0); - d.readS32(25, &m_workspaceIndex, 0); - d.readBlob(26, &m_geometryBytes); return true; } diff --git a/plugins/samplemimo/metismiso/metismisosettings.h b/plugins/samplemimo/metismiso/metismisosettings.h index 26257d225..3fd3ee396 100644 --- a/plugins/samplemimo/metismiso/metismisosettings.h +++ b/plugins/samplemimo/metismiso/metismisosettings.h @@ -48,8 +48,6 @@ struct MetisMISOSettings { QString m_reverseAPIAddress; uint16_t m_reverseAPIPort; uint16_t m_reverseAPIDeviceIndex; - int m_workspaceIndex; - QByteArray m_geometryBytes; MetisMISOSettings(); MetisMISOSettings(const MetisMISOSettings& other); diff --git a/plugins/samplemimo/plutosdrmimo/plutosdrmimogui.h b/plugins/samplemimo/plutosdrmimo/plutosdrmimogui.h index cbd816025..3984ee6cc 100644 --- a/plugins/samplemimo/plutosdrmimo/plutosdrmimogui.h +++ b/plugins/samplemimo/plutosdrmimo/plutosdrmimogui.h @@ -44,10 +44,6 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } - virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; } - virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; } - virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; } - virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; } private: Ui::PlutoSDRMIMOGUI* ui; diff --git a/plugins/samplemimo/plutosdrmimo/plutosdrmimosettings.cpp b/plugins/samplemimo/plutosdrmimo/plutosdrmimosettings.cpp index 887c6d6d7..ceb4441d3 100644 --- a/plugins/samplemimo/plutosdrmimo/plutosdrmimosettings.cpp +++ b/plugins/samplemimo/plutosdrmimo/plutosdrmimosettings.cpp @@ -77,7 +77,6 @@ void PlutoSDRMIMOSettings::resetToDefaults() m_reverseAPIAddress = "127.0.0.1"; m_reverseAPIPort = 8888; m_reverseAPIDeviceIndex = 0; - m_workspaceIndex = 0; } QByteArray PlutoSDRMIMOSettings::serialize() const @@ -142,9 +141,6 @@ QByteArray PlutoSDRMIMOSettings::serialize() const s.writeU32(102, m_reverseAPIPort); s.writeU32(103, m_reverseAPIDeviceIndex); - s.writeS32(104, m_workspaceIndex); - s.writeBlob(105, m_geometryBytes); - return s.final(); } @@ -279,9 +275,6 @@ bool PlutoSDRMIMOSettings::deserialize(const QByteArray& data) d.readU32(103, &uintval, 0); m_reverseAPIDeviceIndex = uintval > 99 ? 99 : uintval; - d.readS32(104, &m_workspaceIndex, 0); - d.readBlob(105, &m_geometryBytes); - return true; } else diff --git a/plugins/samplemimo/plutosdrmimo/plutosdrmimosettings.h b/plugins/samplemimo/plutosdrmimo/plutosdrmimosettings.h index 3fda801be..d99efd8f2 100644 --- a/plugins/samplemimo/plutosdrmimo/plutosdrmimosettings.h +++ b/plugins/samplemimo/plutosdrmimo/plutosdrmimosettings.h @@ -121,8 +121,6 @@ struct PlutoSDRMIMOSettings { QString m_reverseAPIAddress; uint16_t m_reverseAPIPort; uint16_t m_reverseAPIDeviceIndex; - int m_workspaceIndex; - QByteArray m_geometryBytes; static const int m_plutoSDRBlockSizeSamples = 64*256; //complex samples per buffer (must be multiple of 64) diff --git a/plugins/samplemimo/testmi/testmigui.h b/plugins/samplemimo/testmi/testmigui.h index ec27cbaae..410fa1099 100644 --- a/plugins/samplemimo/testmi/testmigui.h +++ b/plugins/samplemimo/testmi/testmigui.h @@ -45,10 +45,6 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } - virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; } - virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; } - virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; } - virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; } private: Ui::TestMIGui* ui; diff --git a/plugins/samplemimo/testmi/testmisettings.cpp b/plugins/samplemimo/testmi/testmisettings.cpp index 66d43f192..7461e52b4 100644 --- a/plugins/samplemimo/testmi/testmisettings.cpp +++ b/plugins/samplemimo/testmi/testmisettings.cpp @@ -50,7 +50,6 @@ TestMISettings::TestMISettings() m_reverseAPIAddress = "127.0.0.1"; m_reverseAPIPort = 8888; m_reverseAPIDeviceIndex = 0; - m_workspaceIndex = 0; m_streams.push_back(TestMIStreamSettings()); m_streams.push_back(TestMIStreamSettings()); } @@ -62,7 +61,6 @@ TestMISettings::TestMISettings(const TestMISettings& other) : m_reverseAPIAddress = other.m_reverseAPIAddress; m_reverseAPIPort = other.m_reverseAPIPort; m_reverseAPIDeviceIndex = other.m_reverseAPIDeviceIndex; - m_workspaceIndex = other.m_workspaceIndex; } void TestMISettings::resetToDefaults() @@ -80,8 +78,6 @@ QByteArray TestMISettings::serialize() const s.writeString(2, m_reverseAPIAddress); s.writeU32(3, m_reverseAPIPort); s.writeU32(4, m_reverseAPIDeviceIndex); - s.writeS32(5, m_workspaceIndex); - s.writeBlob(6, m_geometryBytes); for (unsigned int i = 0; i < m_streams.size(); i++) { @@ -132,8 +128,6 @@ bool TestMISettings::deserialize(const QByteArray& data) d.readU32(4, &utmp, 0); m_reverseAPIDeviceIndex = utmp > 99 ? 99 : utmp; - d.readS32(5, &m_workspaceIndex, 0); - d.readBlob(6, &m_geometryBytes); for (unsigned int i = 0; i < m_streams.size(); i++) { diff --git a/plugins/samplemimo/testmi/testmisettings.h b/plugins/samplemimo/testmi/testmisettings.h index 063b9ba19..63c11889b 100644 --- a/plugins/samplemimo/testmi/testmisettings.h +++ b/plugins/samplemimo/testmi/testmisettings.h @@ -71,8 +71,6 @@ struct TestMISettings QString m_reverseAPIAddress; uint16_t m_reverseAPIPort; uint16_t m_reverseAPIDeviceIndex; - int m_workspaceIndex; - QByteArray m_geometryBytes; std::vector m_streams; TestMISettings(); diff --git a/plugins/samplemimo/testmosync/testmosyncgui.h b/plugins/samplemimo/testmosync/testmosyncgui.h index be24d06e1..7c712e1af 100644 --- a/plugins/samplemimo/testmosync/testmosyncgui.h +++ b/plugins/samplemimo/testmosync/testmosyncgui.h @@ -47,10 +47,6 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } - virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; } - virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; } - virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; } - virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; } private: Ui::TestMOSyncGui* ui; diff --git a/plugins/samplemimo/testmosync/testmosyncsettings.cpp b/plugins/samplemimo/testmosync/testmosyncsettings.cpp index 5b53cdcf7..020551f31 100644 --- a/plugins/samplemimo/testmosync/testmosyncsettings.cpp +++ b/plugins/samplemimo/testmosync/testmosyncsettings.cpp @@ -31,7 +31,6 @@ void TestMOSyncSettings::resetToDefaults() m_sampleRate = 48000; m_log2Interp = 0; m_fcPosTx = FC_POS_CENTER; - m_workspaceIndex = 0; m_useReverseAPI = false; m_reverseAPIAddress = "127.0.0.1"; m_reverseAPIPort = 8888; @@ -45,8 +44,6 @@ QByteArray TestMOSyncSettings::serialize() const s.writeU64(1, m_sampleRate); s.writeU32(2, m_log2Interp); s.writeS32(3, (int) m_fcPosTx); - s.writeS32(4, m_workspaceIndex); - s.writeBlob(5, m_geometryBytes); s.writeBool(6, m_useReverseAPI); s.writeString(7, m_reverseAPIAddress); s.writeU32(8, m_reverseAPIPort); @@ -74,8 +71,6 @@ bool TestMOSyncSettings::deserialize(const QByteArray& data) d.readU32(2, &m_log2Interp, 0); d.readS32(3, &intval, 2); m_fcPosTx = (fcPos_t) intval; - d.readS32(4, &m_workspaceIndex, 0); - d.readBlob(5, &m_geometryBytes); d.readBool(1, &m_useReverseAPI, false); d.readString(2, &m_reverseAPIAddress, "127.0.0.1"); d.readU32(3, &utmp, 0); diff --git a/plugins/samplemimo/testmosync/testmosyncsettings.h b/plugins/samplemimo/testmosync/testmosyncsettings.h index 8743979a7..31cbe3774 100644 --- a/plugins/samplemimo/testmosync/testmosyncsettings.h +++ b/plugins/samplemimo/testmosync/testmosyncsettings.h @@ -18,8 +18,6 @@ #ifndef PLUGINS_SAMPLEMIMO_TESTMOSYNC_TESTMOSYNCSETTINGS_H_ #define PLUGINS_SAMPLEMIMO_TESTMOSYNC_TESTMOSYNCSETTINGS_H_ -#include - struct TestMOSyncSettings { typedef enum { FC_POS_INFRA = 0, @@ -35,8 +33,6 @@ struct TestMOSyncSettings { QString m_reverseAPIAddress; uint16_t m_reverseAPIPort; uint16_t m_reverseAPIDeviceIndex; - int m_workspaceIndex; - QByteArray m_geometryBytes; static const unsigned int m_msThrottle; diff --git a/plugins/samplemimo/xtrxmimo/xtrxmimogui.h b/plugins/samplemimo/xtrxmimo/xtrxmimogui.h index 2f40493d7..a41eee57b 100644 --- a/plugins/samplemimo/xtrxmimo/xtrxmimogui.h +++ b/plugins/samplemimo/xtrxmimo/xtrxmimogui.h @@ -44,10 +44,6 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } - virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; } - virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; } - virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; } - virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; } private: Ui::XTRXMIMOGUI* ui; diff --git a/plugins/samplemimo/xtrxmimo/xtrxmimosettings.cpp b/plugins/samplemimo/xtrxmimo/xtrxmimosettings.cpp index 2d6cb75f1..fcde1096d 100644 --- a/plugins/samplemimo/xtrxmimo/xtrxmimosettings.cpp +++ b/plugins/samplemimo/xtrxmimo/xtrxmimosettings.cpp @@ -75,8 +75,6 @@ void XTRXMIMOSettings::resetToDefaults() m_lpfBWTx1 = 4.5e6f; m_gainTx1 = 20; m_pwrmodeTx1 = 4; - // GUI - m_workspaceIndex = 0; } QByteArray XTRXMIMOSettings::serialize() const @@ -131,9 +129,6 @@ QByteArray XTRXMIMOSettings::serialize() const s.writeFloat(90, m_lpfBWTx1); s.writeU32(91, m_gainTx1); s.writeU32(92, m_pwrmodeTx1); - // GUI - s.writeS32(93, m_workspaceIndex); - s.writeBlob(94, m_geometryBytes); return s.final(); } diff --git a/plugins/samplemimo/xtrxmimo/xtrxmimosettings.h b/plugins/samplemimo/xtrxmimo/xtrxmimosettings.h index f86fdb5ce..8dc3a7a19 100644 --- a/plugins/samplemimo/xtrxmimo/xtrxmimosettings.h +++ b/plugins/samplemimo/xtrxmimo/xtrxmimosettings.h @@ -93,9 +93,6 @@ struct XTRXMIMOSettings float m_lpfBWTx1; //!< LMS analog lowpass filter bandwidth (Hz) uint32_t m_gainTx1; //!< Optimally distributed gain (dB) uint32_t m_pwrmodeTx1; - // GUI - int m_workspaceIndex; - QByteArray m_geometryBytes; XTRXMIMOSettings(); void resetToDefaults(); diff --git a/plugins/samplesink/audiooutput/audiooutputgui.h b/plugins/samplesink/audiooutput/audiooutputgui.h index d53748dfc..4a5b2d406 100644 --- a/plugins/samplesink/audiooutput/audiooutputgui.h +++ b/plugins/samplesink/audiooutput/audiooutputgui.h @@ -45,10 +45,6 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } - virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; } - virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; } - virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; } - virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; } private: Ui::AudioOutputGui* ui; diff --git a/plugins/samplesink/audiooutput/audiooutputsettings.cpp b/plugins/samplesink/audiooutput/audiooutputsettings.cpp index b11772506..e6d39f949 100644 --- a/plugins/samplesink/audiooutput/audiooutputsettings.cpp +++ b/plugins/samplesink/audiooutput/audiooutputsettings.cpp @@ -34,7 +34,6 @@ void AudioOutputSettings::resetToDefaults() m_reverseAPIAddress = "127.0.0.1"; m_reverseAPIPort = 8888; m_reverseAPIDeviceIndex = 0; - m_workspaceIndex = 0; } QByteArray AudioOutputSettings::serialize() const @@ -44,8 +43,6 @@ QByteArray AudioOutputSettings::serialize() const s.writeString(1, m_deviceName); s.writeFloat(3, m_volume); s.writeS32(5, (int)m_iqMapping); - s.writeS32(6, m_workspaceIndex); - s.writeBlob(7, m_geometryBytes); s.writeBool(24, m_useReverseAPI); s.writeString(25, m_reverseAPIAddress); @@ -72,8 +69,6 @@ bool AudioOutputSettings::deserialize(const QByteArray& data) d.readString(1, &m_deviceName, ""); d.readFloat(3, &m_volume, 1.0f); d.readS32(5, (int *)&m_iqMapping, IQMapping::LR); - d.readS32(6, &m_workspaceIndex, 0); - d.readBlob(7, &m_geometryBytes); d.readBool(24, &m_useReverseAPI, false); d.readString(25, &m_reverseAPIAddress, "127.0.0.1"); diff --git a/plugins/samplesink/audiooutput/audiooutputsettings.h b/plugins/samplesink/audiooutput/audiooutputsettings.h index 66335e21d..d2f9e7634 100644 --- a/plugins/samplesink/audiooutput/audiooutputsettings.h +++ b/plugins/samplesink/audiooutput/audiooutputsettings.h @@ -34,8 +34,6 @@ struct AudioOutputSettings { QString m_reverseAPIAddress; uint16_t m_reverseAPIPort; uint16_t m_reverseAPIDeviceIndex; - int m_workspaceIndex; - QByteArray m_geometryBytes; AudioOutputSettings(); void resetToDefaults(); diff --git a/plugins/samplesink/bladerf1output/bladerf1outputgui.h b/plugins/samplesink/bladerf1output/bladerf1outputgui.h index af5cc768e..31ea820f6 100644 --- a/plugins/samplesink/bladerf1output/bladerf1outputgui.h +++ b/plugins/samplesink/bladerf1output/bladerf1outputgui.h @@ -45,10 +45,6 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } - virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; } - virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; } - virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; } - virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; } private: Ui::Bladerf1OutputGui* ui; diff --git a/plugins/samplesink/bladerf1output/bladerf1outputsettings.cpp b/plugins/samplesink/bladerf1output/bladerf1outputsettings.cpp index 4e29762e4..42e33f262 100644 --- a/plugins/samplesink/bladerf1output/bladerf1outputsettings.cpp +++ b/plugins/samplesink/bladerf1output/bladerf1outputsettings.cpp @@ -41,7 +41,6 @@ void BladeRF1OutputSettings::resetToDefaults() m_reverseAPIAddress = "127.0.0.1"; m_reverseAPIPort = 8888; m_reverseAPIDeviceIndex = 0; - m_workspaceIndex = 0; } QByteArray BladeRF1OutputSettings::serialize() const @@ -60,8 +59,6 @@ QByteArray BladeRF1OutputSettings::serialize() const s.writeString(10, m_reverseAPIAddress); s.writeU32(11, m_reverseAPIPort); s.writeU32(12, m_reverseAPIDeviceIndex); - s.writeS32(13, m_workspaceIndex); - s.writeBlob(14, m_geometryBytes); return s.final(); } @@ -103,8 +100,6 @@ bool BladeRF1OutputSettings::deserialize(const QByteArray& data) d.readU32(12, &uintval, 0); m_reverseAPIDeviceIndex = uintval > 99 ? 99 : uintval; - d.readS32(13, &m_workspaceIndex, 0); - d.readBlob(14, &m_geometryBytes); return true; } diff --git a/plugins/samplesink/bladerf1output/bladerf1outputsettings.h b/plugins/samplesink/bladerf1output/bladerf1outputsettings.h index 44c0b5d0b..71d15376c 100644 --- a/plugins/samplesink/bladerf1output/bladerf1outputsettings.h +++ b/plugins/samplesink/bladerf1output/bladerf1outputsettings.h @@ -37,8 +37,6 @@ struct BladeRF1OutputSettings { QString m_reverseAPIAddress; uint16_t m_reverseAPIPort; uint16_t m_reverseAPIDeviceIndex; - int m_workspaceIndex; - QByteArray m_geometryBytes; BladeRF1OutputSettings(); void resetToDefaults(); diff --git a/plugins/samplesink/bladerf2output/bladerf2outputgui.h b/plugins/samplesink/bladerf2output/bladerf2outputgui.h index c4930bbce..bd447541e 100644 --- a/plugins/samplesink/bladerf2output/bladerf2outputgui.h +++ b/plugins/samplesink/bladerf2output/bladerf2outputgui.h @@ -46,10 +46,6 @@ public: virtual bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } virtual bool handleMessage(const Message& message); - virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; } - virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; } - virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; } - virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; } private: Ui::BladeRF2OutputGui* ui; diff --git a/plugins/samplesink/bladerf2output/bladerf2outputsettings.cpp b/plugins/samplesink/bladerf2output/bladerf2outputsettings.cpp index 1aa48b762..a50ee005f 100644 --- a/plugins/samplesink/bladerf2output/bladerf2outputsettings.cpp +++ b/plugins/samplesink/bladerf2output/bladerf2outputsettings.cpp @@ -41,7 +41,6 @@ void BladeRF2OutputSettings::resetToDefaults() m_reverseAPIAddress = "127.0.0.1"; m_reverseAPIPort = 8888; m_reverseAPIDeviceIndex = 0; - m_workspaceIndex = 0; } QByteArray BladeRF2OutputSettings::serialize() const @@ -60,8 +59,6 @@ QByteArray BladeRF2OutputSettings::serialize() const s.writeString(10, m_reverseAPIAddress); s.writeU32(11, m_reverseAPIPort); s.writeU32(12, m_reverseAPIDeviceIndex); - s.writeS32(13, m_workspaceIndex); - s.writeBlob(14, m_geometryBytes); return s.final(); } @@ -100,8 +97,6 @@ bool BladeRF2OutputSettings::deserialize(const QByteArray& data) d.readU32(12, &uintval, 0); m_reverseAPIDeviceIndex = uintval > 99 ? 99 : uintval; - d.readS32(13, &m_workspaceIndex, 0); - d.readBlob(14, &m_geometryBytes); return true; } diff --git a/plugins/samplesink/bladerf2output/bladerf2outputsettings.h b/plugins/samplesink/bladerf2output/bladerf2outputsettings.h index 21e5e762a..69a4dd3b3 100644 --- a/plugins/samplesink/bladerf2output/bladerf2outputsettings.h +++ b/plugins/samplesink/bladerf2output/bladerf2outputsettings.h @@ -35,8 +35,6 @@ struct BladeRF2OutputSettings { QString m_reverseAPIAddress; uint16_t m_reverseAPIPort; uint16_t m_reverseAPIDeviceIndex; - int m_workspaceIndex; - QByteArray m_geometryBytes; BladeRF2OutputSettings(); void resetToDefaults(); diff --git a/plugins/samplesink/fileoutput/fileoutputgui.h b/plugins/samplesink/fileoutput/fileoutputgui.h index a95c25b92..7d8906ebe 100644 --- a/plugins/samplesink/fileoutput/fileoutputgui.h +++ b/plugins/samplesink/fileoutput/fileoutputgui.h @@ -47,10 +47,6 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } - virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; } - virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; } - virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; } - virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; } private: Ui::FileOutputGui* ui; diff --git a/plugins/samplesink/fileoutput/fileoutputsettings.cpp b/plugins/samplesink/fileoutput/fileoutputsettings.cpp index e07abde6a..a4affa826 100644 --- a/plugins/samplesink/fileoutput/fileoutputsettings.cpp +++ b/plugins/samplesink/fileoutput/fileoutputsettings.cpp @@ -33,7 +33,6 @@ void FileOutputSettings::resetToDefaults() m_reverseAPIAddress = "127.0.0.1"; m_reverseAPIPort = 8888; m_reverseAPIDeviceIndex = 0; - m_workspaceIndex = 0; } QByteArray FileOutputSettings::serialize() const @@ -47,8 +46,6 @@ QByteArray FileOutputSettings::serialize() const s.writeString(5, m_reverseAPIAddress); s.writeU32(6, m_reverseAPIPort); s.writeU32(7, m_reverseAPIDeviceIndex); - s.writeS32(8, m_workspaceIndex); - s.writeBlob(9, m_geometryBytes); return s.final(); } @@ -82,8 +79,6 @@ bool FileOutputSettings::deserialize(const QByteArray& data) d.readU32(7, &uintval, 0); m_reverseAPIDeviceIndex = uintval > 99 ? 99 : uintval; - d.readS32(8, &m_workspaceIndex, 0); - d.readBlob(9, &m_geometryBytes); return true; } diff --git a/plugins/samplesink/fileoutput/fileoutputsettings.h b/plugins/samplesink/fileoutput/fileoutputsettings.h index a75d9af88..91b0df057 100644 --- a/plugins/samplesink/fileoutput/fileoutputsettings.h +++ b/plugins/samplesink/fileoutput/fileoutputsettings.h @@ -18,8 +18,6 @@ #ifndef PLUGINS_SAMPLESINK_FILEOUTPUT_FILEOUTPUTSETTINGS_H_ #define PLUGINS_SAMPLESINK_FILEOUTPUT_FILEOUTPUTSETTINGS_H_ -#include - struct FileOutputSettings { quint64 m_centerFrequency; quint64 m_sampleRate; @@ -29,8 +27,6 @@ struct FileOutputSettings { QString m_reverseAPIAddress; uint16_t m_reverseAPIPort; uint16_t m_reverseAPIDeviceIndex; - int m_workspaceIndex; - QByteArray m_geometryBytes; FileOutputSettings(); void resetToDefaults(); diff --git a/plugins/samplesink/hackrfoutput/hackrfoutputgui.h b/plugins/samplesink/hackrfoutput/hackrfoutputgui.h index 44ac8ab02..78d834806 100644 --- a/plugins/samplesink/hackrfoutput/hackrfoutputgui.h +++ b/plugins/samplesink/hackrfoutput/hackrfoutputgui.h @@ -55,10 +55,6 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } - virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; } - virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; } - virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; } - virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; } private: Ui::HackRFOutputGui* ui; diff --git a/plugins/samplesink/hackrfoutput/hackrfoutputsettings.cpp b/plugins/samplesink/hackrfoutput/hackrfoutputsettings.cpp index 498b18951..e876d4c66 100644 --- a/plugins/samplesink/hackrfoutput/hackrfoutputsettings.cpp +++ b/plugins/samplesink/hackrfoutput/hackrfoutputsettings.cpp @@ -43,7 +43,6 @@ void HackRFOutputSettings::resetToDefaults() m_reverseAPIAddress = "127.0.0.1"; m_reverseAPIPort = 8888; m_reverseAPIDeviceIndex = 0; - m_workspaceIndex = 0; } QByteArray HackRFOutputSettings::serialize() const @@ -64,8 +63,6 @@ QByteArray HackRFOutputSettings::serialize() const s.writeU32(12, m_reverseAPIDeviceIndex); s.writeBool(13, m_transverterMode); s.writeS64(14, m_transverterDeltaFrequency); - s.writeS32(15, m_workspaceIndex); - s.writeBlob(16, m_geometryBytes); return s.final(); } @@ -108,8 +105,6 @@ bool HackRFOutputSettings::deserialize(const QByteArray& data) m_reverseAPIDeviceIndex = uintval > 99 ? 99 : uintval; d.readBool(13, &m_transverterMode, false); d.readS64(14, &m_transverterDeltaFrequency, 0); - d.readS32(15, &m_workspaceIndex, 0); - d.readBlob(16, &m_geometryBytes); return true; } diff --git a/plugins/samplesink/hackrfoutput/hackrfoutputsettings.h b/plugins/samplesink/hackrfoutput/hackrfoutputsettings.h index e8b4ee158..25ffe4c54 100644 --- a/plugins/samplesink/hackrfoutput/hackrfoutputsettings.h +++ b/plugins/samplesink/hackrfoutput/hackrfoutputsettings.h @@ -43,8 +43,6 @@ struct HackRFOutputSettings { QString m_reverseAPIAddress; uint16_t m_reverseAPIPort; uint16_t m_reverseAPIDeviceIndex; - int m_workspaceIndex; - QByteArray m_geometryBytes; HackRFOutputSettings(); void resetToDefaults(); diff --git a/plugins/samplesink/limesdroutput/limesdroutputgui.h b/plugins/samplesink/limesdroutput/limesdroutputgui.h index 8ba79e4e3..125414032 100644 --- a/plugins/samplesink/limesdroutput/limesdroutputgui.h +++ b/plugins/samplesink/limesdroutput/limesdroutputgui.h @@ -45,10 +45,6 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } - virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; } - virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; } - virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; } - virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; } private: Ui::LimeSDROutputGUI* ui; diff --git a/plugins/samplesink/limesdroutput/limesdroutputsettings.cpp b/plugins/samplesink/limesdroutput/limesdroutputsettings.cpp index 6164e616b..bd54bb58b 100644 --- a/plugins/samplesink/limesdroutput/limesdroutputsettings.cpp +++ b/plugins/samplesink/limesdroutput/limesdroutputsettings.cpp @@ -47,7 +47,6 @@ void LimeSDROutputSettings::resetToDefaults() m_reverseAPIAddress = "127.0.0.1"; m_reverseAPIPort = 8888; m_reverseAPIDeviceIndex = 0; - m_workspaceIndex = 0; } QByteArray LimeSDROutputSettings::serialize() const @@ -74,8 +73,6 @@ QByteArray LimeSDROutputSettings::serialize() const s.writeString(21, m_reverseAPIAddress); s.writeU32(22, m_reverseAPIPort); s.writeU32(23, m_reverseAPIDeviceIndex); - s.writeS32(24, m_workspaceIndex); - s.writeBlob(25, m_geometryBytes); return s.final(); } @@ -126,8 +123,6 @@ bool LimeSDROutputSettings::deserialize(const QByteArray& data) d.readU32(23, &uintval, 0); m_reverseAPIDeviceIndex = uintval > 99 ? 99 : uintval; - d.readS32(24, &m_workspaceIndex, 0); - d.readBlob(25, &m_geometryBytes); return true; } diff --git a/plugins/samplesink/limesdroutput/limesdroutputsettings.h b/plugins/samplesink/limesdroutput/limesdroutputsettings.h index 148493670..6bed846c4 100644 --- a/plugins/samplesink/limesdroutput/limesdroutputsettings.h +++ b/plugins/samplesink/limesdroutput/limesdroutputsettings.h @@ -65,8 +65,6 @@ struct LimeSDROutputSettings QString m_reverseAPIAddress; uint16_t m_reverseAPIPort; uint16_t m_reverseAPIDeviceIndex; - int m_workspaceIndex; - QByteArray m_geometryBytes; LimeSDROutputSettings(); void resetToDefaults(); diff --git a/plugins/samplesink/localoutput/localoutputgui.h b/plugins/samplesink/localoutput/localoutputgui.h index 50a9ecead..da888c787 100644 --- a/plugins/samplesink/localoutput/localoutputgui.h +++ b/plugins/samplesink/localoutput/localoutputgui.h @@ -46,10 +46,6 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } - virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; } - virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; } - virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; } - virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; } private: Ui::LocalOutputGui* ui; diff --git a/plugins/samplesink/localoutput/localoutputsettings.cpp b/plugins/samplesink/localoutput/localoutputsettings.cpp index 8165dd454..1b4bfe3cf 100644 --- a/plugins/samplesink/localoutput/localoutputsettings.cpp +++ b/plugins/samplesink/localoutput/localoutputsettings.cpp @@ -29,7 +29,6 @@ void LocalOutputSettings::resetToDefaults() m_reverseAPIAddress = "127.0.0.1"; m_reverseAPIPort = 8888; m_reverseAPIDeviceIndex = 0; - m_workspaceIndex = 0; } QByteArray LocalOutputSettings::serialize() const @@ -39,8 +38,6 @@ QByteArray LocalOutputSettings::serialize() const s.writeString(4, m_reverseAPIAddress); s.writeU32(5, m_reverseAPIPort); s.writeU32(6, m_reverseAPIDeviceIndex); - s.writeS32(7, m_workspaceIndex); - s.writeBlob(8, m_geometryBytes); return s.final(); } @@ -70,8 +67,6 @@ bool LocalOutputSettings::deserialize(const QByteArray& data) d.readU32(6, &uintval, 0); m_reverseAPIDeviceIndex = uintval > 99 ? 99 : uintval; - d.readS32(7, &m_workspaceIndex, 0); - d.readBlob(8, &m_geometryBytes); return true; } diff --git a/plugins/samplesink/localoutput/localoutputsettings.h b/plugins/samplesink/localoutput/localoutputsettings.h index 5b42fe082..6c7569f7f 100644 --- a/plugins/samplesink/localoutput/localoutputsettings.h +++ b/plugins/samplesink/localoutput/localoutputsettings.h @@ -26,8 +26,6 @@ struct LocalOutputSettings { QString m_reverseAPIAddress; uint16_t m_reverseAPIPort; uint16_t m_reverseAPIDeviceIndex; - int m_workspaceIndex; - QByteArray m_geometryBytes; LocalOutputSettings(); void resetToDefaults(); diff --git a/plugins/samplesink/plutosdroutput/plutosdroutputgui.h b/plugins/samplesink/plutosdroutput/plutosdroutputgui.h index 9851da659..46bbd040d 100644 --- a/plugins/samplesink/plutosdroutput/plutosdroutputgui.h +++ b/plugins/samplesink/plutosdroutput/plutosdroutputgui.h @@ -47,10 +47,6 @@ public: virtual QByteArray serialize() const; virtual bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } - virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; } - virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; } - virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; } - virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; } private: Ui::PlutoSDROutputGUI* ui; diff --git a/plugins/samplesink/plutosdroutput/plutosdroutputsettings.cpp b/plugins/samplesink/plutosdroutput/plutosdroutputsettings.cpp index 6885defcc..d42615dac 100644 --- a/plugins/samplesink/plutosdroutput/plutosdroutputsettings.cpp +++ b/plugins/samplesink/plutosdroutput/plutosdroutputsettings.cpp @@ -44,7 +44,6 @@ void PlutoSDROutputSettings::resetToDefaults() m_reverseAPIAddress = "127.0.0.1"; m_reverseAPIPort = 8888; m_reverseAPIDeviceIndex = 0; - m_workspaceIndex = 0; } QByteArray PlutoSDROutputSettings::serialize() const @@ -67,8 +66,6 @@ QByteArray PlutoSDROutputSettings::serialize() const s.writeString(18, m_reverseAPIAddress); s.writeU32(19, m_reverseAPIPort); s.writeU32(20, m_reverseAPIDeviceIndex); - s.writeS32(21, m_workspaceIndex); - s.writeBlob(22, m_geometryBytes); return s.final(); } @@ -122,8 +119,6 @@ bool PlutoSDROutputSettings::deserialize(const QByteArray& data) d.readU32(20, &uintval, 0); m_reverseAPIDeviceIndex = uintval > 99 ? 99 : uintval; - d.readS32(21, &m_workspaceIndex, 0); - d.readBlob(22, &m_geometryBytes); return true; } diff --git a/plugins/samplesink/plutosdroutput/plutosdroutputsettings.h b/plugins/samplesink/plutosdroutput/plutosdroutputsettings.h index db4f4d5ad..02f917885 100644 --- a/plugins/samplesink/plutosdroutput/plutosdroutputsettings.h +++ b/plugins/samplesink/plutosdroutput/plutosdroutputsettings.h @@ -51,8 +51,6 @@ struct PlutoSDROutputSettings { QString m_reverseAPIAddress; uint16_t m_reverseAPIPort; uint16_t m_reverseAPIDeviceIndex; - int m_workspaceIndex; - QByteArray m_geometryBytes; PlutoSDROutputSettings(); void resetToDefaults(); diff --git a/plugins/samplesink/remoteoutput/remoteoutputgui.h b/plugins/samplesink/remoteoutput/remoteoutputgui.h index f2f440d74..3e7df619f 100644 --- a/plugins/samplesink/remoteoutput/remoteoutputgui.h +++ b/plugins/samplesink/remoteoutput/remoteoutputgui.h @@ -77,10 +77,6 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } - virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; } - virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; } - virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; } - virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; } private: Ui::RemoteOutputGui* ui; diff --git a/plugins/samplesink/remoteoutput/remoteoutputsettings.cpp b/plugins/samplesink/remoteoutput/remoteoutputsettings.cpp index 65a222484..cbff6e6f0 100644 --- a/plugins/samplesink/remoteoutput/remoteoutputsettings.cpp +++ b/plugins/samplesink/remoteoutput/remoteoutputsettings.cpp @@ -37,7 +37,6 @@ void RemoteOutputSettings::resetToDefaults() m_reverseAPIAddress = "127.0.0.1"; m_reverseAPIPort = 8888; m_reverseAPIDeviceIndex = 0; - m_workspaceIndex = 0; } QByteArray RemoteOutputSettings::serialize() const @@ -56,8 +55,6 @@ QByteArray RemoteOutputSettings::serialize() const s.writeString(13, m_reverseAPIAddress); s.writeU32(14, m_reverseAPIPort); s.writeU32(15, m_reverseAPIDeviceIndex); - s.writeS32(16, m_workspaceIndex); - s.writeBlob(17, m_geometryBytes); return s.final(); } @@ -98,8 +95,6 @@ bool RemoteOutputSettings::deserialize(const QByteArray& data) d.readU32(15, &uintval, 0); m_reverseAPIDeviceIndex = uintval > 99 ? 99 : uintval; - d.readS32(16, &m_workspaceIndex, 0); - d.readBlob(17, &m_geometryBytes); return true; } diff --git a/plugins/samplesink/remoteoutput/remoteoutputsettings.h b/plugins/samplesink/remoteoutput/remoteoutputsettings.h index 814577e3a..830e056dd 100644 --- a/plugins/samplesink/remoteoutput/remoteoutputsettings.h +++ b/plugins/samplesink/remoteoutput/remoteoutputsettings.h @@ -34,8 +34,6 @@ struct RemoteOutputSettings { QString m_reverseAPIAddress; uint16_t m_reverseAPIPort; uint16_t m_reverseAPIDeviceIndex; - int m_workspaceIndex; - QByteArray m_geometryBytes; RemoteOutputSettings(); void resetToDefaults(); diff --git a/plugins/samplesink/soapysdroutput/soapysdroutputgui.h b/plugins/samplesink/soapysdroutput/soapysdroutputgui.h index 8f3b53e7f..a17339dfe 100644 --- a/plugins/samplesink/soapysdroutput/soapysdroutputgui.h +++ b/plugins/samplesink/soapysdroutput/soapysdroutputgui.h @@ -53,10 +53,6 @@ public: virtual QByteArray serialize() const; virtual bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } - virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; } - virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; } - virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; } - virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; } private: void createRangesControl( diff --git a/plugins/samplesink/soapysdroutput/soapysdroutputsettings.cpp b/plugins/samplesink/soapysdroutput/soapysdroutputsettings.cpp index 2cc77d0fe..7d7a431d0 100644 --- a/plugins/samplesink/soapysdroutput/soapysdroutputsettings.cpp +++ b/plugins/samplesink/soapysdroutput/soapysdroutputsettings.cpp @@ -47,7 +47,6 @@ void SoapySDROutputSettings::resetToDefaults() m_reverseAPIAddress = "127.0.0.1"; m_reverseAPIPort = 8888; m_reverseAPIDeviceIndex = 0; - m_workspaceIndex = 0; } QByteArray SoapySDROutputSettings::serialize() const @@ -77,8 +76,6 @@ QByteArray SoapySDROutputSettings::serialize() const s.writeString(24, m_reverseAPIAddress); s.writeU32(25, m_reverseAPIPort); s.writeU32(26, m_reverseAPIDeviceIndex); - s.writeS32(27, m_workspaceIndex); - s.writeBlob(28, m_geometryBytes); return s.final(); } @@ -136,8 +133,6 @@ bool SoapySDROutputSettings::deserialize(const QByteArray& data) d.readU32(26, &uintval, 0); m_reverseAPIDeviceIndex = uintval > 99 ? 99 : uintval; - d.readS32(27, &m_workspaceIndex, 0); - d.readBlob(28, &m_geometryBytes); return true; } diff --git a/plugins/samplesink/soapysdroutput/soapysdroutputsettings.h b/plugins/samplesink/soapysdroutput/soapysdroutputsettings.h index 7216385aa..9589a65f1 100644 --- a/plugins/samplesink/soapysdroutput/soapysdroutputsettings.h +++ b/plugins/samplesink/soapysdroutput/soapysdroutputsettings.h @@ -45,8 +45,6 @@ struct SoapySDROutputSettings { QString m_reverseAPIAddress; uint16_t m_reverseAPIPort; uint16_t m_reverseAPIDeviceIndex; - int m_workspaceIndex; - QByteArray m_geometryBytes; SoapySDROutputSettings(); void resetToDefaults(); diff --git a/plugins/samplesink/testsink/testsinkgui.h b/plugins/samplesink/testsink/testsinkgui.h index dc7566492..6e861d24b 100644 --- a/plugins/samplesink/testsink/testsinkgui.h +++ b/plugins/samplesink/testsink/testsinkgui.h @@ -48,10 +48,6 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } - virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; } - virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; } - virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; } - virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; } private: Ui::TestSinkGui* ui; diff --git a/plugins/samplesink/testsink/testsinksettings.cpp b/plugins/samplesink/testsink/testsinksettings.cpp index ec32a5dbf..3b8fe09b5 100644 --- a/plugins/samplesink/testsink/testsinksettings.cpp +++ b/plugins/samplesink/testsink/testsinksettings.cpp @@ -30,7 +30,6 @@ void TestSinkSettings::resetToDefaults() m_sampleRate = 48000; m_log2Interp = 0; m_spectrumGUI = nullptr; - m_workspaceIndex = 0; m_useReverseAPI = false; m_reverseAPIAddress = "127.0.0.1"; m_reverseAPIPort = 8888; @@ -49,8 +48,6 @@ QByteArray TestSinkSettings::serialize() const s.writeBlob(4, m_spectrumGUI->serialize()); } - s.writeS32(5, m_workspaceIndex); - s.writeBlob(6, m_geometryBytes); s.writeBool(7, m_useReverseAPI); s.writeString(8, m_reverseAPIAddress); s.writeU32(9, m_reverseAPIPort); @@ -83,8 +80,6 @@ bool TestSinkSettings::deserialize(const QByteArray& data) m_spectrumGUI->deserialize(bytetmp); } - d.readS32(5, &m_workspaceIndex, 0); - d.readBlob(6, &m_geometryBytes); d.readBool(7, &m_useReverseAPI, false); d.readString(8, &m_reverseAPIAddress, "127.0.0.1"); d.readU32(9, &uintval, 0); diff --git a/plugins/samplesink/testsink/testsinksettings.h b/plugins/samplesink/testsink/testsinksettings.h index 6f6377ae6..27a7d0589 100644 --- a/plugins/samplesink/testsink/testsinksettings.h +++ b/plugins/samplesink/testsink/testsinksettings.h @@ -27,8 +27,6 @@ struct TestSinkSettings { quint64 m_sampleRate; quint32 m_log2Interp; Serializable *m_spectrumGUI; - int m_workspaceIndex; - QByteArray m_geometryBytes; bool m_useReverseAPI; QString m_reverseAPIAddress; uint16_t m_reverseAPIPort; diff --git a/plugins/samplesink/usrpoutput/usrpoutputgui.h b/plugins/samplesink/usrpoutput/usrpoutputgui.h index 4d602bc92..ac6205488 100644 --- a/plugins/samplesink/usrpoutput/usrpoutputgui.h +++ b/plugins/samplesink/usrpoutput/usrpoutputgui.h @@ -52,10 +52,6 @@ public: bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } virtual bool handleMessage(const Message& message); - virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; } - virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; } - virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; } - virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; } private: Ui::USRPOutputGUI* ui; diff --git a/plugins/samplesink/usrpoutput/usrpoutputsettings.cpp b/plugins/samplesink/usrpoutput/usrpoutputsettings.cpp index 3483bd7fd..ef7c067c2 100644 --- a/plugins/samplesink/usrpoutput/usrpoutputsettings.cpp +++ b/plugins/samplesink/usrpoutput/usrpoutputsettings.cpp @@ -42,7 +42,6 @@ void USRPOutputSettings::resetToDefaults() m_reverseAPIAddress = "127.0.0.1"; m_reverseAPIPort = 8888; m_reverseAPIDeviceIndex = 0; - m_workspaceIndex = 0; } QByteArray USRPOutputSettings::serialize() const @@ -62,8 +61,6 @@ QByteArray USRPOutputSettings::serialize() const s.writeU32(11, m_reverseAPIPort); s.writeU32(12, m_reverseAPIDeviceIndex); s.writeS32(13, m_loOffset); - s.writeS32(14, m_workspaceIndex); - s.writeBlob(15, m_geometryBytes); return s.final(); } @@ -103,8 +100,6 @@ bool USRPOutputSettings::deserialize(const QByteArray& data) d.readU32(12, &uintval, 0); m_reverseAPIDeviceIndex = uintval > 99 ? 99 : uintval; d.readS32(13, &m_loOffset, 0); - d.readS32(14, &m_workspaceIndex, 0); - d.readBlob(15, &m_geometryBytes); return true; } diff --git a/plugins/samplesink/usrpoutput/usrpoutputsettings.h b/plugins/samplesink/usrpoutput/usrpoutputsettings.h index 968e9de84..80e20d281 100644 --- a/plugins/samplesink/usrpoutput/usrpoutputsettings.h +++ b/plugins/samplesink/usrpoutput/usrpoutputsettings.h @@ -47,8 +47,6 @@ struct USRPOutputSettings QString m_reverseAPIAddress; uint16_t m_reverseAPIPort; uint16_t m_reverseAPIDeviceIndex; - int m_workspaceIndex; - QByteArray m_geometryBytes; USRPOutputSettings(); void resetToDefaults(); diff --git a/plugins/samplesink/xtrxoutput/xtrxoutputgui.h b/plugins/samplesink/xtrxoutput/xtrxoutputgui.h index c355e7524..1e724396c 100644 --- a/plugins/samplesink/xtrxoutput/xtrxoutputgui.h +++ b/plugins/samplesink/xtrxoutput/xtrxoutputgui.h @@ -44,10 +44,6 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } - virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; } - virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; } - virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; } - virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; } private: Ui::XTRXOutputGUI* ui; diff --git a/plugins/samplesink/xtrxoutput/xtrxoutputsettings.cpp b/plugins/samplesink/xtrxoutput/xtrxoutputsettings.cpp index 42ed29e20..f5dfe188c 100644 --- a/plugins/samplesink/xtrxoutput/xtrxoutputsettings.cpp +++ b/plugins/samplesink/xtrxoutput/xtrxoutputsettings.cpp @@ -42,7 +42,6 @@ void XTRXOutputSettings::resetToDefaults() m_reverseAPIAddress = "127.0.0.1"; m_reverseAPIPort = 8888; m_reverseAPIDeviceIndex = 0; - m_workspaceIndex = 0; } QByteArray XTRXOutputSettings::serialize() const @@ -64,8 +63,6 @@ QByteArray XTRXOutputSettings::serialize() const s.writeString(13, m_reverseAPIAddress); s.writeU32(14, m_reverseAPIPort); s.writeU32(15, m_reverseAPIDeviceIndex); - s.writeS32(16, m_workspaceIndex); - s.writeBlob(17, m_geometryBytes); return s.final(); } @@ -109,8 +106,6 @@ bool XTRXOutputSettings::deserialize(const QByteArray& data) d.readU32(15, &uintval, 0); m_reverseAPIDeviceIndex = uintval > 99 ? 99 : uintval; - d.readS32(16, &m_workspaceIndex, 0); - d.readBlob(17, &m_geometryBytes); return true; } diff --git a/plugins/samplesink/xtrxoutput/xtrxoutputsettings.h b/plugins/samplesink/xtrxoutput/xtrxoutputsettings.h index a7a9909e8..71954f555 100644 --- a/plugins/samplesink/xtrxoutput/xtrxoutputsettings.h +++ b/plugins/samplesink/xtrxoutput/xtrxoutputsettings.h @@ -49,8 +49,6 @@ struct XTRXOutputSettings QString m_reverseAPIAddress; uint16_t m_reverseAPIPort; uint16_t m_reverseAPIDeviceIndex; - int m_workspaceIndex; - QByteArray m_geometryBytes; XTRXOutputSettings(); void resetToDefaults(); diff --git a/plugins/samplesource/airspy/airspygui.h b/plugins/samplesource/airspy/airspygui.h index 2f0a9c03c..4d9ac8006 100644 --- a/plugins/samplesource/airspy/airspygui.h +++ b/plugins/samplesource/airspy/airspygui.h @@ -44,10 +44,6 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue* getInputMessageQueue() { return &m_inputMessageQueue; } - virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; } - virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; } - virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; } - virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; } uint32_t getDevSampleRate(unsigned int index); int getDevSampleRateIndex(uint32_t sampleRate); diff --git a/plugins/samplesource/airspy/airspysettings.cpp b/plugins/samplesource/airspy/airspysettings.cpp index 1026a1213..5be260a5a 100644 --- a/plugins/samplesource/airspy/airspysettings.cpp +++ b/plugins/samplesource/airspy/airspysettings.cpp @@ -46,7 +46,6 @@ void AirspySettings::resetToDefaults() m_reverseAPIAddress = "127.0.0.1"; m_reverseAPIPort = 8888; m_reverseAPIDeviceIndex = 0; - m_workspaceIndex = 0; } QByteArray AirspySettings::serialize() const @@ -72,8 +71,6 @@ QByteArray AirspySettings::serialize() const s.writeU32(17, m_reverseAPIPort); s.writeU32(18, m_reverseAPIDeviceIndex); s.writeBool(19, m_iqOrder); - s.writeS32(20, m_workspaceIndex); - s.writeBlob(21, m_geometryBytes); return s.final(); } @@ -121,8 +118,6 @@ bool AirspySettings::deserialize(const QByteArray& data) d.readU32(18, &uintval, 0); m_reverseAPIDeviceIndex = uintval > 99 ? 99 : uintval; d.readBool(19, &m_iqOrder, true); - d.readS32(20, &m_workspaceIndex, 0); - d.readBlob(21, &m_geometryBytes); return true; } diff --git a/plugins/samplesource/airspy/airspysettings.h b/plugins/samplesource/airspy/airspysettings.h index 9b6275aa4..5b2850fcd 100644 --- a/plugins/samplesource/airspy/airspysettings.h +++ b/plugins/samplesource/airspy/airspysettings.h @@ -47,8 +47,6 @@ struct AirspySettings { QString m_reverseAPIAddress; uint16_t m_reverseAPIPort; uint16_t m_reverseAPIDeviceIndex; - int m_workspaceIndex; - QByteArray m_geometryBytes; AirspySettings(); void resetToDefaults(); diff --git a/plugins/samplesource/airspyhf/airspyhfgui.h b/plugins/samplesource/airspyhf/airspyhfgui.h index 58e2ce8fa..aeb7bc5b1 100644 --- a/plugins/samplesource/airspyhf/airspyhfgui.h +++ b/plugins/samplesource/airspyhf/airspyhfgui.h @@ -45,10 +45,6 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue* getInputMessageQueue() { return &m_inputMessageQueue; } - virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; } - virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; } - virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; } - virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; } uint32_t getDevSampleRate(unsigned int index); int getDevSampleRateIndex(uint32_t sampleRate); diff --git a/plugins/samplesource/airspyhf/airspyhfsettings.cpp b/plugins/samplesource/airspyhf/airspyhfsettings.cpp index ead7a0f1b..4f7ae4b57 100644 --- a/plugins/samplesource/airspyhf/airspyhfsettings.cpp +++ b/plugins/samplesource/airspyhf/airspyhfsettings.cpp @@ -46,7 +46,6 @@ void AirspyHFSettings::resetToDefaults() m_attenuatorSteps = 0; m_dcBlock = false; m_iqCorrection = false; - m_workspaceIndex = 0; } QByteArray AirspyHFSettings::serialize() const @@ -71,8 +70,6 @@ QByteArray AirspyHFSettings::serialize() const s.writeBool(19, m_dcBlock); s.writeBool(20, m_iqCorrection); s.writeBool(21, m_iqOrder); - s.writeS32(22, m_workspaceIndex); - s.writeBlob(23, m_geometryBytes); return s.final(); } @@ -120,8 +117,6 @@ bool AirspyHFSettings::deserialize(const QByteArray& data) d.readBool(19, &m_dcBlock, false); d.readBool(20, &m_iqCorrection, false); d.readBool(21, &m_iqOrder, true); - d.readS32(22, &m_workspaceIndex, 0); - d.readBlob(23, &m_geometryBytes); return true; } diff --git a/plugins/samplesource/airspyhf/airspyhfsettings.h b/plugins/samplesource/airspyhf/airspyhfsettings.h index d093f747f..805a0a708 100644 --- a/plugins/samplesource/airspyhf/airspyhfsettings.h +++ b/plugins/samplesource/airspyhf/airspyhfsettings.h @@ -41,8 +41,6 @@ struct AirspyHFSettings quint32 m_attenuatorSteps; bool m_dcBlock; bool m_iqCorrection; - int m_workspaceIndex; - QByteArray m_geometryBytes; AirspyHFSettings(); void resetToDefaults(); diff --git a/plugins/samplesource/audioinput/audioinputgui.h b/plugins/samplesource/audioinput/audioinputgui.h index 46775ba21..9febcfbfd 100644 --- a/plugins/samplesource/audioinput/audioinputgui.h +++ b/plugins/samplesource/audioinput/audioinputgui.h @@ -46,10 +46,6 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } - virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; } - virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; } - virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; } - virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; } private: Ui::AudioInputGui* ui; diff --git a/plugins/samplesource/audioinput/audioinputsettings.cpp b/plugins/samplesource/audioinput/audioinputsettings.cpp index 777ccfd02..84fb1ae07 100644 --- a/plugins/samplesource/audioinput/audioinputsettings.cpp +++ b/plugins/samplesource/audioinput/audioinputsettings.cpp @@ -36,7 +36,6 @@ void AudioInputSettings::resetToDefaults() m_reverseAPIAddress = "127.0.0.1"; m_reverseAPIPort = 8888; m_reverseAPIDeviceIndex = 0; - m_workspaceIndex = 0; } QByteArray AudioInputSettings::serialize() const @@ -48,8 +47,6 @@ QByteArray AudioInputSettings::serialize() const s.writeFloat(3, m_volume); s.writeU32(4, m_log2Decim); s.writeS32(5, (int)m_iqMapping); - s.writeS32(6, m_workspaceIndex); - s.writeBlob(7, m_geometryBytes); s.writeBool(24, m_useReverseAPI); s.writeString(25, m_reverseAPIAddress); @@ -78,8 +75,6 @@ bool AudioInputSettings::deserialize(const QByteArray& data) d.readFloat(3, &m_volume, 1.0f); d.readU32(4, &m_log2Decim, 0); d.readS32(5, (int *)&m_iqMapping, IQMapping::L); - d.readS32(6, &m_workspaceIndex, 0); - d.readBlob(7, &m_geometryBytes); d.readBool(24, &m_useReverseAPI, false); d.readString(25, &m_reverseAPIAddress, "127.0.0.1"); diff --git a/plugins/samplesource/audioinput/audioinputsettings.h b/plugins/samplesource/audioinput/audioinputsettings.h index f707fbead..16f8eb4f1 100644 --- a/plugins/samplesource/audioinput/audioinputsettings.h +++ b/plugins/samplesource/audioinput/audioinputsettings.h @@ -39,8 +39,6 @@ struct AudioInputSettings { QString m_reverseAPIAddress; uint16_t m_reverseAPIPort; uint16_t m_reverseAPIDeviceIndex; - int m_workspaceIndex; - QByteArray m_geometryBytes; AudioInputSettings(); void resetToDefaults(); diff --git a/plugins/samplesource/bladerf1input/bladerf1inputgui.h b/plugins/samplesource/bladerf1input/bladerf1inputgui.h index a774bc4c1..45e6b7294 100644 --- a/plugins/samplesource/bladerf1input/bladerf1inputgui.h +++ b/plugins/samplesource/bladerf1input/bladerf1inputgui.h @@ -44,10 +44,6 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } - virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; } - virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; } - virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; } - virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; } private: Ui::Bladerf1InputGui* ui; diff --git a/plugins/samplesource/bladerf1input/bladerf1inputsettings.cpp b/plugins/samplesource/bladerf1input/bladerf1inputsettings.cpp index 4975d5c85..6745b693e 100644 --- a/plugins/samplesource/bladerf1input/bladerf1inputsettings.cpp +++ b/plugins/samplesource/bladerf1input/bladerf1inputsettings.cpp @@ -47,7 +47,6 @@ void BladeRF1InputSettings::resetToDefaults() m_reverseAPIAddress = "127.0.0.1"; m_reverseAPIPort = 8888; m_reverseAPIDeviceIndex = 0; - m_workspaceIndex = 0; } QByteArray BladeRF1InputSettings::serialize() const @@ -71,8 +70,6 @@ QByteArray BladeRF1InputSettings::serialize() const s.writeU32(15, m_reverseAPIPort); s.writeU32(16, m_reverseAPIDeviceIndex); s.writeBool(17, m_iqOrder); - s.writeS32(18, m_workspaceIndex); - s.writeBlob(19, m_geometryBytes); return s.final(); } @@ -120,8 +117,6 @@ bool BladeRF1InputSettings::deserialize(const QByteArray& data) d.readU32(16, &uintval, 0); m_reverseAPIDeviceIndex = uintval > 99 ? 99 : uintval; d.readBool(17, &m_iqOrder); - d.readS32(18, &m_workspaceIndex, 0); - d.readBlob(19, &m_geometryBytes); return true; } diff --git a/plugins/samplesource/bladerf1input/bladerf1inputsettings.h b/plugins/samplesource/bladerf1input/bladerf1inputsettings.h index 333218ffd..32ab4ba33 100644 --- a/plugins/samplesource/bladerf1input/bladerf1inputsettings.h +++ b/plugins/samplesource/bladerf1input/bladerf1inputsettings.h @@ -48,8 +48,6 @@ struct BladeRF1InputSettings { QString m_reverseAPIAddress; uint16_t m_reverseAPIPort; uint16_t m_reverseAPIDeviceIndex; - int m_workspaceIndex; - QByteArray m_geometryBytes; BladeRF1InputSettings(); void resetToDefaults(); diff --git a/plugins/samplesource/bladerf2input/bladerf2inputgui.h b/plugins/samplesource/bladerf2input/bladerf2inputgui.h index 08cb773a2..2647fccd6 100644 --- a/plugins/samplesource/bladerf2input/bladerf2inputgui.h +++ b/plugins/samplesource/bladerf2input/bladerf2inputgui.h @@ -44,10 +44,6 @@ public: virtual QByteArray serialize() const; virtual bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } - virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; } - virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; } - virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; } - virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; } private: Ui::Bladerf2InputGui* ui; diff --git a/plugins/samplesource/bladerf2input/bladerf2inputsettings.cpp b/plugins/samplesource/bladerf2input/bladerf2inputsettings.cpp index f0de33bae..17c975f62 100644 --- a/plugins/samplesource/bladerf2input/bladerf2inputsettings.cpp +++ b/plugins/samplesource/bladerf2input/bladerf2inputsettings.cpp @@ -44,7 +44,6 @@ void BladeRF2InputSettings::resetToDefaults() m_reverseAPIAddress = "127.0.0.1"; m_reverseAPIPort = 8888; m_reverseAPIDeviceIndex = 0; - m_workspaceIndex = 0; } QByteArray BladeRF2InputSettings::serialize() const @@ -68,8 +67,6 @@ QByteArray BladeRF2InputSettings::serialize() const s.writeU32(15, m_reverseAPIPort); s.writeU32(16, m_reverseAPIDeviceIndex); s.writeBool(17, m_iqOrder); - s.writeS32(18, m_workspaceIndex); - s.writeBlob(19, m_geometryBytes); return s.final(); } @@ -115,8 +112,6 @@ bool BladeRF2InputSettings::deserialize(const QByteArray& data) d.readU32(16, &uintval, 0); m_reverseAPIDeviceIndex = uintval > 99 ? 99 : uintval; d.readBool(17, &m_iqOrder, true); - d.readS32(18, &m_workspaceIndex, 0); - d.readBlob(19, &m_geometryBytes); return true; } diff --git a/plugins/samplesource/bladerf2input/bladerf2inputsettings.h b/plugins/samplesource/bladerf2input/bladerf2inputsettings.h index 7059be36a..6fb1a2952 100644 --- a/plugins/samplesource/bladerf2input/bladerf2inputsettings.h +++ b/plugins/samplesource/bladerf2input/bladerf2inputsettings.h @@ -46,8 +46,6 @@ struct BladeRF2InputSettings { QString m_reverseAPIAddress; uint16_t m_reverseAPIPort; uint16_t m_reverseAPIDeviceIndex; - int m_workspaceIndex; - QByteArray m_geometryBytes; BladeRF2InputSettings(); void resetToDefaults(); diff --git a/plugins/samplesource/fcdpro/fcdprogui.h b/plugins/samplesource/fcdpro/fcdprogui.h index 50c7594fb..4ddf0afa2 100644 --- a/plugins/samplesource/fcdpro/fcdprogui.h +++ b/plugins/samplesource/fcdpro/fcdprogui.h @@ -45,10 +45,6 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } - virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; } - virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; } - virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; } - virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; } private: Ui::FCDProGui* ui; diff --git a/plugins/samplesource/fcdpro/fcdprosettings.cpp b/plugins/samplesource/fcdpro/fcdprosettings.cpp index 3765dc3fb..075419d6b 100644 --- a/plugins/samplesource/fcdpro/fcdprosettings.cpp +++ b/plugins/samplesource/fcdpro/fcdprosettings.cpp @@ -55,7 +55,6 @@ void FCDProSettings::resetToDefaults() m_reverseAPIAddress = "127.0.0.1"; m_reverseAPIPort = 8888; m_reverseAPIDeviceIndex = 0; - m_workspaceIndex = 0; } QByteArray FCDProSettings::serialize() const @@ -90,8 +89,6 @@ QByteArray FCDProSettings::serialize() const s.writeU32(26, m_reverseAPIPort); s.writeU32(27, m_reverseAPIDeviceIndex); s.writeBool(28, m_iqOrder); - s.writeS32(29, m_workspaceIndex); - s.writeBlob(30, m_geometryBytes); return s.final(); } @@ -148,8 +145,6 @@ bool FCDProSettings::deserialize(const QByteArray& data) d.readU32(27, &uintval, 0); m_reverseAPIDeviceIndex = uintval > 99 ? 99 : uintval; d.readBool(28, &m_iqOrder, true); - d.readS32(29, &m_workspaceIndex, 0); - d.readBlob(30, &m_geometryBytes); return true; } diff --git a/plugins/samplesource/fcdpro/fcdprosettings.h b/plugins/samplesource/fcdpro/fcdprosettings.h index e00a1292a..03d126c7e 100644 --- a/plugins/samplesource/fcdpro/fcdprosettings.h +++ b/plugins/samplesource/fcdpro/fcdprosettings.h @@ -56,8 +56,6 @@ struct FCDProSettings { QString m_reverseAPIAddress; uint16_t m_reverseAPIPort; uint16_t m_reverseAPIDeviceIndex; - int m_workspaceIndex; - QByteArray m_geometryBytes; FCDProSettings(); void resetToDefaults(); diff --git a/plugins/samplesource/fcdproplus/fcdproplusgui.h b/plugins/samplesource/fcdproplus/fcdproplusgui.h index 11955c69c..8e4090fe7 100644 --- a/plugins/samplesource/fcdproplus/fcdproplusgui.h +++ b/plugins/samplesource/fcdproplus/fcdproplusgui.h @@ -44,10 +44,6 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } - virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; } - virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; } - virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; } - virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; } private: Ui::FCDProPlusGui* ui; diff --git a/plugins/samplesource/fcdproplus/fcdproplussettings.cpp b/plugins/samplesource/fcdproplus/fcdproplussettings.cpp index 53fd2be89..aaa302934 100644 --- a/plugins/samplesource/fcdproplus/fcdproplussettings.cpp +++ b/plugins/samplesource/fcdproplus/fcdproplussettings.cpp @@ -46,7 +46,6 @@ void FCDProPlusSettings::resetToDefaults() m_reverseAPIAddress = "127.0.0.1"; m_reverseAPIPort = 8888; m_reverseAPIDeviceIndex = 0; - m_workspaceIndex = 0; } QByteArray FCDProPlusSettings::serialize() const @@ -71,8 +70,6 @@ QByteArray FCDProPlusSettings::serialize() const s.writeU32(16, m_reverseAPIPort); s.writeU32(17, m_reverseAPIDeviceIndex); s.writeBool(18, m_iqOrder); - s.writeS32(19, m_workspaceIndex); - s.writeBlob(20, m_geometryBytes); return s.final(); } @@ -119,8 +116,6 @@ bool FCDProPlusSettings::deserialize(const QByteArray& data) d.readU32(17, &uintval, 0); m_reverseAPIDeviceIndex = uintval > 99 ? 99 : uintval; d.readBool(18, &m_iqOrder, true); - d.readS32(19, &m_workspaceIndex, 0); - d.readBlob(20, &m_geometryBytes); return true; } diff --git a/plugins/samplesource/fcdproplus/fcdproplussettings.h b/plugins/samplesource/fcdproplus/fcdproplussettings.h index 5b9397805..ed5bf7401 100644 --- a/plugins/samplesource/fcdproplus/fcdproplussettings.h +++ b/plugins/samplesource/fcdproplus/fcdproplussettings.h @@ -47,8 +47,6 @@ struct FCDProPlusSettings { QString m_reverseAPIAddress; uint16_t m_reverseAPIPort; uint16_t m_reverseAPIDeviceIndex; - int m_workspaceIndex; - QByteArray m_geometryBytes; FCDProPlusSettings(); void resetToDefaults(); diff --git a/plugins/samplesource/fileinput/fileinputgui.h b/plugins/samplesource/fileinput/fileinputgui.h index 6f6e7b01e..b447b2cb6 100644 --- a/plugins/samplesource/fileinput/fileinputgui.h +++ b/plugins/samplesource/fileinput/fileinputgui.h @@ -46,10 +46,6 @@ public: bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } virtual bool handleMessage(const Message& message); - virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; } - virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; } - virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; } - virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; } private: Ui::FileInputGUI* ui; diff --git a/plugins/samplesource/fileinput/fileinputsettings.cpp b/plugins/samplesource/fileinput/fileinputsettings.cpp index 0bbf959cc..c63128502 100644 --- a/plugins/samplesource/fileinput/fileinputsettings.cpp +++ b/plugins/samplesource/fileinput/fileinputsettings.cpp @@ -36,7 +36,6 @@ void FileInputSettings::resetToDefaults() m_reverseAPIAddress = "127.0.0.1"; m_reverseAPIPort = 8888; m_reverseAPIDeviceIndex = 0; - m_workspaceIndex = 0; } QByteArray FileInputSettings::serialize() const @@ -49,8 +48,6 @@ QByteArray FileInputSettings::serialize() const s.writeString(5, m_reverseAPIAddress); s.writeU32(6, m_reverseAPIPort); s.writeU32(7, m_reverseAPIDeviceIndex); - s.writeS32(8, m_workspaceIndex); - s.writeBlob(9, m_geometryBytes); return s.final(); } @@ -83,8 +80,6 @@ bool FileInputSettings::deserialize(const QByteArray& data) d.readU32(7, &uintval, 0); m_reverseAPIDeviceIndex = uintval > 99 ? 99 : uintval; - d.readS32(8, &m_workspaceIndex, 0); - d.readBlob(9, &m_geometryBytes); return true; } diff --git a/plugins/samplesource/fileinput/fileinputsettings.h b/plugins/samplesource/fileinput/fileinputsettings.h index 1d9bdaf33..d007000e5 100644 --- a/plugins/samplesource/fileinput/fileinputsettings.h +++ b/plugins/samplesource/fileinput/fileinputsettings.h @@ -29,8 +29,6 @@ struct FileInputSettings { QString m_reverseAPIAddress; uint16_t m_reverseAPIPort; uint16_t m_reverseAPIDeviceIndex; - int m_workspaceIndex; - QByteArray m_geometryBytes; static const unsigned int m_accelerationMaxScale; //!< Max power of 10 multiplier to 2,5,10 base ex: 2 -> 2,5,10,20,50,100,200,500,1000 diff --git a/plugins/samplesource/hackrfinput/hackrfinputgui.h b/plugins/samplesource/hackrfinput/hackrfinputgui.h index 51cac6209..2a90c045c 100644 --- a/plugins/samplesource/hackrfinput/hackrfinputgui.h +++ b/plugins/samplesource/hackrfinput/hackrfinputgui.h @@ -54,10 +54,6 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } - virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; } - virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; } - virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; } - virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; } private: Ui::HackRFInputGui* ui; diff --git a/plugins/samplesource/hackrfinput/hackrfinputsettings.cpp b/plugins/samplesource/hackrfinput/hackrfinputsettings.cpp index 7c20b4454..e7b14f65c 100644 --- a/plugins/samplesource/hackrfinput/hackrfinputsettings.cpp +++ b/plugins/samplesource/hackrfinput/hackrfinputsettings.cpp @@ -48,7 +48,6 @@ void HackRFInputSettings::resetToDefaults() m_reverseAPIAddress = "127.0.0.1"; m_reverseAPIPort = 8888; m_reverseAPIDeviceIndex = 0; - m_workspaceIndex = 0; } QByteArray HackRFInputSettings::serialize() const @@ -74,8 +73,6 @@ QByteArray HackRFInputSettings::serialize() const s.writeS64(19, m_transverterDeltaFrequency); s.writeBool(20, m_iqOrder); s.writeBool(21, m_autoBBF); - s.writeS32(22, m_workspaceIndex); - s.writeBlob(23, m_geometryBytes); return s.final(); } @@ -123,8 +120,6 @@ bool HackRFInputSettings::deserialize(const QByteArray& data) d.readS64(19, &m_transverterDeltaFrequency, 0); d.readBool(20, &m_iqOrder, true); d.readBool(21, &m_autoBBF, true); - d.readS32(22, &m_workspaceIndex, 0); - d.readBlob(23, &m_geometryBytes); return true; } diff --git a/plugins/samplesource/hackrfinput/hackrfinputsettings.h b/plugins/samplesource/hackrfinput/hackrfinputsettings.h index 36bad5903..de756e03e 100644 --- a/plugins/samplesource/hackrfinput/hackrfinputsettings.h +++ b/plugins/samplesource/hackrfinput/hackrfinputsettings.h @@ -48,8 +48,6 @@ struct HackRFInputSettings { QString m_reverseAPIAddress; uint16_t m_reverseAPIPort; uint16_t m_reverseAPIDeviceIndex; - int m_workspaceIndex; - QByteArray m_geometryBytes; HackRFInputSettings(); void resetToDefaults(); diff --git a/plugins/samplesource/kiwisdr/kiwisdrgui.h b/plugins/samplesource/kiwisdr/kiwisdrgui.h index 125fd6111..60b0936f2 100644 --- a/plugins/samplesource/kiwisdr/kiwisdrgui.h +++ b/plugins/samplesource/kiwisdr/kiwisdrgui.h @@ -46,10 +46,6 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } - virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; } - virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; } - virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; } - virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; } private: Ui::KiwiSDRGui* ui; diff --git a/plugins/samplesource/kiwisdr/kiwisdrsettings.cpp b/plugins/samplesource/kiwisdr/kiwisdrsettings.cpp index 8dfbecd14..f894288c7 100644 --- a/plugins/samplesource/kiwisdr/kiwisdrsettings.cpp +++ b/plugins/samplesource/kiwisdr/kiwisdrsettings.cpp @@ -38,8 +38,6 @@ void KiwiSDRSettings::resetToDefaults() m_reverseAPIAddress = "127.0.0.1"; m_reverseAPIPort = 8888; m_reverseAPIDeviceIndex = 0; - - m_workspaceIndex = 0; } QByteArray KiwiSDRSettings::serialize() const @@ -49,8 +47,6 @@ QByteArray KiwiSDRSettings::serialize() const s.writeString(2, m_serverAddress); s.writeU32(3, m_gain); s.writeBool(4, m_useAGC); - s.writeS32(5, m_workspaceIndex); - s.writeBlob(6, m_geometryBytes); s.writeBool(100, m_useReverseAPI); s.writeString(101, m_reverseAPIAddress); @@ -77,8 +73,6 @@ bool KiwiSDRSettings::deserialize(const QByteArray& data) d.readString(2, &m_serverAddress, "127.0.0.1:8073"); d.readU32(3, &m_gain, 20); d.readBool(4, &m_useAGC, true); - d.readS32(5, &m_workspaceIndex, 0); - d.readBlob(6, &m_geometryBytes); d.readBool(100, &m_useReverseAPI, false); d.readString(101, &m_reverseAPIAddress, "127.0.0.1"); diff --git a/plugins/samplesource/kiwisdr/kiwisdrsettings.h b/plugins/samplesource/kiwisdr/kiwisdrsettings.h index d084df434..23b09f312 100644 --- a/plugins/samplesource/kiwisdr/kiwisdrsettings.h +++ b/plugins/samplesource/kiwisdr/kiwisdrsettings.h @@ -35,9 +35,6 @@ struct KiwiSDRSettings { uint16_t m_reverseAPIPort; uint16_t m_reverseAPIDeviceIndex; - int m_workspaceIndex; - QByteArray m_geometryBytes; - KiwiSDRSettings(); void resetToDefaults(); QByteArray serialize() const; diff --git a/plugins/samplesource/limesdrinput/limesdrinputgui.h b/plugins/samplesource/limesdrinput/limesdrinputgui.h index e58cd27dc..2da4a4a39 100644 --- a/plugins/samplesource/limesdrinput/limesdrinputgui.h +++ b/plugins/samplesource/limesdrinput/limesdrinputgui.h @@ -44,10 +44,6 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } - virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; } - virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; } - virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; } - virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; } private: Ui::LimeSDRInputGUI* ui; diff --git a/plugins/samplesource/limesdrinput/limesdrinputsettings.cpp b/plugins/samplesource/limesdrinput/limesdrinputsettings.cpp index ba08ac6c5..01d7340c0 100644 --- a/plugins/samplesource/limesdrinput/limesdrinputsettings.cpp +++ b/plugins/samplesource/limesdrinput/limesdrinputsettings.cpp @@ -53,7 +53,6 @@ void LimeSDRInputSettings::resetToDefaults() m_reverseAPIAddress = "127.0.0.1"; m_reverseAPIPort = 8888; m_reverseAPIDeviceIndex = 0; - m_workspaceIndex = 0; } QByteArray LimeSDRInputSettings::serialize() const @@ -87,8 +86,6 @@ QByteArray LimeSDRInputSettings::serialize() const s.writeU32(26, m_reverseAPIPort); s.writeU32(27, m_reverseAPIDeviceIndex); s.writeBool(28, m_iqOrder); - s.writeS32(29, m_workspaceIndex); - s.writeBlob(30, m_geometryBytes); return s.final(); } @@ -147,8 +144,6 @@ bool LimeSDRInputSettings::deserialize(const QByteArray& data) d.readU32(27, &uintval, 0); m_reverseAPIDeviceIndex = uintval > 99 ? 99 : uintval; d.readBool(28, &m_iqOrder, true); - d.readS32(29, &m_workspaceIndex, 0); - d.readBlob(20, &m_geometryBytes); return true; } diff --git a/plugins/samplesource/limesdrinput/limesdrinputsettings.h b/plugins/samplesource/limesdrinput/limesdrinputsettings.h index 96f5fdae5..ef43bbadf 100644 --- a/plugins/samplesource/limesdrinput/limesdrinputsettings.h +++ b/plugins/samplesource/limesdrinput/limesdrinputsettings.h @@ -73,8 +73,6 @@ struct LimeSDRInputSettings QString m_reverseAPIAddress; uint16_t m_reverseAPIPort; uint16_t m_reverseAPIDeviceIndex; - int m_workspaceIndex; - QByteArray m_geometryBytes; LimeSDRInputSettings(); void resetToDefaults(); diff --git a/plugins/samplesource/localinput/localinputgui.h b/plugins/samplesource/localinput/localinputgui.h index 191b7bea8..3822ee69a 100644 --- a/plugins/samplesource/localinput/localinputgui.h +++ b/plugins/samplesource/localinput/localinputgui.h @@ -46,10 +46,6 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } - virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; } - virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; } - virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; } - virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; } private: Ui::LocalInputGui* ui; diff --git a/plugins/samplesource/localinput/localinputsettings.cpp b/plugins/samplesource/localinput/localinputsettings.cpp index 7c48abbe3..f567857ab 100644 --- a/plugins/samplesource/localinput/localinputsettings.cpp +++ b/plugins/samplesource/localinput/localinputsettings.cpp @@ -31,7 +31,6 @@ void LocalInputSettings::resetToDefaults() m_reverseAPIAddress = "127.0.0.1"; m_reverseAPIPort = 8888; m_reverseAPIDeviceIndex = 0; - m_workspaceIndex = 0; } QByteArray LocalInputSettings::serialize() const @@ -44,8 +43,6 @@ QByteArray LocalInputSettings::serialize() const s.writeString(4, m_reverseAPIAddress); s.writeU32(5, m_reverseAPIPort); s.writeU32(6, m_reverseAPIDeviceIndex); - s.writeS32(7, m_workspaceIndex); - s.writeBlob(8, m_geometryBytes); return s.final(); } @@ -78,8 +75,6 @@ bool LocalInputSettings::deserialize(const QByteArray& data) d.readU32(6, &uintval, 0); m_reverseAPIDeviceIndex = uintval > 99 ? 99 : uintval; - d.readS32(7, &m_workspaceIndex, 0); - d.readBlob(8, &m_geometryBytes); return true; } diff --git a/plugins/samplesource/localinput/localinputsettings.h b/plugins/samplesource/localinput/localinputsettings.h index 3f6beb9f0..42689d497 100644 --- a/plugins/samplesource/localinput/localinputsettings.h +++ b/plugins/samplesource/localinput/localinputsettings.h @@ -28,8 +28,6 @@ struct LocalInputSettings { QString m_reverseAPIAddress; uint16_t m_reverseAPIPort; uint16_t m_reverseAPIDeviceIndex; - int m_workspaceIndex; - QByteArray m_geometryBytes; LocalInputSettings(); void resetToDefaults(); diff --git a/plugins/samplesource/perseus/perseusgui.h b/plugins/samplesource/perseus/perseusgui.h index 38a8d1a5c..26e51a937 100644 --- a/plugins/samplesource/perseus/perseusgui.h +++ b/plugins/samplesource/perseus/perseusgui.h @@ -44,10 +44,6 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue* getInputMessageQueue() { return &m_inputMessageQueue; } - virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; } - virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; } - virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; } - virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; } uint32_t getDevSampleRate(unsigned int index); int getDevSampleRateIndex(uint32_t sampleRate); diff --git a/plugins/samplesource/perseus/perseussettings.cpp b/plugins/samplesource/perseus/perseussettings.cpp index 37e8e34c2..c75e08e00 100644 --- a/plugins/samplesource/perseus/perseussettings.cpp +++ b/plugins/samplesource/perseus/perseussettings.cpp @@ -41,7 +41,6 @@ void PerseusSettings::resetToDefaults() m_reverseAPIAddress = "127.0.0.1"; m_reverseAPIPort = 8888; m_reverseAPIDeviceIndex = 0; - m_workspaceIndex = 0; } QByteArray PerseusSettings::serialize() const @@ -62,8 +61,6 @@ QByteArray PerseusSettings::serialize() const s.writeU32(12, m_reverseAPIPort); s.writeU32(13, m_reverseAPIDeviceIndex); s.writeBool(14, m_iqOrder); - s.writeS32(15, m_workspaceIndex); - s.writeBlob(16, m_geometryBytes); return s.final(); } @@ -112,8 +109,6 @@ bool PerseusSettings::deserialize(const QByteArray& data) d.readU32(13, &uintval, 0); m_reverseAPIDeviceIndex = uintval > 99 ? 99 : uintval; d.readBool(14, &m_iqOrder, true); - d.readS32(15, &m_workspaceIndex, 0); - d.readBlob(16, &m_geometryBytes); return true; } diff --git a/plugins/samplesource/perseus/perseussettings.h b/plugins/samplesource/perseus/perseussettings.h index 284c86395..6c28b142a 100644 --- a/plugins/samplesource/perseus/perseussettings.h +++ b/plugins/samplesource/perseus/perseussettings.h @@ -47,8 +47,6 @@ struct PerseusSettings QString m_reverseAPIAddress; uint16_t m_reverseAPIPort; uint16_t m_reverseAPIDeviceIndex; - int m_workspaceIndex; - QByteArray m_geometryBytes; PerseusSettings(); void resetToDefaults(); diff --git a/plugins/samplesource/plutosdrinput/plutosdrinputgui.h b/plugins/samplesource/plutosdrinput/plutosdrinputgui.h index 003c6d298..2ed58ae5c 100644 --- a/plugins/samplesource/plutosdrinput/plutosdrinputgui.h +++ b/plugins/samplesource/plutosdrinput/plutosdrinputgui.h @@ -47,10 +47,6 @@ public: virtual QByteArray serialize() const; virtual bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } - virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; } - virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; } - virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; } - virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; } private: Ui::PlutoSDRInputGUI* ui; diff --git a/plugins/samplesource/plutosdrinput/plutosdrinputsettings.cpp b/plugins/samplesource/plutosdrinput/plutosdrinputsettings.cpp index 4d7c3ab9d..72cb5fae1 100644 --- a/plugins/samplesource/plutosdrinput/plutosdrinputsettings.cpp +++ b/plugins/samplesource/plutosdrinput/plutosdrinputsettings.cpp @@ -53,7 +53,6 @@ void PlutoSDRInputSettings::resetToDefaults() m_reverseAPIAddress = "127.0.0.1"; m_reverseAPIPort = 8888; m_reverseAPIDeviceIndex = 0; - m_workspaceIndex = 0; } QByteArray PlutoSDRInputSettings::serialize() const @@ -84,8 +83,6 @@ QByteArray PlutoSDRInputSettings::serialize() const s.writeBool(23, m_hwRFDCBlock); s.writeBool(24, m_hwIQCorrection); s.writeBool(25, m_iqOrder); - s.writeS32(26, m_workspaceIndex); - s.writeBlob(27, m_geometryBytes); return s.final(); } @@ -158,8 +155,6 @@ bool PlutoSDRInputSettings::deserialize(const QByteArray& data) d.readBool(23, &m_hwRFDCBlock, true); d.readBool(24, &m_hwIQCorrection, true); d.readBool(25, &m_iqOrder, true); - d.readS32(26, &m_workspaceIndex, 0); - d.readBlob(27, &m_geometryBytes); return true; } diff --git a/plugins/samplesource/plutosdrinput/plutosdrinputsettings.h b/plugins/samplesource/plutosdrinput/plutosdrinputsettings.h index 0a3397534..7dabb7310 100644 --- a/plugins/samplesource/plutosdrinput/plutosdrinputsettings.h +++ b/plugins/samplesource/plutosdrinput/plutosdrinputsettings.h @@ -83,8 +83,6 @@ struct PlutoSDRInputSettings { QString m_reverseAPIAddress; uint16_t m_reverseAPIPort; uint16_t m_reverseAPIDeviceIndex; - int m_workspaceIndex; - QByteArray m_geometryBytes; PlutoSDRInputSettings(); void resetToDefaults(); diff --git a/plugins/samplesource/remoteinput/remoteinputgui.h b/plugins/samplesource/remoteinput/remoteinputgui.h index 6de55ffc2..aaea76579 100644 --- a/plugins/samplesource/remoteinput/remoteinputgui.h +++ b/plugins/samplesource/remoteinput/remoteinputgui.h @@ -48,10 +48,6 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } - virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; } - virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; } - virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; } - virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; } private: Ui::RemoteInputGui* ui; diff --git a/plugins/samplesource/remoteinput/remoteinputsettings.cpp b/plugins/samplesource/remoteinput/remoteinputsettings.cpp index 97ede685d..c581c397f 100644 --- a/plugins/samplesource/remoteinput/remoteinputsettings.cpp +++ b/plugins/samplesource/remoteinput/remoteinputsettings.cpp @@ -37,7 +37,6 @@ void RemoteInputSettings::resetToDefaults() m_reverseAPIAddress = "127.0.0.1"; m_reverseAPIPort = 8888; m_reverseAPIDeviceIndex = 0; - m_workspaceIndex = 0; } QByteArray RemoteInputSettings::serialize() const @@ -56,8 +55,6 @@ QByteArray RemoteInputSettings::serialize() const s.writeString(12, m_reverseAPIAddress); s.writeU32(13, m_reverseAPIPort); s.writeU32(14, m_reverseAPIDeviceIndex); - s.writeS32(15, m_workspaceIndex); - s.writeBlob(16, m_geometryBytes); return s.final(); } @@ -98,8 +95,6 @@ bool RemoteInputSettings::deserialize(const QByteArray& data) d.readU32(14, &uintval, 0); m_reverseAPIDeviceIndex = uintval > 99 ? 99 : uintval; - d.readS32(15, &m_workspaceIndex, 0); - d.readBlob(16, &m_geometryBytes); return true; } diff --git a/plugins/samplesource/remoteinput/remoteinputsettings.h b/plugins/samplesource/remoteinput/remoteinputsettings.h index bb544070e..60cec764d 100644 --- a/plugins/samplesource/remoteinput/remoteinputsettings.h +++ b/plugins/samplesource/remoteinput/remoteinputsettings.h @@ -34,8 +34,6 @@ struct RemoteInputSettings { QString m_reverseAPIAddress; uint16_t m_reverseAPIPort; uint16_t m_reverseAPIDeviceIndex; - int m_workspaceIndex; - QByteArray m_geometryBytes; RemoteInputSettings(); void resetToDefaults(); diff --git a/plugins/samplesource/rtlsdr/rtlsdrgui.h b/plugins/samplesource/rtlsdr/rtlsdrgui.h index a6c3f0e98..ec1e22bd3 100644 --- a/plugins/samplesource/rtlsdr/rtlsdrgui.h +++ b/plugins/samplesource/rtlsdr/rtlsdrgui.h @@ -46,10 +46,6 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } - virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; } - virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; } - virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; } - virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; } private: Ui::RTLSDRGui* ui; diff --git a/plugins/samplesource/rtlsdr/rtlsdrsettings.cpp b/plugins/samplesource/rtlsdr/rtlsdrsettings.cpp index c8326fdf9..ace5e0f89 100644 --- a/plugins/samplesource/rtlsdr/rtlsdrsettings.cpp +++ b/plugins/samplesource/rtlsdr/rtlsdrsettings.cpp @@ -47,7 +47,6 @@ void RTLSDRSettings::resetToDefaults() m_reverseAPIAddress = "127.0.0.1"; m_reverseAPIPort = 8888; m_reverseAPIDeviceIndex = 0; - m_workspaceIndex = 0; } QByteArray RTLSDRSettings::serialize() const @@ -74,8 +73,6 @@ QByteArray RTLSDRSettings::serialize() const s.writeU32(19, m_reverseAPIDeviceIndex); s.writeBool(20, m_iqOrder); s.writeBool(21, m_biasTee); - s.writeS32(22, m_workspaceIndex); - s.writeBlob(23, m_geometryBytes); return s.final(); } @@ -124,8 +121,6 @@ bool RTLSDRSettings::deserialize(const QByteArray& data) m_reverseAPIDeviceIndex = utmp > 99 ? 99 : utmp; d.readBool(20, &m_iqOrder, true); d.readBool(21, &m_biasTee, false); - d.readS32(22, &m_workspaceIndex, 0); - d.readBlob(23, &m_geometryBytes); return true; } diff --git a/plugins/samplesource/rtlsdr/rtlsdrsettings.h b/plugins/samplesource/rtlsdr/rtlsdrsettings.h index 2d9913816..a80649339 100644 --- a/plugins/samplesource/rtlsdr/rtlsdrsettings.h +++ b/plugins/samplesource/rtlsdr/rtlsdrsettings.h @@ -48,8 +48,6 @@ struct RTLSDRSettings { QString m_reverseAPIAddress; uint16_t m_reverseAPIPort; uint16_t m_reverseAPIDeviceIndex; - int m_workspaceIndex; - QByteArray m_geometryBytes; RTLSDRSettings(); void resetToDefaults(); diff --git a/plugins/samplesource/sdrplay/sdrplaygui.h b/plugins/samplesource/sdrplay/sdrplaygui.h index 31e783831..d9d720084 100644 --- a/plugins/samplesource/sdrplay/sdrplaygui.h +++ b/plugins/samplesource/sdrplay/sdrplaygui.h @@ -46,10 +46,6 @@ public: virtual QByteArray serialize() const; virtual bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } - virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; } - virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; } - virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; } - virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; } private: Ui::SDRPlayGui* ui; diff --git a/plugins/samplesource/sdrplay/sdrplaysettings.cpp b/plugins/samplesource/sdrplay/sdrplaysettings.cpp index 626ab4ec0..4828bb086 100644 --- a/plugins/samplesource/sdrplay/sdrplaysettings.cpp +++ b/plugins/samplesource/sdrplay/sdrplaysettings.cpp @@ -47,7 +47,6 @@ void SDRPlaySettings::resetToDefaults() m_reverseAPIAddress = "127.0.0.1"; m_reverseAPIPort = 8888; m_reverseAPIDeviceIndex = 0; - m_workspaceIndex = 0; } QByteArray SDRPlaySettings::serialize() const @@ -73,8 +72,6 @@ QByteArray SDRPlaySettings::serialize() const s.writeU32(17, m_reverseAPIPort); s.writeU32(18, m_reverseAPIDeviceIndex); s.writeBool(19, m_iqOrder); - s.writeS32(20, m_workspaceIndex); - s.writeBlob(21, m_geometryBytes); return s.final(); } @@ -122,8 +119,6 @@ bool SDRPlaySettings::deserialize(const QByteArray& data) d.readU32(18, &uintval, 0); m_reverseAPIDeviceIndex = uintval > 99 ? 99 : uintval; d.readBool(19, &m_iqOrder, true); - d.readS32(20, &m_workspaceIndex, 0); - d.readBlob(21, &m_geometryBytes); return true; } diff --git a/plugins/samplesource/sdrplay/sdrplaysettings.h b/plugins/samplesource/sdrplay/sdrplaysettings.h index 1242b532a..7dbe2765c 100644 --- a/plugins/samplesource/sdrplay/sdrplaysettings.h +++ b/plugins/samplesource/sdrplay/sdrplaysettings.h @@ -50,8 +50,6 @@ struct SDRPlaySettings { QString m_reverseAPIAddress; uint16_t m_reverseAPIPort; uint16_t m_reverseAPIDeviceIndex; - int m_workspaceIndex; - QByteArray m_geometryBytes; SDRPlaySettings(); void resetToDefaults(); diff --git a/plugins/samplesource/sdrplayv3/sdrplayv3gui.h b/plugins/samplesource/sdrplayv3/sdrplayv3gui.h index b3fb7df14..39462b66d 100644 --- a/plugins/samplesource/sdrplayv3/sdrplayv3gui.h +++ b/plugins/samplesource/sdrplayv3/sdrplayv3gui.h @@ -46,10 +46,6 @@ public: virtual QByteArray serialize() const; virtual bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } - virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; } - virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; } - virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; } - virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; } private: Ui::SDRPlayV3Gui* ui; diff --git a/plugins/samplesource/sdrplayv3/sdrplayv3settings.cpp b/plugins/samplesource/sdrplayv3/sdrplayv3settings.cpp index 2cc56029d..3787f473b 100644 --- a/plugins/samplesource/sdrplayv3/sdrplayv3settings.cpp +++ b/plugins/samplesource/sdrplayv3/sdrplayv3settings.cpp @@ -54,7 +54,6 @@ void SDRPlayV3Settings::resetToDefaults() m_reverseAPIAddress = "127.0.0.1"; m_reverseAPIPort = 8888; m_reverseAPIDeviceIndex = 0; - m_workspaceIndex = 0; } QByteArray SDRPlayV3Settings::serialize() const @@ -86,8 +85,6 @@ QByteArray SDRPlayV3Settings::serialize() const s.writeBool(26, m_transverterMode); s.writeS64(27, m_transverterDeltaFrequency); s.writeBool(28, m_iqOrder); - s.writeS32(29, m_workspaceIndex); - s.writeBlob(30, m_geometryBytes); return s.final(); } @@ -141,8 +138,6 @@ bool SDRPlayV3Settings::deserialize(const QByteArray& data) d.readBool(26, &m_transverterMode, false); d.readS64(27, &m_transverterDeltaFrequency, 0); d.readBool(28, &m_iqOrder, true); - d.readS32(29, &m_workspaceIndex, 0); - d.readBlob(30, &m_geometryBytes); return true; } diff --git a/plugins/samplesource/sdrplayv3/sdrplayv3settings.h b/plugins/samplesource/sdrplayv3/sdrplayv3settings.h index f5c0c6466..6a2ed2a90 100644 --- a/plugins/samplesource/sdrplayv3/sdrplayv3settings.h +++ b/plugins/samplesource/sdrplayv3/sdrplayv3settings.h @@ -57,8 +57,6 @@ struct SDRPlayV3Settings { QString m_reverseAPIAddress; uint16_t m_reverseAPIPort; uint16_t m_reverseAPIDeviceIndex; - int m_workspaceIndex; - QByteArray m_geometryBytes; SDRPlayV3Settings(); void resetToDefaults(); diff --git a/plugins/samplesource/sigmffileinput/sigmffileinputgui.h b/plugins/samplesource/sigmffileinput/sigmffileinputgui.h index f468bcc03..381ad5ea9 100644 --- a/plugins/samplesource/sigmffileinput/sigmffileinputgui.h +++ b/plugins/samplesource/sigmffileinput/sigmffileinputgui.h @@ -46,10 +46,6 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } - virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; } - virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; } - virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; } - virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; } private: Ui::SigMFFileInputGUI* ui; diff --git a/plugins/samplesource/sigmffileinput/sigmffileinputsettings.cpp b/plugins/samplesource/sigmffileinput/sigmffileinputsettings.cpp index 2104b9c16..5abdce315 100644 --- a/plugins/samplesource/sigmffileinput/sigmffileinputsettings.cpp +++ b/plugins/samplesource/sigmffileinput/sigmffileinputsettings.cpp @@ -36,7 +36,6 @@ void SigMFFileInputSettings::resetToDefaults() m_reverseAPIAddress = "127.0.0.1"; m_reverseAPIPort = 8888; m_reverseAPIDeviceIndex = 0; - m_workspaceIndex = 0; } QByteArray SigMFFileInputSettings::serialize() const @@ -50,8 +49,6 @@ QByteArray SigMFFileInputSettings::serialize() const s.writeString(6, m_reverseAPIAddress); s.writeU32(7, m_reverseAPIPort); s.writeU32(8, m_reverseAPIDeviceIndex); - s.writeS32(9, m_workspaceIndex); - s.writeBlob(10, m_geometryBytes); return s.final(); } @@ -85,8 +82,6 @@ bool SigMFFileInputSettings::deserialize(const QByteArray& data) d.readU32(8, &uintval, 0); m_reverseAPIDeviceIndex = uintval > 99 ? 99 : uintval; - d.readS32(9, &m_workspaceIndex, 0); - d.readBlob(10, &m_geometryBytes); return true; } diff --git a/plugins/samplesource/sigmffileinput/sigmffileinputsettings.h b/plugins/samplesource/sigmffileinput/sigmffileinputsettings.h index 30ce9149e..9e365494f 100644 --- a/plugins/samplesource/sigmffileinput/sigmffileinputsettings.h +++ b/plugins/samplesource/sigmffileinput/sigmffileinputsettings.h @@ -30,8 +30,6 @@ struct SigMFFileInputSettings { QString m_reverseAPIAddress; uint16_t m_reverseAPIPort; uint16_t m_reverseAPIDeviceIndex; - int m_workspaceIndex; - QByteArray m_geometryBytes; static const unsigned int m_accelerationMaxScale; //!< Max power of 10 multiplier to 2,5,10 base ex: 2 -> 2,5,10,20,50,100,200,500,1000 diff --git a/plugins/samplesource/soapysdrinput/soapysdrinputgui.h b/plugins/samplesource/soapysdrinput/soapysdrinputgui.h index b1bc40230..483f9e823 100644 --- a/plugins/samplesource/soapysdrinput/soapysdrinputgui.h +++ b/plugins/samplesource/soapysdrinput/soapysdrinputgui.h @@ -51,10 +51,6 @@ public: virtual QByteArray serialize() const; virtual bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } - virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; } - virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; } - virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; } - virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; } private: void createRangesControl( diff --git a/plugins/samplesource/soapysdrinput/soapysdrinputsettings.cpp b/plugins/samplesource/soapysdrinput/soapysdrinputsettings.cpp index 0709f7f3f..6583dde0a 100644 --- a/plugins/samplesource/soapysdrinput/soapysdrinputsettings.cpp +++ b/plugins/samplesource/soapysdrinput/soapysdrinputsettings.cpp @@ -50,7 +50,6 @@ void SoapySDRInputSettings::resetToDefaults() m_reverseAPIAddress = "127.0.0.1"; m_reverseAPIPort = 8888; m_reverseAPIDeviceIndex = 0; - m_workspaceIndex = 0; } QByteArray SoapySDRInputSettings::serialize() const @@ -84,8 +83,6 @@ QByteArray SoapySDRInputSettings::serialize() const s.writeU32(25, m_reverseAPIPort); s.writeU32(26, m_reverseAPIDeviceIndex); s.writeBool(27, m_iqOrder); - s.writeS32(28, m_workspaceIndex); - s.writeBlob(29, m_geometryBytes); return s.final(); } @@ -149,8 +146,6 @@ bool SoapySDRInputSettings::deserialize(const QByteArray& data) d.readU32(26, &uintval, 0); m_reverseAPIDeviceIndex = uintval > 99 ? 99 : uintval; d.readBool(27, &m_iqOrder, true); - d.readS32(28, &m_workspaceIndex, 0); - d.readBlob(29, &m_geometryBytes); return true; } diff --git a/plugins/samplesource/soapysdrinput/soapysdrinputsettings.h b/plugins/samplesource/soapysdrinput/soapysdrinputsettings.h index d68416238..baab1b235 100644 --- a/plugins/samplesource/soapysdrinput/soapysdrinputsettings.h +++ b/plugins/samplesource/soapysdrinput/soapysdrinputsettings.h @@ -56,8 +56,6 @@ struct SoapySDRInputSettings { QString m_reverseAPIAddress; uint16_t m_reverseAPIPort; uint16_t m_reverseAPIDeviceIndex; - int m_workspaceIndex; - QByteArray m_geometryBytes; SoapySDRInputSettings(); void resetToDefaults(); diff --git a/plugins/samplesource/testsource/testsourcegui.h b/plugins/samplesource/testsource/testsourcegui.h index 61b34eab1..c6243b0ff 100644 --- a/plugins/samplesource/testsource/testsourcegui.h +++ b/plugins/samplesource/testsource/testsourcegui.h @@ -45,10 +45,6 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } - virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; } - virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; } - virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; } - virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; } private: Ui::TestSourceGui* ui; diff --git a/plugins/samplesource/testsource/testsourcesettings.cpp b/plugins/samplesource/testsource/testsourcesettings.cpp index 3db304e51..b94d7e044 100644 --- a/plugins/samplesource/testsource/testsourcesettings.cpp +++ b/plugins/samplesource/testsource/testsourcesettings.cpp @@ -46,7 +46,6 @@ void TestSourceSettings::resetToDefaults() m_reverseAPIAddress = "127.0.0.1"; m_reverseAPIPort = 8888; m_reverseAPIDeviceIndex = 0; - m_workspaceIndex = 0; } QByteArray TestSourceSettings::serialize() const @@ -72,8 +71,6 @@ QByteArray TestSourceSettings::serialize() const s.writeString(19, m_reverseAPIAddress); s.writeU32(20, m_reverseAPIPort); s.writeU32(21, m_reverseAPIDeviceIndex); - s.writeS32(22, m_workspaceIndex); - s.writeBlob(23, m_geometryBytes); return s.final(); } @@ -136,8 +133,6 @@ bool TestSourceSettings::deserialize(const QByteArray& data) d.readU32(21, &utmp, 0); m_reverseAPIDeviceIndex = utmp > 99 ? 99 : utmp; - d.readS32(22, &m_workspaceIndex, 0); - d.readBlob(23, &m_geometryBytes); return true; } diff --git a/plugins/samplesource/testsource/testsourcesettings.h b/plugins/samplesource/testsource/testsourcesettings.h index c22ffbb25..e7529d51a 100644 --- a/plugins/samplesource/testsource/testsourcesettings.h +++ b/plugins/samplesource/testsource/testsourcesettings.h @@ -64,8 +64,6 @@ struct TestSourceSettings { QString m_reverseAPIAddress; uint16_t m_reverseAPIPort; uint16_t m_reverseAPIDeviceIndex; - int m_workspaceIndex; - QByteArray m_geometryBytes; TestSourceSettings(); void resetToDefaults(); diff --git a/plugins/samplesource/usrpinput/usrpinputgui.h b/plugins/samplesource/usrpinput/usrpinputgui.h index 4facc1e5a..d9c61d808 100644 --- a/plugins/samplesource/usrpinput/usrpinputgui.h +++ b/plugins/samplesource/usrpinput/usrpinputgui.h @@ -50,10 +50,6 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } - virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; } - virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; } - virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; } - virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; } private: Ui::USRPInputGUI* ui; diff --git a/plugins/samplesource/usrpinput/usrpinputsettings.cpp b/plugins/samplesource/usrpinput/usrpinputsettings.cpp index 76cfc3061..9cd5210de 100644 --- a/plugins/samplesource/usrpinput/usrpinputsettings.cpp +++ b/plugins/samplesource/usrpinput/usrpinputsettings.cpp @@ -44,7 +44,6 @@ void USRPInputSettings::resetToDefaults() m_reverseAPIAddress = "127.0.0.1"; m_reverseAPIPort = 8888; m_reverseAPIDeviceIndex = 0; - m_workspaceIndex = 0; } QByteArray USRPInputSettings::serialize() const @@ -67,8 +66,6 @@ QByteArray USRPInputSettings::serialize() const s.writeU32(14, m_reverseAPIPort); s.writeU32(15, m_reverseAPIDeviceIndex); s.writeS32(16, m_loOffset); - s.writeS32(17, m_workspaceIndex); - s.writeBlob(18, m_geometryBytes); return s.final(); } @@ -113,8 +110,6 @@ bool USRPInputSettings::deserialize(const QByteArray& data) d.readU32(15, &uintval, 0); m_reverseAPIDeviceIndex = uintval > 99 ? 99 : uintval; d.readS32(16, &m_loOffset, 0); - d.readS32(17, &m_workspaceIndex, 0); - d.readBlob(18, &m_geometryBytes); return true; } diff --git a/plugins/samplesource/usrpinput/usrpinputsettings.h b/plugins/samplesource/usrpinput/usrpinputsettings.h index e843f0092..9f6aa5372 100644 --- a/plugins/samplesource/usrpinput/usrpinputsettings.h +++ b/plugins/samplesource/usrpinput/usrpinputsettings.h @@ -54,8 +54,6 @@ struct USRPInputSettings QString m_reverseAPIAddress; uint16_t m_reverseAPIPort; uint16_t m_reverseAPIDeviceIndex; - int m_workspaceIndex; - QByteArray m_geometryBytes; USRPInputSettings(); void resetToDefaults(); diff --git a/plugins/samplesource/xtrxinput/xtrxinputgui.h b/plugins/samplesource/xtrxinput/xtrxinputgui.h index d727a5c4d..7f8221479 100644 --- a/plugins/samplesource/xtrxinput/xtrxinputgui.h +++ b/plugins/samplesource/xtrxinput/xtrxinputgui.h @@ -45,10 +45,6 @@ public: QByteArray serialize() const; bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } - virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; } - virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; } - virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; } - virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; } private: Ui::XTRXInputGUI* ui; diff --git a/plugins/samplesource/xtrxinput/xtrxinputsettings.cpp b/plugins/samplesource/xtrxinput/xtrxinputsettings.cpp index 47bbe02d3..98584835d 100644 --- a/plugins/samplesource/xtrxinput/xtrxinputsettings.cpp +++ b/plugins/samplesource/xtrxinput/xtrxinputsettings.cpp @@ -48,7 +48,6 @@ void XTRXInputSettings::resetToDefaults() m_reverseAPIAddress = "127.0.0.1"; m_reverseAPIPort = 8888; m_reverseAPIDeviceIndex = 0; - m_workspaceIndex = 0; } QByteArray XTRXInputSettings::serialize() const @@ -77,8 +76,6 @@ QByteArray XTRXInputSettings::serialize() const s.writeU32(24, m_reverseAPIPort); s.writeU32(25, m_reverseAPIDeviceIndex); s.writeBool(26, m_iqOrder); - s.writeS32(27, m_workspaceIndex); - s.writeBlob(28, m_geometryBytes); return s.final(); } @@ -130,8 +127,6 @@ bool XTRXInputSettings::deserialize(const QByteArray& data) d.readU32(25, &uintval, 0); m_reverseAPIDeviceIndex = uintval > 99 ? 99 : uintval; d.readBool(26, &m_iqOrder, true); - d.readS32(27, &m_workspaceIndex, 0); - d.readBlob(28, &m_geometryBytes); return true; } diff --git a/plugins/samplesource/xtrxinput/xtrxinputsettings.h b/plugins/samplesource/xtrxinput/xtrxinputsettings.h index 40c771af8..fb0e9a63e 100644 --- a/plugins/samplesource/xtrxinput/xtrxinputsettings.h +++ b/plugins/samplesource/xtrxinput/xtrxinputsettings.h @@ -61,8 +61,6 @@ struct XTRXInputSettings QString m_reverseAPIAddress; uint16_t m_reverseAPIPort; uint16_t m_reverseAPIDeviceIndex; - int m_workspaceIndex; - QByteArray m_geometryBytes; XTRXInputSettings(); void resetToDefaults(); diff --git a/sdrgui/channel/readme.md b/sdrgui/channel/readme.md index 6ed955ce1..d99ad4ed2 100644 --- a/sdrgui/channel/readme.md +++ b/sdrgui/channel/readme.md @@ -58,7 +58,7 @@ If the device is a MIMO device and the channel is single stream based i.e. not M

    A.2.6: Toggle reverse API feature

    -Use this checkbox to toggle on/off the reverse API feature. With reverse API engaged the changes in the channel settings are forwarded to an API endpoint given by address (5.1.5), port (5.1.6), device index (5.1.7) and channel index (5.1.8) in the same format as the SDRangel REST API channel settings endpoint. With the values of the screenshot the API URL is: `http://127.0.0.1:8888/sdrangel/deviceset/0/channel/0/settings` The JSON payload follows the same format as the SDRangel REST API channel settings. Using the same example this would be: +Use this checkbox to toggle on/off the reverse API feature. With reverse API engaged the changes in the channel settings are forwarded to an API endpoint given by address (A.2.7), port (A.2.8), device index (A.2.9) and channel index (A.2.10) in the same format as the SDRangel REST API channel settings endpoint. With the values of the screenshot the API URL is: `http://127.0.0.1:8888/sdrangel/deviceset/0/channel/0/settings` The JSON payload follows the same format as the SDRangel REST API channel settings. Using the same example this would be: ``` { diff --git a/sdrgui/device/devicegui.h b/sdrgui/device/devicegui.h index 13fd111da..d4899c84d 100644 --- a/sdrgui/device/devicegui.h +++ b/sdrgui/device/devicegui.h @@ -59,11 +59,8 @@ public: virtual void resetToDefaults() = 0; virtual QByteArray serialize() const = 0; virtual bool deserialize(const QByteArray& data) = 0; - // Data saved in the derived settings - virtual void setWorkspaceIndex(int index)= 0; - virtual int getWorkspaceIndex() const = 0; - virtual void setGeometryBytes(const QByteArray& blob) = 0; - virtual QByteArray getGeometryBytes() const = 0; + void setWorkspaceIndex(int index) { m_workspaceIndex = index; } + int getWorkspaceIndex() const { return m_workspaceIndex; } virtual MessageQueue* getInputMessageQueue() = 0; @@ -86,6 +83,7 @@ protected: DeviceType m_deviceType; int m_deviceSetIndex; + int m_workspaceIndex; QString m_helpURL; QWidget *m_contents; ContextMenuType m_contextMenuType; diff --git a/sdrgui/device/readme.md b/sdrgui/device/readme.md index 0cddbbcaf..ee0f66265 100644 --- a/sdrgui/device/readme.md +++ b/sdrgui/device/readme.md @@ -30,7 +30,7 @@ All device types have common settings. Clicking on the button opens a dialog to

    A.2.1: Toggle reverse API feature

    -Use this checkbox to toggle on/off the reverse API feature. With reverse API engaged the changes in the device settings are forwarded to an API endpoint given by address (2.1.2.2), port (2.1.2.3) and device index (2.1.2.4) in the same format as the SDRangel REST API device settings endpoint. With the values of the screenshot the API URL is: `http://127.0.0.1:8888/sdrangel/deviceset/0/device/settings` The JSON payload follows the same format as the SDRangel REST API device settings. For example with HachRF Rx this would be something like: +Use this checkbox to toggle on/off the reverse API feature. With reverse API engaged the changes in the device settings are forwarded to an API endpoint given by address (A.2.2), port (A.2.3) and device index A.2.4) in the same format as the SDRangel REST API device settings endpoint. With the values of the screenshot the API URL is: `http://127.0.0.1:8888/sdrangel/deviceset/0/device/settings` The JSON payload follows the same format as the SDRangel REST API device settings. For example with HachRF Rx this would be something like: ``` { diff --git a/sdrgui/mainwindow.cpp b/sdrgui/mainwindow.cpp index 84bba6c5e..9995a75bc 100644 --- a/sdrgui/mainwindow.cpp +++ b/sdrgui/mainwindow.cpp @@ -1266,7 +1266,8 @@ void MainWindow::loadConfiguration(const Configuration *configuration, bool from deviceSetPreset.getSpectrumWorkspaceIndex() : deviceWorkspaceIndex; sampleSinkAdd(m_workspaces[deviceWorkspaceIndex], m_workspaces[spectrumWorkspaceIndex], bestDeviceIndex); - } else if (deviceSetPreset.isMIMOPreset()) + } + else if (deviceSetPreset.isMIMOPreset()) { int bestDeviceIndex = DeviceEnumerator::instance()->getBestMIMOSamplingDeviceIndex( deviceSetPreset.getSelectedDevice().m_deviceId, @@ -1350,7 +1351,7 @@ void MainWindow::saveConfiguration(Configuration *configuration) deviceSetPresets.back().setSpectrumWorkspaceIndex(deviceUISet->m_mainSpectrumGUI->getWorkspaceIndex()); deviceSetPresets.back().setDeviceGeometry(deviceUISet->m_deviceGUI->saveGeometry()); deviceSetPresets.back().setDeviceWorkspaceIndex(deviceUISet->m_deviceGUI->getWorkspaceIndex()); - qDebug("MainWindow::saveConfiguration: %s device in workspace %d spectrun in %d", + qDebug("MainWindow::saveConfiguration: %s device in workspace %d spectrum in %d", qPrintable(deviceUISet->m_deviceAPI->getSamplingDeviceId()), deviceUISet->m_deviceGUI->getWorkspaceIndex(), deviceUISet->m_mainSpectrumGUI->getWorkspaceIndex()); From 5b0f0e4e511bae87512133240c13570a15dcb5e0 Mon Sep 17 00:00:00 2001 From: f4exb Date: Wed, 20 Apr 2022 21:33:18 +0200 Subject: [PATCH 043/115] Massive UI revamping (v7): restore menu shortcuts and ellipsis. Fixes #1208 --- sdrgui/gui/myposdialog.ui | 4 ++-- sdrgui/mainwindow.cpp | 50 +++++++++++++++++++-------------------- 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/sdrgui/gui/myposdialog.ui b/sdrgui/gui/myposdialog.ui index 3e88ed7a6..fde93a7b0 100644 --- a/sdrgui/gui/myposdialog.ui +++ b/sdrgui/gui/myposdialog.ui @@ -7,7 +7,7 @@ 0 0 324 - 190 + 229
    @@ -17,7 +17,7 @@ - Dialog + My Position diff --git a/sdrgui/mainwindow.cpp b/sdrgui/mainwindow.cpp index 9995a75bc..6fcd1cc19 100644 --- a/sdrgui/mainwindow.cpp +++ b/sdrgui/mainwindow.cpp @@ -1396,78 +1396,78 @@ void MainWindow::createMenuBar() { QMenuBar *menuBar = this->menuBar(); - QMenu *fileMenu = menuBar->addMenu("File"); - QAction *exitAction = fileMenu->addAction("Exit"); + QMenu *fileMenu = menuBar->addMenu("&File"); + QAction *exitAction = fileMenu->addAction("E&xit"); exitAction->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_Q)); exitAction->setToolTip("Exit"); QObject::connect(exitAction, &QAction::triggered, this, &QMainWindow::close); - QMenu *viewMenu = menuBar->addMenu("View"); - QAction *fullscreenAction = viewMenu->addAction("Fullscreen"); + QMenu *viewMenu = menuBar->addMenu("&View"); + QAction *fullscreenAction = viewMenu->addAction("&Fullscreen"); fullscreenAction->setShortcut(QKeySequence(Qt::Key_F11)); fullscreenAction->setToolTip("Toggle fullscreen view"); fullscreenAction->setCheckable(true); QObject::connect(fullscreenAction, &QAction::triggered, this, &MainWindow::on_action_View_Fullscreen_toggled); - QMenu *workspacesMenu = menuBar->addMenu("Workspaces"); - QAction *newWorkspaceAction = workspacesMenu->addAction("New"); + QMenu *workspacesMenu = menuBar->addMenu("&Workspaces"); + QAction *newWorkspaceAction = workspacesMenu->addAction("&New"); newWorkspaceAction->setToolTip("Add a new workspace"); QObject::connect(newWorkspaceAction, &QAction::triggered, this, &MainWindow::addWorkspace); - QAction *viewAllWorkspacesAction = workspacesMenu->addAction("View all"); + QAction *viewAllWorkspacesAction = workspacesMenu->addAction("&View all"); viewAllWorkspacesAction->setToolTip("View all workspaces"); QObject::connect(viewAllWorkspacesAction, &QAction::triggered, this, &MainWindow::viewAllWorkspaces); - QAction *removeEmptyWorkspacesAction = workspacesMenu->addAction("Remove empty"); + QAction *removeEmptyWorkspacesAction = workspacesMenu->addAction("&Remove empty"); removeEmptyWorkspacesAction->setToolTip("Remove empty workspaces"); QObject::connect(removeEmptyWorkspacesAction, &QAction::triggered, this, &MainWindow::removeEmptyWorkspaces); - QMenu *preferencesMenu = menuBar->addMenu("Preferences"); - QAction *configurationsAction = preferencesMenu->addAction("Configurations"); + QMenu *preferencesMenu = menuBar->addMenu("&Preferences"); + QAction *configurationsAction = preferencesMenu->addAction("&Configurations..."); configurationsAction->setToolTip("Manage configurations"); QObject::connect(configurationsAction, &QAction::triggered, this, &MainWindow::on_action_Configurations_triggered); - QAction *audioAction = preferencesMenu->addAction("Audio"); + QAction *audioAction = preferencesMenu->addAction("&Audio..."); audioAction->setToolTip("Audio preferences"); QObject::connect(audioAction, &QAction::triggered, this, &MainWindow::on_action_Audio_triggered); - QAction *loggingAction = preferencesMenu->addAction("Logging"); + QAction *loggingAction = preferencesMenu->addAction("&Logging..."); loggingAction->setToolTip("Logging preferences"); QObject::connect(loggingAction, &QAction::triggered, this, &MainWindow::on_action_Logging_triggered); - QAction *myPositionAction = preferencesMenu->addAction("My Position"); + QAction *myPositionAction = preferencesMenu->addAction("My &Position..."); myPositionAction->setToolTip("Set station position"); QObject::connect(myPositionAction, &QAction::triggered, this, &MainWindow::on_action_My_Position_triggered); - QAction *fftAction = preferencesMenu->addAction("FFT"); + QAction *fftAction = preferencesMenu->addAction("&FFT..."); fftAction->setToolTip("Set FFT cache"); QObject::connect(fftAction, &QAction::triggered, this, &MainWindow::on_action_FFT_triggered); #ifndef __APPLE__ - QAction *ambeAction = preferencesMenu->addAction("AMBE"); + QAction *ambeAction = preferencesMenu->addAction("A&MBE..."); ambeAction->setToolTip("AMBE options"); QObject::connect(ambeAction, &QAction::triggered, this, &MainWindow::on_action_AMBE_triggered); #endif #if defined(HAS_LIMERFEUSB) - QAction *limeRFEAction = preferencesMenu->addAction("Lime RFE"); + QAction *limeRFEAction = preferencesMenu->addAction("Lime &RFE..."); limeRFEAction->setToolTip("Lime RFE options"); QObject::connect(limeRFEAction, &QAction::triggered, this, &MainWindow::on_action_LimeRFE_triggered); #endif - QMenu *devicesMenu = preferencesMenu->addMenu("Devices"); - QAction *userArgumentsAction = devicesMenu->addAction("User arguments"); + QMenu *devicesMenu = preferencesMenu->addMenu("&Devices"); + QAction *userArgumentsAction = devicesMenu->addAction("&User arguments..."); userArgumentsAction->setToolTip("Device custom user arguments"); QObject::connect(userArgumentsAction, &QAction::triggered, this, &MainWindow::on_action_DeviceUserArguments_triggered); - QAction *commandsAction = preferencesMenu->addAction("Commands"); + QAction *commandsAction = preferencesMenu->addAction("C&ommands..."); commandsAction->setToolTip("External commands dialog"); QObject::connect(commandsAction, &QAction::triggered, this, &MainWindow::on_action_commands_triggered); - QAction *saveAllAction = preferencesMenu->addAction("Save all"); + QAction *saveAllAction = preferencesMenu->addAction("&Save all"); saveAllAction->setToolTip("Save all current settings"); QObject::connect(saveAllAction, &QAction::triggered, this, &MainWindow::on_action_saveAll_triggered); - QMenu *helpMenu = menuBar->addMenu("Help"); - QAction *quickStartAction = helpMenu->addAction("Quick start"); + QMenu *helpMenu = menuBar->addMenu("&Help"); + QAction *quickStartAction = helpMenu->addAction("&Quick start..."); quickStartAction->setToolTip("Instructions for quick start"); QObject::connect(quickStartAction, &QAction::triggered, this, &MainWindow::on_action_Quick_Start_triggered); - QAction *mainWindowAction = helpMenu->addAction("Main Window"); + QAction *mainWindowAction = helpMenu->addAction("&Main Window..."); mainWindowAction->setToolTip("Help on main window details"); QObject::connect(mainWindowAction, &QAction::triggered, this, &MainWindow::on_action_Main_Window_triggered); - QAction *loadedPluginsAction = helpMenu->addAction("Loaded plugins"); + QAction *loadedPluginsAction = helpMenu->addAction("Loaded &Plugins..."); loadedPluginsAction->setToolTip("List available plugins"); QObject::connect(loadedPluginsAction, &QAction::triggered, this, &MainWindow::on_action_Loaded_Plugins_triggered); - QAction *aboutAction = helpMenu->addAction("ABout SDRangel"); + QAction *aboutAction = helpMenu->addAction("&About SDRangel..."); aboutAction->setToolTip("SDRangel application details"); QObject::connect(aboutAction, &QAction::triggered, this, &MainWindow::on_action_About_triggered); } From 62ca8f8b29e76a0fedcfd0e6fa1887fdf4856254 Mon Sep 17 00:00:00 2001 From: f4exb Date: Wed, 20 Apr 2022 22:33:28 +0200 Subject: [PATCH 044/115] Massive UI revamping (v7): fixed main spectrum window minimum height. Fixes #1210 --- sdrgui/mainspectrum/mainspectrumgui.cpp | 4 +++- sdrgui/mainspectrum/mainspectrumgui.h | 2 ++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/sdrgui/mainspectrum/mainspectrumgui.cpp b/sdrgui/mainspectrum/mainspectrumgui.cpp index 04aab3795..2567bdc41 100644 --- a/sdrgui/mainspectrum/mainspectrumgui.cpp +++ b/sdrgui/mainspectrum/mainspectrumgui.cpp @@ -42,6 +42,8 @@ MainSpectrumGUI::MainSpectrumGUI(GLSpectrum *spectrum, GLSpectrumGUI *spectrumGU setWindowFlags(windowFlags() | Qt::FramelessWindowHint); m_helpURL = "sdrgui/mainspectrum/readme.md"; + setMinimumSize(m_MinimumWidth, m_MinimumHeight); + m_indexLabel = new QLabel(); m_indexLabel->setFixedSize(32, 16); m_indexLabel->setStyleSheet("QLabel { background-color: rgb(128, 128, 128); qproperty-alignment: AlignCenter; }"); @@ -221,7 +223,7 @@ void MainSpectrumGUI::shrinkWindow() { qDebug("MainSpectrumGUI::shrinkWindow"); adjustSize(); - resize(width(), 360); + resize(width(), m_MinimumHeight); } void MainSpectrumGUI::setTitle(const QString& title) diff --git a/sdrgui/mainspectrum/mainspectrumgui.h b/sdrgui/mainspectrum/mainspectrumgui.h index 29892af29..859e61678 100644 --- a/sdrgui/mainspectrum/mainspectrumgui.h +++ b/sdrgui/mainspectrum/mainspectrumgui.h @@ -86,6 +86,8 @@ private: QSizeGrip *m_sizeGripBottomRight; bool m_drag; QPoint m_DragPosition; + static const int m_MinimumWidth = 360; + static const int m_MinimumHeight = 200 + 20 + 10 + 4*22 + 5; void closeEvent(QCloseEvent *event); void mousePressEvent(QMouseEvent* event); From 90d331675a47b37d81ce74cf5c50d31102820343 Mon Sep 17 00:00:00 2001 From: f4exb Date: Thu, 21 Apr 2022 07:53:36 +0200 Subject: [PATCH 045/115] Massive UI revamping (v7): fixed widget size handling: sample sources. Part ofs #1209 --- plugins/samplesource/airspy/airspygui.cpp | 8 ++ plugins/samplesource/airspy/airspygui.h | 3 + plugins/samplesource/airspy/airspygui.ui | 24 +++--- plugins/samplesource/airspyhf/airspyhfgui.cpp | 8 ++ plugins/samplesource/airspyhf/airspyhfgui.h | 3 + plugins/samplesource/airspyhf/airspyhfgui.ui | 25 ++++--- .../samplesource/audioinput/audioinputgui.cpp | 8 ++ .../samplesource/audioinput/audioinputgui.h | 3 + .../samplesource/audioinput/audioinputgui.ui | 46 ++++++++++-- .../bladerf1input/bladerf1inputgui.cpp | 8 ++ .../bladerf1input/bladerf1inputgui.h | 3 + .../bladerf1input/bladerf1inputgui.ui | 22 ++++-- .../bladerf2input/bladerf2inputgui.cpp | 8 ++ .../bladerf2input/bladerf2inputgui.h | 3 + .../bladerf2input/bladerf2inputgui.ui | 22 ++++-- plugins/samplesource/fcdpro/fcdprogui.cpp | 8 ++ plugins/samplesource/fcdpro/fcdprogui.h | 3 + plugins/samplesource/fcdpro/fcdprogui.ui | 22 ++++-- .../samplesource/fcdproplus/fcdproplusgui.cpp | 8 ++ .../samplesource/fcdproplus/fcdproplusgui.h | 3 + .../samplesource/fcdproplus/fcdproplusgui.ui | 22 ++++-- .../samplesource/fileinput/fileinputgui.cpp | 8 ++ plugins/samplesource/fileinput/fileinputgui.h | 3 + .../samplesource/fileinput/fileinputgui.ui | 13 ++-- .../hackrfinput/hackrfinputgui.cpp | 8 ++ .../samplesource/hackrfinput/hackrfinputgui.h | 3 + .../hackrfinput/hackrfinputgui.ui | 24 +++--- plugins/samplesource/kiwisdr/kiwisdrgui.cpp | 8 ++ plugins/samplesource/kiwisdr/kiwisdrgui.h | 3 + plugins/samplesource/kiwisdr/kiwisdrgui.ui | 25 ++++--- .../limesdrinput/limesdrinputgui.cpp | 8 ++ .../limesdrinput/limesdrinputgui.h | 3 + .../limesdrinput/limesdrinputgui.ui | 32 ++++---- .../samplesource/localinput/localinputgui.cpp | 8 ++ .../samplesource/localinput/localinputgui.h | 3 + .../samplesource/localinput/localinputgui.ui | 31 ++++++-- plugins/samplesource/perseus/perseusgui.cpp | 9 +++ plugins/samplesource/perseus/perseusgui.h | 3 + plugins/samplesource/perseus/perseusgui.ui | 22 ++++-- .../plutosdrinput/plutosdrinputgui.cpp | 8 ++ .../plutosdrinput/plutosdrinputgui.h | 3 + .../plutosdrinput/plutosdrinputgui.ui | 22 ++++-- .../remoteinput/remoteinputgui.cpp | 8 ++ .../samplesource/remoteinput/remoteinputgui.h | 3 + .../remoteinput/remoteinputgui.ui | 40 ++++++---- plugins/samplesource/rtlsdr/rtlsdrgui.cpp | 9 ++- plugins/samplesource/rtlsdr/rtlsdrgui.h | 3 + plugins/samplesource/rtlsdr/rtlsdrgui.ui | 22 ++++-- plugins/samplesource/sdrplay/sdrplaygui.cpp | 8 ++ plugins/samplesource/sdrplay/sdrplaygui.h | 3 + plugins/samplesource/sdrplay/sdrplaygui.ui | 29 ++++--- .../samplesource/sdrplayv3/sdrplayv3gui.cpp | 8 ++ plugins/samplesource/sdrplayv3/sdrplayv3gui.h | 3 + .../samplesource/sdrplayv3/sdrplayv3gui.ui | 41 +++++----- .../sigmffileinput/sigmffileinputgui.cpp | 8 ++ .../sigmffileinput/sigmffileinputgui.h | 3 + .../sigmffileinput/sigmffileinputgui.ui | 16 ++-- .../soapysdrinput/soapysdrinputgui.cpp | 8 ++ .../soapysdrinput/soapysdrinputgui.h | 3 + .../soapysdrinput/soapysdrinputgui.ui | 34 +++++++-- .../samplesource/testsource/testsourcegui.cpp | 8 ++ .../samplesource/testsource/testsourcegui.h | 3 + .../samplesource/testsource/testsourcegui.ui | 75 ++++++++++++++----- .../samplesource/usrpinput/usrpinputgui.cpp | 8 ++ plugins/samplesource/usrpinput/usrpinputgui.h | 3 + .../samplesource/usrpinput/usrpinputgui.ui | 30 +++++--- .../samplesource/xtrxinput/xtrxinputgui.cpp | 8 ++ plugins/samplesource/xtrxinput/xtrxinputgui.h | 3 + .../samplesource/xtrxinput/xtrxinputgui.ui | 26 ++++--- 69 files changed, 695 insertions(+), 225 deletions(-) diff --git a/plugins/samplesource/airspy/airspygui.cpp b/plugins/samplesource/airspy/airspygui.cpp index 951b14c0d..09b22bb81 100644 --- a/plugins/samplesource/airspy/airspygui.cpp +++ b/plugins/samplesource/airspy/airspygui.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include @@ -47,6 +48,7 @@ AirspyGui::AirspyGui(DeviceUISet *deviceUISet, QWidget* parent) : m_sampleSource = (AirspyInput*) m_deviceUISet->m_deviceAPI->getSampleSource(); ui->setupUi(getContents()); + setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); getContents()->setStyleSheet(QString(tr("#AirspyGui { border: 1px solid %1 }") .arg(palette().highlight().color().darker(115).name()))); ui->centerFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold)); @@ -104,6 +106,12 @@ bool AirspyGui::deserialize(const QByteArray& data) } } +void AirspyGui::resizeEvent(QResizeEvent* size) +{ + adjustSize(); + size->accept(); +} + bool AirspyGui::handleMessage(const Message& message) { if (AirspyInput::MsgConfigureAirspy::match(message)) diff --git a/plugins/samplesource/airspy/airspygui.h b/plugins/samplesource/airspy/airspygui.h index 4d9ac8006..cc8151b50 100644 --- a/plugins/samplesource/airspy/airspygui.h +++ b/plugins/samplesource/airspy/airspygui.h @@ -48,6 +48,9 @@ public: uint32_t getDevSampleRate(unsigned int index); int getDevSampleRateIndex(uint32_t sampleRate); +protected: + void resizeEvent(QResizeEvent* size); + private: Ui::AirspyGui* ui; diff --git a/plugins/samplesource/airspy/airspygui.ui b/plugins/samplesource/airspy/airspygui.ui index e59c34ce1..c0bd4ebb4 100644 --- a/plugins/samplesource/airspy/airspygui.ui +++ b/plugins/samplesource/airspy/airspygui.ui @@ -7,11 +7,11 @@ 0 0 360 - 244 + 187 - + 0 0 @@ -19,7 +19,13 @@ 360 - 0 + 187 + + + + + 360 + 187 @@ -286,7 +292,7 @@ - + 0 0 @@ -612,17 +618,17 @@ + + ButtonSwitch + QToolButton +
    gui/buttonswitch.h
    +
    ValueDial QWidget
    gui/valuedial.h
    1
    - - ButtonSwitch - QToolButton -
    gui/buttonswitch.h
    -
    TransverterButton QPushButton diff --git a/plugins/samplesource/airspyhf/airspyhfgui.cpp b/plugins/samplesource/airspyhf/airspyhfgui.cpp index c8831abaa..3426f0f10 100644 --- a/plugins/samplesource/airspyhf/airspyhfgui.cpp +++ b/plugins/samplesource/airspyhf/airspyhfgui.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include @@ -46,6 +47,7 @@ AirspyHFGui::AirspyHFGui(DeviceUISet *deviceUISet, QWidget* parent) : m_sampleSource = (AirspyHFInput*) m_deviceUISet->m_deviceAPI->getSampleSource(); ui->setupUi(getContents()); + setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); getContents()->setStyleSheet(QString(tr("#AirspyHFGui { border: 1px solid %1 }") .arg(palette().highlight().color().darker(115).name()))); m_helpURL = "plugins/samplesource/airspyhf/readme.md"; @@ -104,6 +106,12 @@ bool AirspyHFGui::deserialize(const QByteArray& data) } } +void AirspyHFGui::resizeEvent(QResizeEvent* size) +{ + adjustSize(); + size->accept(); +} + bool AirspyHFGui::handleMessage(const Message& message) { if (AirspyHFInput::MsgConfigureAirspyHF::match(message)) diff --git a/plugins/samplesource/airspyhf/airspyhfgui.h b/plugins/samplesource/airspyhf/airspyhfgui.h index aeb7bc5b1..238003e29 100644 --- a/plugins/samplesource/airspyhf/airspyhfgui.h +++ b/plugins/samplesource/airspyhf/airspyhfgui.h @@ -49,6 +49,9 @@ public: uint32_t getDevSampleRate(unsigned int index); int getDevSampleRateIndex(uint32_t sampleRate); +protected: + void resizeEvent(QResizeEvent* size); + private: Ui::AirspyHFGui* ui; diff --git a/plugins/samplesource/airspyhf/airspyhfgui.ui b/plugins/samplesource/airspyhf/airspyhfgui.ui index efd5e525b..399a64899 100644 --- a/plugins/samplesource/airspyhf/airspyhfgui.ui +++ b/plugins/samplesource/airspyhf/airspyhfgui.ui @@ -7,11 +7,11 @@ 0 0 360 - 180 + 137 - + 0 0 @@ -19,7 +19,13 @@ 360 - 0 + 137 + + + + + 360 + 137 @@ -49,9 +55,6 @@ - - 4 - @@ -584,17 +587,17 @@ + + ButtonSwitch + QToolButton +
    gui/buttonswitch.h
    +
    ValueDial QWidget
    gui/valuedial.h
    1
    - - ButtonSwitch - QToolButton -
    gui/buttonswitch.h
    -
    TransverterButton QPushButton diff --git a/plugins/samplesource/audioinput/audioinputgui.cpp b/plugins/samplesource/audioinput/audioinputgui.cpp index 5d186ed09..c0ae6373e 100644 --- a/plugins/samplesource/audioinput/audioinputgui.cpp +++ b/plugins/samplesource/audioinput/audioinputgui.cpp @@ -18,6 +18,7 @@ #include #include +#include #include "ui_audioinputgui.h" #include "gui/colormapper.h" @@ -43,6 +44,7 @@ AudioInputGui::AudioInputGui(DeviceUISet *deviceUISet, QWidget* parent) : m_sampleSource = (AudioInput*) m_deviceUISet->m_deviceAPI->getSampleSource(); ui->setupUi(getContents()); + setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); getContents()->setStyleSheet(QString(tr("#AudioInputGui { border: 1px solid %1 }") .arg(palette().highlight().color().darker(115).name()))); m_helpURL = "plugins/samplesource/audioinput/readme.md"; @@ -95,6 +97,12 @@ bool AudioInputGui::deserialize(const QByteArray& data) } } +void AudioInputGui::resizeEvent(QResizeEvent* size) +{ + adjustSize(); + size->accept(); +} + bool AudioInputGui::handleMessage(const Message& message) { if (AudioInput::MsgConfigureAudioInput::match(message)) diff --git a/plugins/samplesource/audioinput/audioinputgui.h b/plugins/samplesource/audioinput/audioinputgui.h index 9febcfbfd..a11d90cae 100644 --- a/plugins/samplesource/audioinput/audioinputgui.h +++ b/plugins/samplesource/audioinput/audioinputgui.h @@ -47,6 +47,9 @@ public: bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } +protected: + void resizeEvent(QResizeEvent* size); + private: Ui::AudioInputGui* ui; diff --git a/plugins/samplesource/audioinput/audioinputgui.ui b/plugins/samplesource/audioinput/audioinputgui.ui index 1d5ff0178..6be0b2e39 100644 --- a/plugins/samplesource/audioinput/audioinputgui.ui +++ b/plugins/samplesource/audioinput/audioinputgui.ui @@ -11,7 +11,7 @@
    - + 0 0 @@ -19,7 +19,13 @@ 360 - 0 + 191 + + + + + 360 + 191 @@ -29,7 +35,7 @@ - FunCubeDongle + Audio @@ -49,15 +55,18 @@ - - 4 - + + + 0 + 0 + + start/stop acquisition @@ -105,6 +114,31 @@
    + + + + + 0 + 0 + + + + + 5 + 32 + + + + + 5 + 32 + + + + + + + diff --git a/plugins/samplesource/bladerf1input/bladerf1inputgui.cpp b/plugins/samplesource/bladerf1input/bladerf1inputgui.cpp index 5c378ef94..9c4051878 100644 --- a/plugins/samplesource/bladerf1input/bladerf1inputgui.cpp +++ b/plugins/samplesource/bladerf1input/bladerf1inputgui.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include @@ -48,6 +49,7 @@ Bladerf1InputGui::Bladerf1InputGui(DeviceUISet *deviceUISet, QWidget* parent) : m_sampleSource = (Bladerf1Input*) m_deviceUISet->m_deviceAPI->getSampleSource(); ui->setupUi(getContents()); + setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); getContents()->setStyleSheet(QString(tr("#Bladerf1InputGui { border: 1px solid %1 }") .arg(palette().highlight().color().darker(115).name()))); m_helpURL = "plugins/samplesource/bladerf1input/readme.md"; @@ -114,6 +116,12 @@ bool Bladerf1InputGui::deserialize(const QByteArray& data) } } +void Bladerf1InputGui::resizeEvent(QResizeEvent* size) +{ + adjustSize(); + size->accept(); +} + bool Bladerf1InputGui::handleMessage(const Message& message) { if (Bladerf1Input::MsgConfigureBladerf1::match(message)) diff --git a/plugins/samplesource/bladerf1input/bladerf1inputgui.h b/plugins/samplesource/bladerf1input/bladerf1inputgui.h index 45e6b7294..9202e42f2 100644 --- a/plugins/samplesource/bladerf1input/bladerf1inputgui.h +++ b/plugins/samplesource/bladerf1input/bladerf1inputgui.h @@ -72,6 +72,9 @@ private: bool handleMessage(const Message& message); void makeUIConnections(); +protected: + void resizeEvent(QResizeEvent* size); + private slots: void handleInputMessages(); void on_centerFrequency_changed(quint64 value); diff --git a/plugins/samplesource/bladerf1input/bladerf1inputgui.ui b/plugins/samplesource/bladerf1input/bladerf1inputgui.ui index 1ab849e8a..4d9ffaf4f 100644 --- a/plugins/samplesource/bladerf1input/bladerf1inputgui.ui +++ b/plugins/samplesource/bladerf1input/bladerf1inputgui.ui @@ -7,11 +7,11 @@ 0 0 360 - 250 + 173 - + 0 0 @@ -19,7 +19,13 @@ 360 - 0 + 173 + + + + + 360 + 173 @@ -684,17 +690,17 @@ + + ButtonSwitch + QToolButton +
    gui/buttonswitch.h
    +
    ValueDial QWidget
    gui/valuedial.h
    1
    - - ButtonSwitch - QToolButton -
    gui/buttonswitch.h
    -
    diff --git a/plugins/samplesource/bladerf2input/bladerf2inputgui.cpp b/plugins/samplesource/bladerf2input/bladerf2inputgui.cpp index f1a2a3637..d96c48b2a 100644 --- a/plugins/samplesource/bladerf2input/bladerf2inputgui.cpp +++ b/plugins/samplesource/bladerf2input/bladerf2inputgui.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include @@ -51,6 +52,7 @@ BladeRF2InputGui::BladeRF2InputGui(DeviceUISet *deviceUISet, QWidget* parent) : uint64_t f_min, f_max; ui->setupUi(getContents()); + setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); getContents()->setStyleSheet(QString(tr("#Bladerf2InputGui { border: 1px solid %1 }") .arg(palette().highlight().color().darker(115).name()))); m_helpURL = "plugins/samplesource/bladerf2input/readme.md"; @@ -136,6 +138,12 @@ bool BladeRF2InputGui::deserialize(const QByteArray& data) } } +void BladeRF2InputGui::resizeEvent(QResizeEvent* size) +{ + adjustSize(); + size->accept(); +} + void BladeRF2InputGui::updateFrequencyLimits() { // values in kHz diff --git a/plugins/samplesource/bladerf2input/bladerf2inputgui.h b/plugins/samplesource/bladerf2input/bladerf2inputgui.h index 2647fccd6..45c842676 100644 --- a/plugins/samplesource/bladerf2input/bladerf2inputgui.h +++ b/plugins/samplesource/bladerf2input/bladerf2inputgui.h @@ -79,6 +79,9 @@ private: bool handleMessage(const Message& message); void makeUIConnections(); +protected: + void resizeEvent(QResizeEvent* size); + private slots: void handleInputMessages(); void on_centerFrequency_changed(quint64 value); diff --git a/plugins/samplesource/bladerf2input/bladerf2inputgui.ui b/plugins/samplesource/bladerf2input/bladerf2inputgui.ui index fd648fe31..fa03a988c 100644 --- a/plugins/samplesource/bladerf2input/bladerf2inputgui.ui +++ b/plugins/samplesource/bladerf2input/bladerf2inputgui.ui @@ -7,11 +7,11 @@ 0 0 360 - 209 + 172
    - + 0 0 @@ -19,7 +19,13 @@ 360 - 0 + 172 + + + + + 360 + 172 @@ -552,17 +558,17 @@ + + ButtonSwitch + QToolButton +
    gui/buttonswitch.h
    +
    ValueDial QWidget
    gui/valuedial.h
    1
    - - ButtonSwitch - QToolButton -
    gui/buttonswitch.h
    -
    TransverterButton QPushButton diff --git a/plugins/samplesource/fcdpro/fcdprogui.cpp b/plugins/samplesource/fcdpro/fcdprogui.cpp index ee7a60bd0..638f570da 100644 --- a/plugins/samplesource/fcdpro/fcdprogui.cpp +++ b/plugins/samplesource/fcdpro/fcdprogui.cpp @@ -17,6 +17,7 @@ #include #include +#include #include "ui_fcdprogui.h" #include "gui/colormapper.h" @@ -44,6 +45,7 @@ FCDProGui::FCDProGui(DeviceUISet *deviceUISet, QWidget* parent) : m_sampleSource = (FCDProInput*) m_deviceUISet->m_deviceAPI->getSampleSource(); ui->setupUi(getContents()); + setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); getContents()->setStyleSheet(QString(tr("#FCDProGui { border: 1px solid %1 }") .arg(palette().highlight().color().darker(115).name()))); m_helpURL = "plugins/samplesource/fcdpro/readme.md"; @@ -197,6 +199,12 @@ bool FCDProGui::deserialize(const QByteArray& data) } } +void FCDProGui::resizeEvent(QResizeEvent* size) +{ + adjustSize(); + size->accept(); +} + bool FCDProGui::handleMessage(const Message& message) { if (FCDProInput::MsgConfigureFCDPro::match(message)) diff --git a/plugins/samplesource/fcdpro/fcdprogui.h b/plugins/samplesource/fcdpro/fcdprogui.h index 4ddf0afa2..717d0a588 100644 --- a/plugins/samplesource/fcdpro/fcdprogui.h +++ b/plugins/samplesource/fcdpro/fcdprogui.h @@ -70,6 +70,9 @@ private: bool handleMessage(const Message& message); void makeUIConnections(); +protected: + void resizeEvent(QResizeEvent* size); + private slots: void handleInputMessages(); void on_centerFrequency_changed(quint64 value); diff --git a/plugins/samplesource/fcdpro/fcdprogui.ui b/plugins/samplesource/fcdpro/fcdprogui.ui index 3e75bb1d6..9f777151b 100644 --- a/plugins/samplesource/fcdpro/fcdprogui.ui +++ b/plugins/samplesource/fcdpro/fcdprogui.ui @@ -7,11 +7,11 @@ 0 0 360 - 443 + 394
    - + 0 0 @@ -19,7 +19,13 @@ 360 - 0 + 394 + + + + + 360 + 394 @@ -613,17 +619,17 @@ + + ButtonSwitch + QToolButton +
    gui/buttonswitch.h
    +
    ValueDial QWidget
    gui/valuedial.h
    1
    - - ButtonSwitch - QToolButton -
    gui/buttonswitch.h
    -
    TransverterButton QPushButton diff --git a/plugins/samplesource/fcdproplus/fcdproplusgui.cpp b/plugins/samplesource/fcdproplus/fcdproplusgui.cpp index 33503edfd..3630fd7f4 100644 --- a/plugins/samplesource/fcdproplus/fcdproplusgui.cpp +++ b/plugins/samplesource/fcdproplus/fcdproplusgui.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include "ui_fcdproplusgui.h" #include "gui/colormapper.h" @@ -45,6 +46,7 @@ FCDProPlusGui::FCDProPlusGui(DeviceUISet *deviceUISet, QWidget* parent) : m_sampleSource = (FCDProPlusInput*) m_deviceUISet->m_deviceAPI->getSampleSource(); ui->setupUi(getContents()); + setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); getContents()->setStyleSheet(QString(tr("#FCDProPlusGui { border: 1px solid %1 }") .arg(palette().highlight().color().darker(115).name()))); m_helpURL = "plugins/samplesource/fcdproplus/readme.md"; @@ -115,6 +117,12 @@ bool FCDProPlusGui::deserialize(const QByteArray& data) } } +void FCDProPlusGui::resizeEvent(QResizeEvent* size) +{ + adjustSize(); + size->accept(); +} + bool FCDProPlusGui::handleMessage(const Message& message) { if (FCDProPlusInput::MsgConfigureFCDProPlus::match(message)) diff --git a/plugins/samplesource/fcdproplus/fcdproplusgui.h b/plugins/samplesource/fcdproplus/fcdproplusgui.h index 8e4090fe7..317c0ab0b 100644 --- a/plugins/samplesource/fcdproplus/fcdproplusgui.h +++ b/plugins/samplesource/fcdproplus/fcdproplusgui.h @@ -69,6 +69,9 @@ private: bool handleMessage(const Message& message); void makeUIConnections(); +protected: + void resizeEvent(QResizeEvent* size); + private slots: void handleInputMessages(); void on_centerFrequency_changed(quint64 value); diff --git a/plugins/samplesource/fcdproplus/fcdproplusgui.ui b/plugins/samplesource/fcdproplus/fcdproplusgui.ui index 982210b30..191bca334 100644 --- a/plugins/samplesource/fcdproplus/fcdproplusgui.ui +++ b/plugins/samplesource/fcdproplus/fcdproplusgui.ui @@ -7,11 +7,11 @@ 0 0 360 - 219 + 162
    - + 0 0 @@ -19,7 +19,13 @@ 360 - 0 + 162 + + + + + 360 + 162 @@ -455,17 +461,17 @@ + + ButtonSwitch + QToolButton +
    gui/buttonswitch.h
    +
    ValueDial QWidget
    gui/valuedial.h
    1
    - - ButtonSwitch - QToolButton -
    gui/buttonswitch.h
    -
    TransverterButton QPushButton diff --git a/plugins/samplesource/fileinput/fileinputgui.cpp b/plugins/samplesource/fileinput/fileinputgui.cpp index 39bc4004b..13130ca3b 100644 --- a/plugins/samplesource/fileinput/fileinputgui.cpp +++ b/plugins/samplesource/fileinput/fileinputgui.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include "ui_fileinputgui.h" #include "plugin/pluginapi.h" @@ -56,6 +57,7 @@ FileInputGUI::FileInputGUI(DeviceUISet *deviceUISet, QWidget* parent) : { setAttribute(Qt::WA_DeleteOnClose, true); ui->setupUi(getContents()); + setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); getContents()->setStyleSheet(QString(tr("#FileInputGUI { border: 1px solid %1 }") .arg(palette().highlight().color().darker(115).name()))); m_helpURL = "plugins/samplesource/fileinput/readme.md"; @@ -117,6 +119,12 @@ bool FileInputGUI::deserialize(const QByteArray& data) } } +void FileInputGUI::resizeEvent(QResizeEvent* size) +{ + adjustSize(); + size->accept(); +} + void FileInputGUI::handleInputMessages() { Message* message; diff --git a/plugins/samplesource/fileinput/fileinputgui.h b/plugins/samplesource/fileinput/fileinputgui.h index b447b2cb6..43a7d49ac 100644 --- a/plugins/samplesource/fileinput/fileinputgui.h +++ b/plugins/samplesource/fileinput/fileinputgui.h @@ -47,6 +47,9 @@ public: virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } virtual bool handleMessage(const Message& message); +protected: + void resizeEvent(QResizeEvent* size); + private: Ui::FileInputGUI* ui; diff --git a/plugins/samplesource/fileinput/fileinputgui.ui b/plugins/samplesource/fileinput/fileinputgui.ui index 512b97571..6285afe43 100644 --- a/plugins/samplesource/fileinput/fileinputgui.ui +++ b/plugins/samplesource/fileinput/fileinputgui.ui @@ -11,7 +11,7 @@
    - + 0 0 @@ -19,7 +19,13 @@ 360 - 0 + 202 + + + + + 360 + 202 @@ -52,9 +58,6 @@ - - 4 - diff --git a/plugins/samplesource/hackrfinput/hackrfinputgui.cpp b/plugins/samplesource/hackrfinput/hackrfinputgui.cpp index 2c1658f28..6f6adcf02 100644 --- a/plugins/samplesource/hackrfinput/hackrfinputgui.cpp +++ b/plugins/samplesource/hackrfinput/hackrfinputgui.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include @@ -49,6 +50,7 @@ HackRFInputGui::HackRFInputGui(DeviceUISet *deviceUISet, QWidget* parent) : m_sampleSource = (HackRFInput*) m_deviceUISet->m_deviceAPI->getSampleSource(); ui->setupUi(getContents()); + setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); getContents()->setStyleSheet(QString(tr("#HackRFInputGui { border: 1px solid %1 }") .arg(palette().highlight().color().darker(115).name()))); m_helpURL = "plugins/samplesource/hackrfinput/readme.md"; @@ -114,6 +116,12 @@ bool HackRFInputGui::deserialize(const QByteArray& data) } } +void HackRFInputGui::resizeEvent(QResizeEvent* size) +{ + adjustSize(); + size->accept(); +} + bool HackRFInputGui::handleMessage(const Message& message) { if (HackRFInput::MsgConfigureHackRF::match(message)) diff --git a/plugins/samplesource/hackrfinput/hackrfinputgui.h b/plugins/samplesource/hackrfinput/hackrfinputgui.h index 2a90c045c..c9840a3b5 100644 --- a/plugins/samplesource/hackrfinput/hackrfinputgui.h +++ b/plugins/samplesource/hackrfinput/hackrfinputgui.h @@ -55,6 +55,9 @@ public: bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } +protected: + void resizeEvent(QResizeEvent* size); + private: Ui::HackRFInputGui* ui; diff --git a/plugins/samplesource/hackrfinput/hackrfinputgui.ui b/plugins/samplesource/hackrfinput/hackrfinputgui.ui index d28afc6ba..08a06ddef 100644 --- a/plugins/samplesource/hackrfinput/hackrfinputgui.ui +++ b/plugins/samplesource/hackrfinput/hackrfinputgui.ui @@ -7,11 +7,11 @@ 0 0 360 - 272 + 230 - + 0 0 @@ -19,7 +19,13 @@ 360 - 0 + 230 + + + + + 360 + 230 @@ -116,7 +122,7 @@ 32 - 50 + 0 @@ -661,17 +667,17 @@ + + ButtonSwitch + QToolButton +
    gui/buttonswitch.h
    +
    ValueDial QWidget
    gui/valuedial.h
    1
    - - ButtonSwitch - QToolButton -
    gui/buttonswitch.h
    -
    TransverterButton QPushButton diff --git a/plugins/samplesource/kiwisdr/kiwisdrgui.cpp b/plugins/samplesource/kiwisdr/kiwisdrgui.cpp index 2676ceea1..c6444f519 100644 --- a/plugins/samplesource/kiwisdr/kiwisdrgui.cpp +++ b/plugins/samplesource/kiwisdr/kiwisdrgui.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include "ui_kiwisdrgui.h" #include "plugin/pluginapi.h" @@ -67,6 +68,7 @@ KiwiSDRGui::KiwiSDRGui(DeviceUISet *deviceUISet, QWidget* parent) : m_statusColors.push_back("rgb(232, 85, 232)"); // Disconnected (magenta) ui->setupUi(getContents()); + setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); getContents()->setStyleSheet(QString(tr("#KiwiSDRGui { border: 1px solid %1 }") .arg(palette().highlight().color().darker(115).name()))); m_helpURL = "plugins/samplesource/kiwisdr/readme.md"; @@ -121,6 +123,12 @@ bool KiwiSDRGui::deserialize(const QByteArray& data) } } +void KiwiSDRGui::resizeEvent(QResizeEvent* size) +{ + adjustSize(); + size->accept(); +} + void KiwiSDRGui::on_startStop_toggled(bool checked) { if (m_doApplySettings) diff --git a/plugins/samplesource/kiwisdr/kiwisdrgui.h b/plugins/samplesource/kiwisdr/kiwisdrgui.h index 60b0936f2..90ebe736a 100644 --- a/plugins/samplesource/kiwisdr/kiwisdrgui.h +++ b/plugins/samplesource/kiwisdr/kiwisdrgui.h @@ -47,6 +47,9 @@ public: bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } +protected: + void resizeEvent(QResizeEvent* size); + private: Ui::KiwiSDRGui* ui; diff --git a/plugins/samplesource/kiwisdr/kiwisdrgui.ui b/plugins/samplesource/kiwisdr/kiwisdrgui.ui index 1a843a4d3..e1b799d65 100644 --- a/plugins/samplesource/kiwisdr/kiwisdrgui.ui +++ b/plugins/samplesource/kiwisdr/kiwisdrgui.ui @@ -7,11 +7,11 @@ 0 0 360 - 146 + 106
    - + 0 0 @@ -19,7 +19,13 @@ 360 - 0 + 106 + + + + + 360 + 106 @@ -52,9 +58,6 @@ - - 4 - @@ -314,17 +317,17 @@ + + ButtonSwitch + QToolButton +
    gui/buttonswitch.h
    +
    ValueDial QWidget
    gui/valuedial.h
    1
    - - ButtonSwitch - QToolButton -
    gui/buttonswitch.h
    -
    diff --git a/plugins/samplesource/limesdrinput/limesdrinputgui.cpp b/plugins/samplesource/limesdrinput/limesdrinputgui.cpp index 20e374c75..b515535c5 100644 --- a/plugins/samplesource/limesdrinput/limesdrinputgui.cpp +++ b/plugins/samplesource/limesdrinput/limesdrinputgui.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include @@ -49,6 +50,7 @@ LimeSDRInputGUI::LimeSDRInputGUI(DeviceUISet *deviceUISet, QWidget* parent) : m_limeSDRInput = (LimeSDRInput*) m_deviceUISet->m_deviceAPI->getSampleSource(); ui->setupUi(getContents()); + setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); getContents()->setStyleSheet(QString(tr("#LimeSDRInputGUI { border: 1px solid %1 }") .arg(palette().highlight().color().darker(115).name()))); m_helpURL = "plugins/samplesource/limesdrinput/readme.md"; @@ -142,6 +144,12 @@ bool LimeSDRInputGUI::deserialize(const QByteArray& data) } } +void LimeSDRInputGUI::resizeEvent(QResizeEvent* size) +{ + adjustSize(); + size->accept(); +} + bool LimeSDRInputGUI::handleMessage(const Message& message) { if (LimeSDRInput::MsgConfigureLimeSDR::match(message)) diff --git a/plugins/samplesource/limesdrinput/limesdrinputgui.h b/plugins/samplesource/limesdrinput/limesdrinputgui.h index 2da4a4a39..eac7ce41e 100644 --- a/plugins/samplesource/limesdrinput/limesdrinputgui.h +++ b/plugins/samplesource/limesdrinput/limesdrinputgui.h @@ -45,6 +45,9 @@ public: bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } +protected: + void resizeEvent(QResizeEvent* size); + private: Ui::LimeSDRInputGUI* ui; diff --git a/plugins/samplesource/limesdrinput/limesdrinputgui.ui b/plugins/samplesource/limesdrinput/limesdrinputgui.ui index b69695c8c..61d89ebaf 100644 --- a/plugins/samplesource/limesdrinput/limesdrinputgui.ui +++ b/plugins/samplesource/limesdrinput/limesdrinputgui.ui @@ -6,8 +6,8 @@ 0 0 - 360 - 266 + 370 + 209
    @@ -18,8 +18,14 @@ - 360 - 0 + 370 + 209 + + + + + 370 + 209 @@ -1157,6 +1163,11 @@ QToolTip{background-color: white; color: black;} + + ButtonSwitch + QToolButton +
    gui/buttonswitch.h
    +
    ValueDial QWidget @@ -1164,21 +1175,16 @@ QToolTip{background-color: white; color: black;} 1 - ButtonSwitch - QToolButton -
    gui/buttonswitch.h
    + ValueDialZ + QWidget +
    gui/valuedialz.h
    + 1
    TransverterButton QPushButton
    gui/transverterbutton.h
    - - ValueDialZ - QWidget -
    gui/valuedialz.h
    - 1 -
    ExternalClockButton QToolButton diff --git a/plugins/samplesource/localinput/localinputgui.cpp b/plugins/samplesource/localinput/localinputgui.cpp index d5a657dfa..cab4d3e22 100644 --- a/plugins/samplesource/localinput/localinputgui.cpp +++ b/plugins/samplesource/localinput/localinputgui.cpp @@ -29,6 +29,7 @@ #include #include #include +#include #include "ui_localinputgui.h" #include "gui/colormapper.h" @@ -75,6 +76,7 @@ LocalInputGui::LocalInputGui(DeviceUISet *deviceUISet, QWidget* parent) : m_startingTimeStampms = 0; ui->setupUi(getContents()); + setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); getContents()->setStyleSheet(QString(tr("#LocalInputGui { border: 1px solid %1 }") .arg(palette().highlight().color().darker(115).name()))); m_helpURL = "plugins/samplesource/localinput/readme.md"; @@ -143,6 +145,12 @@ bool LocalInputGui::deserialize(const QByteArray& data) } } +void LocalInputGui::resizeEvent(QResizeEvent* size) +{ + adjustSize(); + size->accept(); +} + bool LocalInputGui::handleMessage(const Message& message) { if (LocalInput::MsgConfigureLocalInput::match(message)) diff --git a/plugins/samplesource/localinput/localinputgui.h b/plugins/samplesource/localinput/localinputgui.h index 3822ee69a..dfa8a25bd 100644 --- a/plugins/samplesource/localinput/localinputgui.h +++ b/plugins/samplesource/localinput/localinputgui.h @@ -47,6 +47,9 @@ public: bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } +protected: + void resizeEvent(QResizeEvent* size); + private: Ui::LocalInputGui* ui; diff --git a/plugins/samplesource/localinput/localinputgui.ui b/plugins/samplesource/localinput/localinputgui.ui index d2d8398d4..ba4ffa686 100644 --- a/plugins/samplesource/localinput/localinputgui.ui +++ b/plugins/samplesource/localinput/localinputgui.ui @@ -7,13 +7,25 @@ 0 0 360 - 110 + 80
    + + + 0 + 0 + + 360 - 0 + 80 + + + + + 360 + 110 @@ -43,15 +55,24 @@ - - 4 - + + + 0 + 0 + + + + + 26 + 16777215 + + start/stop acquisition diff --git a/plugins/samplesource/perseus/perseusgui.cpp b/plugins/samplesource/perseus/perseusgui.cpp index 0b4a8c049..9176fc0a1 100644 --- a/plugins/samplesource/perseus/perseusgui.cpp +++ b/plugins/samplesource/perseus/perseusgui.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include "device/deviceapi.h" #include "device/deviceuiset.h" @@ -44,6 +45,7 @@ PerseusGui::PerseusGui(DeviceUISet *deviceUISet, QWidget* parent) : m_sampleSource = (PerseusInput*) m_deviceUISet->m_deviceAPI->getSampleSource(); ui->setupUi(getContents()); + setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); getContents()->setStyleSheet(QString(tr("#PerseusGui { border: 1px solid %1 }") .arg(palette().highlight().color().darker(115).name()))); m_helpURL = "plugins/samplesource/perseus/readme.md"; @@ -102,6 +104,13 @@ bool PerseusGui::deserialize(const QByteArray& data) } } +void PerseusGui::resizeEvent(QResizeEvent* size) +{ + adjustSize(); + size->accept(); +} + + bool PerseusGui::handleMessage(const Message& message) { // Returned messages to update the GUI (usually from web interface) diff --git a/plugins/samplesource/perseus/perseusgui.h b/plugins/samplesource/perseus/perseusgui.h index 26e51a937..dc3273258 100644 --- a/plugins/samplesource/perseus/perseusgui.h +++ b/plugins/samplesource/perseus/perseusgui.h @@ -48,6 +48,9 @@ public: uint32_t getDevSampleRate(unsigned int index); int getDevSampleRateIndex(uint32_t sampleRate); +protected: + void resizeEvent(QResizeEvent* size); + private: Ui::PerseusGui* ui; diff --git a/plugins/samplesource/perseus/perseusgui.ui b/plugins/samplesource/perseus/perseusgui.ui index 7630ced73..0b22c36a5 100644 --- a/plugins/samplesource/perseus/perseusgui.ui +++ b/plugins/samplesource/perseus/perseusgui.ui @@ -7,11 +7,11 @@ 0 0 360 - 182 + 136
    - + 0 0 @@ -19,7 +19,13 @@ 360 - 0 + 136 + + + + + 360 + 136 @@ -458,17 +464,17 @@ + + ButtonSwitch + QToolButton +
    gui/buttonswitch.h
    +
    ValueDial QWidget
    gui/valuedial.h
    1
    - - ButtonSwitch - QToolButton -
    gui/buttonswitch.h
    -
    TransverterButton QPushButton diff --git a/plugins/samplesource/plutosdrinput/plutosdrinputgui.cpp b/plugins/samplesource/plutosdrinput/plutosdrinputgui.cpp index 5ce1de780..9c7b757ce 100644 --- a/plugins/samplesource/plutosdrinput/plutosdrinputgui.cpp +++ b/plugins/samplesource/plutosdrinput/plutosdrinputgui.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include "dsp/dspengine.h" #include "dsp/dspcommands.h" @@ -49,6 +50,7 @@ PlutoSDRInputGui::PlutoSDRInputGui(DeviceUISet *deviceUISet, QWidget* parent) : m_sampleSource = (PlutoSDRInput*) m_deviceUISet->m_deviceAPI->getSampleSource(); ui->setupUi(getContents()); + setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); getContents()->setStyleSheet(QString(tr("#PlutoSDRInputGUI { border: 1px solid %1 }") .arg(palette().highlight().color().darker(115).name()))); m_helpURL = "plugins/samplesource/plutosdrinput/readme.md"; @@ -122,6 +124,12 @@ bool PlutoSDRInputGui::deserialize(const QByteArray& data) } } +void PlutoSDRInputGui::resizeEvent(QResizeEvent* size) +{ + adjustSize(); + size->accept(); +} + bool PlutoSDRInputGui::handleMessage(const Message& message) { if (PlutoSDRInput::MsgConfigurePlutoSDR::match(message)) diff --git a/plugins/samplesource/plutosdrinput/plutosdrinputgui.h b/plugins/samplesource/plutosdrinput/plutosdrinputgui.h index 2ed58ae5c..878b30db4 100644 --- a/plugins/samplesource/plutosdrinput/plutosdrinputgui.h +++ b/plugins/samplesource/plutosdrinput/plutosdrinputgui.h @@ -48,6 +48,9 @@ public: virtual bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } +protected: + void resizeEvent(QResizeEvent* size); + private: Ui::PlutoSDRInputGUI* ui; DeviceUISet* m_deviceUISet; diff --git a/plugins/samplesource/plutosdrinput/plutosdrinputgui.ui b/plugins/samplesource/plutosdrinput/plutosdrinputgui.ui index 7d8a8df4c..999f168af 100644 --- a/plugins/samplesource/plutosdrinput/plutosdrinputgui.ui +++ b/plugins/samplesource/plutosdrinput/plutosdrinputgui.ui @@ -7,11 +7,11 @@ 0 0 360 - 291 + 240
    - + 0 0 @@ -19,7 +19,13 @@ 360 - 0 + 240 + + + + + 360 + 240 @@ -1010,17 +1016,17 @@ + + ButtonSwitch + QToolButton +
    gui/buttonswitch.h
    +
    ValueDial QWidget
    gui/valuedial.h
    1
    - - ButtonSwitch - QToolButton -
    gui/buttonswitch.h
    -
    TransverterButton QPushButton diff --git a/plugins/samplesource/remoteinput/remoteinputgui.cpp b/plugins/samplesource/remoteinput/remoteinputgui.cpp index aa8187ece..40e0d6267 100644 --- a/plugins/samplesource/remoteinput/remoteinputgui.cpp +++ b/plugins/samplesource/remoteinput/remoteinputgui.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include "ui_remoteinputgui.h" #include "gui/colormapper.h" @@ -72,6 +73,7 @@ RemoteInputGui::RemoteInputGui(DeviceUISet *deviceUISet, QWidget* parent) : m_startingTimeStampms = 0; ui->setupUi(getContents()); + setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); getContents()->setStyleSheet(QString(tr("#RemoteInputGui { border: 1px solid %1 }") .arg(palette().highlight().color().darker(115).name()))); m_helpURL = "plugins/samplesource/remoteinput/readme.md"; @@ -148,6 +150,12 @@ bool RemoteInputGui::deserialize(const QByteArray& data) } } +void RemoteInputGui::resizeEvent(QResizeEvent* size) +{ + adjustSize(); + size->accept(); +} + bool RemoteInputGui::handleMessage(const Message& message) { if (RemoteInput::MsgConfigureRemoteInput::match(message)) diff --git a/plugins/samplesource/remoteinput/remoteinputgui.h b/plugins/samplesource/remoteinput/remoteinputgui.h index aaea76579..e9f8013c5 100644 --- a/plugins/samplesource/remoteinput/remoteinputgui.h +++ b/plugins/samplesource/remoteinput/remoteinputgui.h @@ -49,6 +49,9 @@ public: bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } +protected: + void resizeEvent(QResizeEvent* size); + private: Ui::RemoteInputGui* ui; diff --git a/plugins/samplesource/remoteinput/remoteinputgui.ui b/plugins/samplesource/remoteinput/remoteinputgui.ui index bc766560e..62c10e414 100644 --- a/plugins/samplesource/remoteinput/remoteinputgui.ui +++ b/plugins/samplesource/remoteinput/remoteinputgui.ui @@ -7,13 +7,25 @@ 0 0 360 - 400 + 380
    + + + 0 + 0 + + 360 - 0 + 380 + + + + + 360 + 400 @@ -757,13 +769,6 @@
    - - - - Qt::Horizontal - - - @@ -1040,6 +1045,13 @@ + + + + Qt::Horizontal + + + @@ -1065,17 +1077,17 @@ + + ButtonSwitch + QToolButton +
    gui/buttonswitch.h
    +
    ValueDial QWidget
    gui/valuedial.h
    1
    - - ButtonSwitch - QToolButton -
    gui/buttonswitch.h
    -
    diff --git a/plugins/samplesource/rtlsdr/rtlsdrgui.cpp b/plugins/samplesource/rtlsdr/rtlsdrgui.cpp index 35e9fc142..cd8e7b588 100644 --- a/plugins/samplesource/rtlsdr/rtlsdrgui.cpp +++ b/plugins/samplesource/rtlsdr/rtlsdrgui.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include "rtlsdrgui.h" @@ -46,7 +47,7 @@ RTLSDRGui::RTLSDRGui(DeviceUISet *deviceUISet, QWidget* parent) : m_sampleSource = (RTLSDRInput*) m_deviceUISet->m_deviceAPI->getSampleSource(); ui->setupUi(getContents()); - QString s(tr("")); + setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); getContents()->setStyleSheet(QString(tr("#RTLSDRGui { border: 1px solid %1 }") .arg(palette().highlight().color().darker(115).name()))); m_helpURL = "plugins/samplesource/rtlsdr/readme.md"; @@ -126,6 +127,12 @@ bool RTLSDRGui::deserialize(const QByteArray& data) } } +void RTLSDRGui::resizeEvent(QResizeEvent* size) +{ + adjustSize(); + size->accept(); +} + bool RTLSDRGui::handleMessage(const Message& message) { if (RTLSDRInput::MsgConfigureRTLSDR::match(message)) diff --git a/plugins/samplesource/rtlsdr/rtlsdrgui.h b/plugins/samplesource/rtlsdr/rtlsdrgui.h index ec1e22bd3..29cf1470c 100644 --- a/plugins/samplesource/rtlsdr/rtlsdrgui.h +++ b/plugins/samplesource/rtlsdr/rtlsdrgui.h @@ -47,6 +47,9 @@ public: bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } +protected: + void resizeEvent(QResizeEvent* size); + private: Ui::RTLSDRGui* ui; diff --git a/plugins/samplesource/rtlsdr/rtlsdrgui.ui b/plugins/samplesource/rtlsdr/rtlsdrgui.ui index df02e0cd5..74fce0106 100644 --- a/plugins/samplesource/rtlsdr/rtlsdrgui.ui +++ b/plugins/samplesource/rtlsdr/rtlsdrgui.ui @@ -7,11 +7,11 @@ 0 0 360 - 229 + 217 - + 0 0 @@ -19,7 +19,13 @@ 360 - 0 + 217 + + + + + 360 + 217 @@ -645,17 +651,17 @@ + + ButtonSwitch + QToolButton +
    gui/buttonswitch.h
    +
    ValueDial QWidget
    gui/valuedial.h
    1
    - - ButtonSwitch - QToolButton -
    gui/buttonswitch.h
    -
    TransverterButton QPushButton diff --git a/plugins/samplesource/sdrplay/sdrplaygui.cpp b/plugins/samplesource/sdrplay/sdrplaygui.cpp index 0ab1d750e..9a82aec84 100644 --- a/plugins/samplesource/sdrplay/sdrplaygui.cpp +++ b/plugins/samplesource/sdrplay/sdrplaygui.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include "sdrplaygui.h" @@ -43,6 +44,7 @@ SDRPlayGui::SDRPlayGui(DeviceUISet *deviceUISet, QWidget* parent) : m_sampleSource = (SDRPlayInput*) m_deviceUISet->m_deviceAPI->getSampleSource(); ui->setupUi(getContents()); + setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); getContents()->setStyleSheet(QString(tr("#SDRPlayGui { border: 1px solid %1 }") .arg(palette().highlight().color().darker(115).name()))); m_helpURL = "plugins/samplesource/sdrplay/readme.md"; @@ -124,6 +126,12 @@ bool SDRPlayGui::deserialize(const QByteArray& data) } } +void SDRPlayGui::resizeEvent(QResizeEvent* size) +{ + adjustSize(); + size->accept(); +} + bool SDRPlayGui::handleMessage(const Message& message) { if (SDRPlayInput::MsgConfigureSDRPlay::match(message)) diff --git a/plugins/samplesource/sdrplay/sdrplaygui.h b/plugins/samplesource/sdrplay/sdrplaygui.h index d9d720084..6e9196c22 100644 --- a/plugins/samplesource/sdrplay/sdrplaygui.h +++ b/plugins/samplesource/sdrplay/sdrplaygui.h @@ -47,6 +47,9 @@ public: virtual bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } +protected: + void resizeEvent(QResizeEvent* size); + private: Ui::SDRPlayGui* ui; diff --git a/plugins/samplesource/sdrplay/sdrplaygui.ui b/plugins/samplesource/sdrplay/sdrplaygui.ui index 7afef29c8..000007b44 100644 --- a/plugins/samplesource/sdrplay/sdrplaygui.ui +++ b/plugins/samplesource/sdrplay/sdrplaygui.ui @@ -7,11 +7,11 @@ 0 0 360 - 257 + 202
    - + 0 0 @@ -19,7 +19,13 @@ 360 - 0 + 202 + + + + + 360 + 202 @@ -642,27 +648,20 @@
    - - - - Qt::Horizontal - - - + + ButtonSwitch + QToolButton +
    gui/buttonswitch.h
    +
    ValueDial QWidget
    gui/valuedial.h
    1
    - - ButtonSwitch - QToolButton -
    gui/buttonswitch.h
    -
    diff --git a/plugins/samplesource/sdrplayv3/sdrplayv3gui.cpp b/plugins/samplesource/sdrplayv3/sdrplayv3gui.cpp index 53fd850da..691c8ce58 100644 --- a/plugins/samplesource/sdrplayv3/sdrplayv3gui.cpp +++ b/plugins/samplesource/sdrplayv3/sdrplayv3gui.cpp @@ -18,6 +18,7 @@ #include #include +#include #include "sdrplayv3gui.h" #include "sdrplayv3input.h" @@ -43,6 +44,7 @@ SDRPlayV3Gui::SDRPlayV3Gui(DeviceUISet *deviceUISet, QWidget* parent) : m_sdrPlayV3Input = (SDRPlayV3Input*) m_deviceUISet->m_deviceAPI->getSampleSource(); ui->setupUi(getContents()); + setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); getContents()->setStyleSheet(QString(tr("#SDRPlayV3Gui { border: 1px solid %1 }") .arg(palette().highlight().color().darker(115).name()))); m_helpURL = "plugins/samplesource/sdrplayv3/readme.md"; @@ -160,6 +162,12 @@ bool SDRPlayV3Gui::deserialize(const QByteArray& data) } } +void SDRPlayV3Gui::resizeEvent(QResizeEvent* size) +{ + adjustSize(); + size->accept(); +} + bool SDRPlayV3Gui::handleMessage(const Message& message) { if (SDRPlayV3Input::MsgConfigureSDRPlayV3::match(message)) diff --git a/plugins/samplesource/sdrplayv3/sdrplayv3gui.h b/plugins/samplesource/sdrplayv3/sdrplayv3gui.h index 39462b66d..59fe97011 100644 --- a/plugins/samplesource/sdrplayv3/sdrplayv3gui.h +++ b/plugins/samplesource/sdrplayv3/sdrplayv3gui.h @@ -47,6 +47,9 @@ public: virtual bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } +protected: + void resizeEvent(QResizeEvent* size); + private: Ui::SDRPlayV3Gui* ui; diff --git a/plugins/samplesource/sdrplayv3/sdrplayv3gui.ui b/plugins/samplesource/sdrplayv3/sdrplayv3gui.ui index fea992488..3f9470b80 100644 --- a/plugins/samplesource/sdrplayv3/sdrplayv3gui.ui +++ b/plugins/samplesource/sdrplayv3/sdrplayv3gui.ui @@ -6,20 +6,26 @@ 0 0 - 360 - 303 + 410 + 234 - + 0 0 - 360 - 0 + 410 + 234 + + + + + 410 + 234 @@ -186,13 +192,6 @@ - - - - LO ppm - - - @@ -212,10 +211,10 @@ - - - - Qt::Horizontal + + + + LO ppm @@ -768,17 +767,17 @@ + + ButtonSwitch + QToolButton +
    gui/buttonswitch.h
    +
    ValueDial QWidget
    gui/valuedial.h
    1
    - - ButtonSwitch - QToolButton -
    gui/buttonswitch.h
    -
    TransverterButton QPushButton diff --git a/plugins/samplesource/sigmffileinput/sigmffileinputgui.cpp b/plugins/samplesource/sigmffileinput/sigmffileinputgui.cpp index d2f88919a..69ad7658c 100644 --- a/plugins/samplesource/sigmffileinput/sigmffileinputgui.cpp +++ b/plugins/samplesource/sigmffileinput/sigmffileinputgui.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include "ui_sigmffileinputgui.h" #include "plugin/pluginapi.h" @@ -62,6 +63,7 @@ SigMFFileInputGUI::SigMFFileInputGUI(DeviceUISet *deviceUISet, QWidget* parent) { setAttribute(Qt::WA_DeleteOnClose, true); ui->setupUi(getContents()); + setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding); getContents()->setStyleSheet(QString(tr("#SigMFFileInputGUI { border: 1px solid %1 }") .arg(palette().highlight().color().darker(115).name()))); m_helpURL = "plugins/samplesource/sigmffileinput/readme.md"; @@ -129,6 +131,12 @@ bool SigMFFileInputGUI::deserialize(const QByteArray& data) } } +void SigMFFileInputGUI::resizeEvent(QResizeEvent* size) +{ + resize(436, height()); + size->accept(); +} + void SigMFFileInputGUI::handleInputMessages() { Message* message; diff --git a/plugins/samplesource/sigmffileinput/sigmffileinputgui.h b/plugins/samplesource/sigmffileinput/sigmffileinputgui.h index 381ad5ea9..30f3cf9a3 100644 --- a/plugins/samplesource/sigmffileinput/sigmffileinputgui.h +++ b/plugins/samplesource/sigmffileinput/sigmffileinputgui.h @@ -47,6 +47,9 @@ public: bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } +protected: + void resizeEvent(QResizeEvent* size); + private: Ui::SigMFFileInputGUI* ui; diff --git a/plugins/samplesource/sigmffileinput/sigmffileinputgui.ui b/plugins/samplesource/sigmffileinput/sigmffileinputgui.ui index 3cc1946bd..8ef55dd12 100644 --- a/plugins/samplesource/sigmffileinput/sigmffileinputgui.ui +++ b/plugins/samplesource/sigmffileinput/sigmffileinputgui.ui @@ -6,20 +6,26 @@ 0 0 - 360 - 391 + 436 + 436 - + 0 0 - 360 - 0 + 436 + 436 + + + + + 436 + 16777215 diff --git a/plugins/samplesource/soapysdrinput/soapysdrinputgui.cpp b/plugins/samplesource/soapysdrinput/soapysdrinputgui.cpp index fa97d6638..6e3337ed7 100644 --- a/plugins/samplesource/soapysdrinput/soapysdrinputgui.cpp +++ b/plugins/samplesource/soapysdrinput/soapysdrinputgui.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include "dsp/dspengine.h" #include "dsp/dspcommands.h" @@ -61,6 +62,7 @@ SoapySDRInputGui::SoapySDRInputGui(DeviceUISet *deviceUISet, QWidget* parent) : setAttribute(Qt::WA_DeleteOnClose, true); m_sampleSource = (SoapySDRInput*) m_deviceUISet->m_deviceAPI->getSampleSource(); ui->setupUi(getContents()); + setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding); getContents()->setStyleSheet(QString(tr("#SoapySDRInputGui { border: 1px solid %1 }") .arg(palette().highlight().color().darker(115).name()))); m_helpURL = "plugins/samplesource/soapysdrinput/readme.md"; @@ -447,6 +449,12 @@ bool SoapySDRInputGui::deserialize(const QByteArray& data) } } +void SoapySDRInputGui::resizeEvent(QResizeEvent* size) +{ + resize(360, height()); + size->accept(); +} + bool SoapySDRInputGui::handleMessage(const Message& message) { if (SoapySDRInput::MsgConfigureSoapySDRInput::match(message)) diff --git a/plugins/samplesource/soapysdrinput/soapysdrinputgui.h b/plugins/samplesource/soapysdrinput/soapysdrinputgui.h index 483f9e823..0f896a921 100644 --- a/plugins/samplesource/soapysdrinput/soapysdrinputgui.h +++ b/plugins/samplesource/soapysdrinput/soapysdrinputgui.h @@ -52,6 +52,9 @@ public: virtual bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } +protected: + void resizeEvent(QResizeEvent* size); + private: void createRangesControl( ItemSettingGUI **settingGUI, diff --git a/plugins/samplesource/soapysdrinput/soapysdrinputgui.ui b/plugins/samplesource/soapysdrinput/soapysdrinputgui.ui index 8e1720a2e..e5d81f55c 100644 --- a/plugins/samplesource/soapysdrinput/soapysdrinputgui.ui +++ b/plugins/samplesource/soapysdrinput/soapysdrinputgui.ui @@ -10,12 +10,24 @@ 238 + + + 0 + 0 + + 360 0 + + + 360 + 16777215 + + Liberation Sans @@ -363,6 +375,18 @@
    + + + 0 + 0 + + + + + 356 + 0 + + Qt::ScrollBarAsNeeded @@ -401,17 +425,17 @@ + + ButtonSwitch + QToolButton +
    gui/buttonswitch.h
    +
    ValueDial QWidget
    gui/valuedial.h
    1
    - - ButtonSwitch - QToolButton -
    gui/buttonswitch.h
    -
    TransverterButton QPushButton diff --git a/plugins/samplesource/testsource/testsourcegui.cpp b/plugins/samplesource/testsource/testsourcegui.cpp index e6236daf8..a97f72de4 100644 --- a/plugins/samplesource/testsource/testsourcegui.cpp +++ b/plugins/samplesource/testsource/testsourcegui.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include "ui_testsourcegui.h" #include "plugin/pluginapi.h" @@ -54,6 +55,7 @@ TestSourceGui::TestSourceGui(DeviceUISet *deviceUISet, QWidget* parent) : m_sampleSource = m_deviceUISet->m_deviceAPI->getSampleSource(); ui->setupUi(getContents()); + setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); getContents()->setStyleSheet(QString(tr("#TestSourceGui { border: 1px solid %1 }") .arg(palette().highlight().color().darker(115).name()))); m_helpURL = "plugins/samplesource/testsource/readme.md"; @@ -113,6 +115,12 @@ bool TestSourceGui::deserialize(const QByteArray& data) } } +void TestSourceGui::resizeEvent(QResizeEvent* size) +{ + adjustSize(); + size->accept(); +} + void TestSourceGui::on_startStop_toggled(bool checked) { if (m_doApplySettings) diff --git a/plugins/samplesource/testsource/testsourcegui.h b/plugins/samplesource/testsource/testsourcegui.h index c6243b0ff..01473a90b 100644 --- a/plugins/samplesource/testsource/testsourcegui.h +++ b/plugins/samplesource/testsource/testsourcegui.h @@ -46,6 +46,9 @@ public: bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } +protected: + void resizeEvent(QResizeEvent* size); + private: Ui::TestSourceGui* ui; diff --git a/plugins/samplesource/testsource/testsourcegui.ui b/plugins/samplesource/testsource/testsourcegui.ui index 889037318..bd492ff6a 100644 --- a/plugins/samplesource/testsource/testsourcegui.ui +++ b/plugins/samplesource/testsource/testsourcegui.ui @@ -6,20 +6,26 @@ 0 0 - 360 - 337 + 381 + 331 - + 0 0 - 360 - 0 + 381 + 331 + + + + + 381 + 331 @@ -272,6 +278,12 @@
    + + + 55 + 0 + + 50 @@ -695,17 +707,16 @@ - - - - Qt::Horizontal - - - + + + 0 + 22 + + Amp fine @@ -719,6 +730,12 @@ 0 + + + 0 + 22 + + Amp coarse @@ -817,6 +834,12 @@ + + + 0 + 22 + + Q bias @@ -838,6 +861,12 @@ + + + 0 + 22 + + I bias @@ -882,6 +911,12 @@ + + + 0 + 22 + + DC bias @@ -926,6 +961,12 @@ + + + 0 + 22 + + Phase @@ -979,17 +1020,17 @@ + + ButtonSwitch + QToolButton +
    gui/buttonswitch.h
    +
    ValueDial QWidget
    gui/valuedial.h
    1
    - - ButtonSwitch - QToolButton -
    gui/buttonswitch.h
    -
    ValueDialZ QWidget diff --git a/plugins/samplesource/usrpinput/usrpinputgui.cpp b/plugins/samplesource/usrpinput/usrpinputgui.cpp index d2edc86bd..77ee8fb0e 100644 --- a/plugins/samplesource/usrpinput/usrpinputgui.cpp +++ b/plugins/samplesource/usrpinput/usrpinputgui.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include @@ -50,6 +51,7 @@ USRPInputGUI::USRPInputGUI(DeviceUISet *deviceUISet, QWidget* parent) : m_usrpInput = (USRPInput*) m_deviceUISet->m_deviceAPI->getSampleSource(); ui->setupUi(getContents()); + setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); getContents()->setStyleSheet(QString(tr("#USRPInputGUI { border: 1px solid %1 }") .arg(palette().highlight().color().darker(115).name()))); m_helpURL = "plugins/samplesource/usrpinput/readme.md"; @@ -152,6 +154,12 @@ bool USRPInputGUI::deserialize(const QByteArray& data) } } +void USRPInputGUI::resizeEvent(QResizeEvent* size) +{ + adjustSize(); + size->accept(); +} + bool USRPInputGUI::handleMessage(const Message& message) { if (USRPInput::MsgConfigureUSRP::match(message)) diff --git a/plugins/samplesource/usrpinput/usrpinputgui.h b/plugins/samplesource/usrpinput/usrpinputgui.h index d9c61d808..9749ccd9c 100644 --- a/plugins/samplesource/usrpinput/usrpinputgui.h +++ b/plugins/samplesource/usrpinput/usrpinputgui.h @@ -51,6 +51,9 @@ public: bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } +protected: + void resizeEvent(QResizeEvent* size); + private: Ui::USRPInputGUI* ui; diff --git a/plugins/samplesource/usrpinput/usrpinputgui.ui b/plugins/samplesource/usrpinput/usrpinputgui.ui index d31bd2bdd..6916cc9eb 100644 --- a/plugins/samplesource/usrpinput/usrpinputgui.ui +++ b/plugins/samplesource/usrpinput/usrpinputgui.ui @@ -7,11 +7,11 @@ 0 0 360 - 221 + 174 - + 0 0 @@ -19,7 +19,13 @@ 360 - 0 + 174 + + + + + 360 + 174 @@ -774,6 +780,11 @@ + + ButtonSwitch + QToolButton +
    gui/buttonswitch.h
    +
    ValueDial QWidget @@ -781,21 +792,16 @@ 1 - ButtonSwitch - QToolButton -
    gui/buttonswitch.h
    + ValueDialZ + QWidget +
    gui/valuedialz.h
    + 1
    TransverterButton QPushButton
    gui/transverterbutton.h
    - - ValueDialZ - QWidget -
    gui/valuedialz.h
    - 1 -
    diff --git a/plugins/samplesource/xtrxinput/xtrxinputgui.cpp b/plugins/samplesource/xtrxinput/xtrxinputgui.cpp index 92409fad1..63f4096dd 100644 --- a/plugins/samplesource/xtrxinput/xtrxinputgui.cpp +++ b/plugins/samplesource/xtrxinput/xtrxinputgui.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include @@ -50,6 +51,7 @@ XTRXInputGUI::XTRXInputGUI(DeviceUISet *deviceUISet, QWidget* parent) : m_XTRXInput = (XTRXInput*) m_deviceUISet->m_deviceAPI->getSampleSource(); ui->setupUi(getContents()); + setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); getContents()->setStyleSheet(QString(tr("#XTRXInputGUI { border: 1px solid %1 }") .arg(palette().highlight().color().darker(115).name()))); m_helpURL = "plugins/samplesource/xtrxinput/readme.md"; @@ -122,6 +124,12 @@ bool XTRXInputGUI::deserialize(const QByteArray& data) } } +void XTRXInputGUI::resizeEvent(QResizeEvent* size) +{ + adjustSize(); + size->accept(); +} + bool XTRXInputGUI::handleMessage(const Message& message) { diff --git a/plugins/samplesource/xtrxinput/xtrxinputgui.h b/plugins/samplesource/xtrxinput/xtrxinputgui.h index 7f8221479..72862c5c6 100644 --- a/plugins/samplesource/xtrxinput/xtrxinputgui.h +++ b/plugins/samplesource/xtrxinput/xtrxinputgui.h @@ -46,6 +46,9 @@ public: bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } +protected: + void resizeEvent(QResizeEvent* size); + private: Ui::XTRXInputGUI* ui; diff --git a/plugins/samplesource/xtrxinput/xtrxinputgui.ui b/plugins/samplesource/xtrxinput/xtrxinputgui.ui index 55b4aa3c2..c26cd98a5 100644 --- a/plugins/samplesource/xtrxinput/xtrxinputgui.ui +++ b/plugins/samplesource/xtrxinput/xtrxinputgui.ui @@ -6,20 +6,26 @@ 0 0 - 360 - 264 + 370 + 207
    - + 0 0 - 360 - 0 + 370 + 207 + + + + + 370 + 207 @@ -1072,17 +1078,17 @@ QToolTip{background-color: white; color: black;} + + ButtonSwitch + QToolButton +
    gui/buttonswitch.h
    +
    ValueDial QWidget
    gui/valuedial.h
    1
    - - ButtonSwitch - QToolButton -
    gui/buttonswitch.h
    -
    ValueDialZ QWidget From f3f504c88d02b6a053da87f0eaff848230d30d52 Mon Sep 17 00:00:00 2001 From: f4exb Date: Thu, 21 Apr 2022 19:24:40 +0200 Subject: [PATCH 046/115] Massive UI revamping (v7): fixed widget size handling: sample sinks. Part of #1209 --- .../samplesink/audiooutput/audiooutputgui.cpp | 8 +++++ .../samplesink/audiooutput/audiooutputgui.h | 3 ++ .../samplesink/audiooutput/audiooutputgui.ui | 17 +++++------ .../bladerf1output/bladerf1outputgui.cpp | 8 +++++ .../bladerf1output/bladerf1outputgui.h | 3 ++ .../bladerf1output/bladerf1outputgui.ui | 20 ++++++++----- .../bladerf2output/bladerf2outputgui.cpp | 8 +++++ .../bladerf2output/bladerf2outputgui.h | 3 ++ .../bladerf2output/bladerf2outputgui.ui | 22 +++++++++----- .../samplesink/fileoutput/fileoutputgui.cpp | 8 +++++ plugins/samplesink/fileoutput/fileoutputgui.h | 3 ++ .../samplesink/fileoutput/fileoutputgui.ui | 20 ++++++++----- .../hackrfoutput/hackrfoutputgui.cpp | 8 +++++ .../samplesink/hackrfoutput/hackrfoutputgui.h | 3 ++ .../hackrfoutput/hackrfoutputgui.ui | 22 +++++++++----- .../limesdroutput/limesdroutputgui.cpp | 8 +++++ .../limesdroutput/limesdroutputgui.h | 3 ++ .../limesdroutput/limesdroutputgui.ui | 30 +++++++++++-------- .../samplesink/localoutput/localoutputgui.cpp | 8 +++++ .../samplesink/localoutput/localoutputgui.h | 3 ++ .../samplesink/localoutput/localoutputgui.ui | 16 ++++++++-- .../plutosdroutput/plutosdroutputgui.cpp | 8 +++++ .../plutosdroutput/plutosdroutputgui.h | 3 ++ .../plutosdroutput/plutosdroutputgui.ui | 22 +++++++++----- .../remoteoutput/remoteoutputgui.cpp | 8 +++++ .../samplesink/remoteoutput/remoteoutputgui.h | 3 ++ .../remoteoutput/remoteoutputgui.ui | 16 ++++++---- .../soapysdroutput/soapysdroutputgui.cpp | 7 +++++ .../soapysdroutput/soapysdroutputgui.h | 3 ++ .../soapysdroutput/soapysdroutputgui.ui | 30 +++++++++++++++---- plugins/samplesink/testsink/testsinkgui.cpp | 2 ++ plugins/samplesink/testsink/testsinkgui.h | 2 ++ plugins/samplesink/testsink/testsinkgui.ui | 4 +-- .../samplesink/usrpoutput/usrpoutputgui.cpp | 8 +++++ plugins/samplesink/usrpoutput/usrpoutputgui.h | 3 ++ .../samplesink/usrpoutput/usrpoutputgui.ui | 30 +++++++++++-------- .../samplesink/xtrxoutput/xtrxoutputgui.cpp | 8 +++++ plugins/samplesink/xtrxoutput/xtrxoutputgui.h | 3 ++ .../samplesink/xtrxoutput/xtrxoutputgui.ui | 22 +++++++++----- 39 files changed, 312 insertions(+), 94 deletions(-) diff --git a/plugins/samplesink/audiooutput/audiooutputgui.cpp b/plugins/samplesink/audiooutput/audiooutputgui.cpp index a9664a96b..8e680df1e 100644 --- a/plugins/samplesink/audiooutput/audiooutputgui.cpp +++ b/plugins/samplesink/audiooutput/audiooutputgui.cpp @@ -17,6 +17,7 @@ #include #include +#include #include "ui_audiooutputgui.h" #include "gui/colormapper.h" @@ -43,6 +44,7 @@ AudioOutputGui::AudioOutputGui(DeviceUISet *deviceUISet, QWidget* parent) : m_audioOutput = (AudioOutput*) m_deviceUISet->m_deviceAPI->getSampleSink(); ui->setupUi(getContents()); + setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); getContents()->setStyleSheet(QString(tr("#AudioOutputGui { border: 1px solid %1 }") .arg(palette().highlight().color().darker(115).name()))); m_helpURL = "plugins/samplesink/audiooutput/readme.md"; @@ -100,6 +102,12 @@ bool AudioOutputGui::deserialize(const QByteArray& data) } } +void AudioOutputGui::resizeEvent(QResizeEvent* size) +{ + adjustSize(); + size->accept(); +} + bool AudioOutputGui::handleMessage(const Message& message) { if (AudioOutput::MsgConfigureAudioOutput::match(message)) diff --git a/plugins/samplesink/audiooutput/audiooutputgui.h b/plugins/samplesink/audiooutput/audiooutputgui.h index 4a5b2d406..91d74861f 100644 --- a/plugins/samplesink/audiooutput/audiooutputgui.h +++ b/plugins/samplesink/audiooutput/audiooutputgui.h @@ -46,6 +46,9 @@ public: bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } +protected: + void resizeEvent(QResizeEvent* size); + private: Ui::AudioOutputGui* ui; diff --git a/plugins/samplesink/audiooutput/audiooutputgui.ui b/plugins/samplesink/audiooutput/audiooutputgui.ui index 9baec9db9..bdbe5c47a 100644 --- a/plugins/samplesink/audiooutput/audiooutputgui.ui +++ b/plugins/samplesink/audiooutput/audiooutputgui.ui @@ -7,7 +7,7 @@ 0 0 360 - 149 + 106
    @@ -19,7 +19,13 @@ 360 - 0 + 106 + + + + + 360 + 106 @@ -161,13 +167,6 @@
    - - - - Qt::Horizontal - - - diff --git a/plugins/samplesink/bladerf1output/bladerf1outputgui.cpp b/plugins/samplesink/bladerf1output/bladerf1outputgui.cpp index fa5a967e0..8d4218202 100644 --- a/plugins/samplesink/bladerf1output/bladerf1outputgui.cpp +++ b/plugins/samplesink/bladerf1output/bladerf1outputgui.cpp @@ -17,6 +17,7 @@ #include #include +#include #include @@ -46,6 +47,7 @@ Bladerf1OutputGui::Bladerf1OutputGui(DeviceUISet *deviceUISet, QWidget* parent) m_deviceSampleSink = (Bladerf1Output*) m_deviceUISet->m_deviceAPI->getSampleSink(); ui->setupUi(getContents()); + setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); getContents()->setStyleSheet(QString(tr("#Bladerf1OutputGui { border: 1px solid %1 }") .arg(palette().highlight().color().darker(115).name()))); m_helpURL = "/plugins/samplesink/bladerf1output/readme.md"; ui->centerFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold)); @@ -108,6 +110,12 @@ bool Bladerf1OutputGui::deserialize(const QByteArray& data) } } +void Bladerf1OutputGui::resizeEvent(QResizeEvent* size) +{ + adjustSize(); + size->accept(); +} + bool Bladerf1OutputGui::handleMessage(const Message& message) { if (Bladerf1Output::MsgConfigureBladerf1::match(message)) diff --git a/plugins/samplesink/bladerf1output/bladerf1outputgui.h b/plugins/samplesink/bladerf1output/bladerf1outputgui.h index 31ea820f6..4e0bbecc7 100644 --- a/plugins/samplesink/bladerf1output/bladerf1outputgui.h +++ b/plugins/samplesink/bladerf1output/bladerf1outputgui.h @@ -46,6 +46,9 @@ public: bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } +protected: + void resizeEvent(QResizeEvent* size); + private: Ui::Bladerf1OutputGui* ui; diff --git a/plugins/samplesink/bladerf1output/bladerf1outputgui.ui b/plugins/samplesink/bladerf1output/bladerf1outputgui.ui index a63f40f7b..3106a75e5 100644 --- a/plugins/samplesink/bladerf1output/bladerf1outputgui.ui +++ b/plugins/samplesink/bladerf1output/bladerf1outputgui.ui @@ -7,7 +7,7 @@ 0 0 360 - 214 + 159 @@ -19,7 +19,13 @@ 360 - 0 + 159 + + + + + 360 + 159 @@ -551,17 +557,17 @@ + + ButtonSwitch + QToolButton +
    gui/buttonswitch.h
    +
    ValueDial QWidget
    gui/valuedial.h
    1
    - - ButtonSwitch - QToolButton -
    gui/buttonswitch.h
    -
    diff --git a/plugins/samplesink/bladerf2output/bladerf2outputgui.cpp b/plugins/samplesink/bladerf2output/bladerf2outputgui.cpp index 7e82197d2..52585235e 100644 --- a/plugins/samplesink/bladerf2output/bladerf2outputgui.cpp +++ b/plugins/samplesink/bladerf2output/bladerf2outputgui.cpp @@ -17,6 +17,7 @@ #include #include +#include #include @@ -49,6 +50,7 @@ BladeRF2OutputGui::BladeRF2OutputGui(DeviceUISet *deviceUISet, QWidget* parent) uint64_t f_min, f_max; ui->setupUi(getContents()); + setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); getContents()->setStyleSheet(QString(tr("#BladeRF2OutputGui { border: 1px solid %1 }") .arg(palette().highlight().color().darker(115).name()))); m_helpURL = "plugins/samplesink/bladerf2output/readme.md"; ui->centerFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold)); @@ -123,6 +125,12 @@ bool BladeRF2OutputGui::deserialize(const QByteArray& data) } } +void BladeRF2OutputGui::resizeEvent(QResizeEvent* size) +{ + adjustSize(); + size->accept(); +} + void BladeRF2OutputGui::updateFrequencyLimits() { // values in kHz diff --git a/plugins/samplesink/bladerf2output/bladerf2outputgui.h b/plugins/samplesink/bladerf2output/bladerf2outputgui.h index bd447541e..4f6e6ce21 100644 --- a/plugins/samplesink/bladerf2output/bladerf2outputgui.h +++ b/plugins/samplesink/bladerf2output/bladerf2outputgui.h @@ -47,6 +47,9 @@ public: virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } virtual bool handleMessage(const Message& message); +protected: + void resizeEvent(QResizeEvent* size); + private: Ui::BladeRF2OutputGui* ui; diff --git a/plugins/samplesink/bladerf2output/bladerf2outputgui.ui b/plugins/samplesink/bladerf2output/bladerf2outputgui.ui index 3a5e17a28..0916b7243 100644 --- a/plugins/samplesink/bladerf2output/bladerf2outputgui.ui +++ b/plugins/samplesink/bladerf2output/bladerf2outputgui.ui @@ -7,11 +7,11 @@ 0 0 360 - 202 + 165 - + 0 0 @@ -19,7 +19,13 @@ 360 - 0 + 165 + + + + + 360 + 165 @@ -490,17 +496,17 @@ + + ButtonSwitch + QToolButton +
    gui/buttonswitch.h
    +
    ValueDial QWidget
    gui/valuedial.h
    1
    - - ButtonSwitch - QToolButton -
    gui/buttonswitch.h
    -
    TransverterButton QPushButton diff --git a/plugins/samplesink/fileoutput/fileoutputgui.cpp b/plugins/samplesink/fileoutput/fileoutputgui.cpp index dfdef4781..9a52e7a06 100644 --- a/plugins/samplesink/fileoutput/fileoutputgui.cpp +++ b/plugins/samplesink/fileoutput/fileoutputgui.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include "ui_fileoutputgui.h" #include "plugin/pluginapi.h" @@ -54,6 +55,7 @@ FileOutputGui::FileOutputGui(DeviceUISet *deviceUISet, QWidget* parent) : { setAttribute(Qt::WA_DeleteOnClose, true); ui->setupUi(getContents()); + setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); getContents()->setStyleSheet(QString(tr("#FileOutputGui { border: 1px solid %1 }") .arg(palette().highlight().color().darker(115).name()))); m_helpURL = "plugins/samplesink/fileoutput/readme.md"; @@ -115,6 +117,12 @@ bool FileOutputGui::deserialize(const QByteArray& data) } } +void FileOutputGui::resizeEvent(QResizeEvent* size) +{ + adjustSize(); + size->accept(); +} + bool FileOutputGui::handleMessage(const Message& message) { if (FileOutput::MsgConfigureFileOutput::match(message)) diff --git a/plugins/samplesink/fileoutput/fileoutputgui.h b/plugins/samplesink/fileoutput/fileoutputgui.h index 7d8906ebe..1ec881108 100644 --- a/plugins/samplesink/fileoutput/fileoutputgui.h +++ b/plugins/samplesink/fileoutput/fileoutputgui.h @@ -48,6 +48,9 @@ public: bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } +protected: + void resizeEvent(QResizeEvent* size); + private: Ui::FileOutputGui* ui; diff --git a/plugins/samplesink/fileoutput/fileoutputgui.ui b/plugins/samplesink/fileoutput/fileoutputgui.ui index a6c19111a..f089af530 100644 --- a/plugins/samplesink/fileoutput/fileoutputgui.ui +++ b/plugins/samplesink/fileoutput/fileoutputgui.ui @@ -7,7 +7,7 @@ 0 0 360 - 144 + 106
    @@ -19,7 +19,13 @@ 360 - 0 + 106 + + + + + 360 + 106 @@ -367,17 +373,17 @@ + + ButtonSwitch + QToolButton +
    gui/buttonswitch.h
    +
    ValueDial QWidget
    gui/valuedial.h
    1
    - - ButtonSwitch - QToolButton -
    gui/buttonswitch.h
    -
    diff --git a/plugins/samplesink/hackrfoutput/hackrfoutputgui.cpp b/plugins/samplesink/hackrfoutput/hackrfoutputgui.cpp index e48eba340..6700e804a 100644 --- a/plugins/samplesink/hackrfoutput/hackrfoutputgui.cpp +++ b/plugins/samplesink/hackrfoutput/hackrfoutputgui.cpp @@ -17,6 +17,7 @@ #include #include +#include #include @@ -48,6 +49,7 @@ HackRFOutputGui::HackRFOutputGui(DeviceUISet *deviceUISet, QWidget* parent) : m_deviceSampleSink = (HackRFOutput*) m_deviceUISet->m_deviceAPI->getSampleSink(); ui->setupUi(getContents()); + setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); getContents()->setStyleSheet(QString(tr("#HackRFOutputGui { border: 1px solid %1 }") .arg(palette().highlight().color().darker(115).name()))); m_helpURL = "plugins/samplesink/hackrfoutput/readme.md"; @@ -109,6 +111,12 @@ bool HackRFOutputGui::deserialize(const QByteArray& data) } } +void HackRFOutputGui::resizeEvent(QResizeEvent* size) +{ + adjustSize(); + size->accept(); +} + void HackRFOutputGui::blockApplySettings(bool block) { m_doApplySettings = !block; diff --git a/plugins/samplesink/hackrfoutput/hackrfoutputgui.h b/plugins/samplesink/hackrfoutput/hackrfoutputgui.h index 78d834806..b41fd3f24 100644 --- a/plugins/samplesink/hackrfoutput/hackrfoutputgui.h +++ b/plugins/samplesink/hackrfoutput/hackrfoutputgui.h @@ -56,6 +56,9 @@ public: bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } +protected: + void resizeEvent(QResizeEvent* size); + private: Ui::HackRFOutputGui* ui; diff --git a/plugins/samplesink/hackrfoutput/hackrfoutputgui.ui b/plugins/samplesink/hackrfoutput/hackrfoutputgui.ui index da73a6cf2..bb34250dc 100644 --- a/plugins/samplesink/hackrfoutput/hackrfoutputgui.ui +++ b/plugins/samplesink/hackrfoutput/hackrfoutputgui.ui @@ -7,11 +7,11 @@ 0 0 360 - 207 + 151
    - + 0 0 @@ -19,7 +19,13 @@ 360 - 0 + 151 + + + + + 360 + 151 @@ -540,17 +546,17 @@ + + ButtonSwitch + QToolButton +
    gui/buttonswitch.h
    +
    ValueDial QWidget
    gui/valuedial.h
    1
    - - ButtonSwitch - QToolButton -
    gui/buttonswitch.h
    -
    TransverterButton QPushButton diff --git a/plugins/samplesink/limesdroutput/limesdroutputgui.cpp b/plugins/samplesink/limesdroutput/limesdroutputgui.cpp index 158da7f39..9acb4ed60 100644 --- a/plugins/samplesink/limesdroutput/limesdroutputgui.cpp +++ b/plugins/samplesink/limesdroutput/limesdroutputgui.cpp @@ -17,6 +17,7 @@ #include #include +#include #include "ui_limesdroutputgui.h" #include "gui/colormapper.h" @@ -45,6 +46,7 @@ LimeSDROutputGUI::LimeSDROutputGUI(DeviceUISet *deviceUISet, QWidget* parent) : m_limeSDROutput = (LimeSDROutput*) m_deviceUISet->m_deviceAPI->getSampleSink(); ui->setupUi(getContents()); + setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); getContents()->setStyleSheet(QString(tr("#LimeSDROutputGUI { border: 1px solid %1 }") .arg(palette().highlight().color().darker(115).name()))); m_helpURL = "plugins/samplesink/limesdroutput/readme.md"; @@ -136,6 +138,12 @@ bool LimeSDROutputGUI::deserialize(const QByteArray& data) } } +void LimeSDROutputGUI::resizeEvent(QResizeEvent* size) +{ + adjustSize(); + size->accept(); +} + void LimeSDROutputGUI::updateFrequencyLimits() { // values in kHz diff --git a/plugins/samplesink/limesdroutput/limesdroutputgui.h b/plugins/samplesink/limesdroutput/limesdroutputgui.h index 125414032..c67e8e110 100644 --- a/plugins/samplesink/limesdroutput/limesdroutputgui.h +++ b/plugins/samplesink/limesdroutput/limesdroutputgui.h @@ -46,6 +46,9 @@ public: bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } +protected: + void resizeEvent(QResizeEvent* size); + private: Ui::LimeSDROutputGUI* ui; diff --git a/plugins/samplesink/limesdroutput/limesdroutputgui.ui b/plugins/samplesink/limesdroutput/limesdroutputgui.ui index faaf8f492..0ecc90907 100644 --- a/plugins/samplesink/limesdroutput/limesdroutputgui.ui +++ b/plugins/samplesink/limesdroutput/limesdroutputgui.ui @@ -7,11 +7,11 @@ 0 0 360 - 264 + 209
    - + 0 0 @@ -19,7 +19,13 @@ 360 - 0 + 209 + + + + + 360 + 209 @@ -928,6 +934,11 @@ QToolTip{background-color: white; color: black;} + + ButtonSwitch + QToolButton +
    gui/buttonswitch.h
    +
    ValueDial QWidget @@ -935,21 +946,16 @@ QToolTip{background-color: white; color: black;} 1 - ButtonSwitch - QToolButton -
    gui/buttonswitch.h
    + ValueDialZ + QWidget +
    gui/valuedialz.h
    + 1
    TransverterButton QPushButton
    gui/transverterbutton.h
    - - ValueDialZ - QWidget -
    gui/valuedialz.h
    - 1 -
    ExternalClockButton QToolButton diff --git a/plugins/samplesink/localoutput/localoutputgui.cpp b/plugins/samplesink/localoutput/localoutputgui.cpp index 22a1da49f..a1c376e97 100644 --- a/plugins/samplesink/localoutput/localoutputgui.cpp +++ b/plugins/samplesink/localoutput/localoutputgui.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include "ui_localoutputgui.h" #include "gui/colormapper.h" @@ -62,6 +63,7 @@ LocalOutputGui::LocalOutputGui(DeviceUISet *deviceUISet, QWidget* parent) : m_paletteWhiteText.setColor(QPalette::WindowText, Qt::white); ui->setupUi(getContents()); + setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); getContents()->setStyleSheet(QString(tr("#LocalOutputGui { border: 1px solid %1 }") .arg(palette().highlight().color().darker(115).name()))); m_helpURL = "plugins/samplesink/localoutput/readme.md"; @@ -130,6 +132,12 @@ bool LocalOutputGui::deserialize(const QByteArray& data) } } +void LocalOutputGui::resizeEvent(QResizeEvent* size) +{ + adjustSize(); + size->accept(); +} + bool LocalOutputGui::handleMessage(const Message& message) { if (LocalOutput::MsgConfigureLocalOutput::match(message)) diff --git a/plugins/samplesink/localoutput/localoutputgui.h b/plugins/samplesink/localoutput/localoutputgui.h index da888c787..d6ec64ede 100644 --- a/plugins/samplesink/localoutput/localoutputgui.h +++ b/plugins/samplesink/localoutput/localoutputgui.h @@ -47,6 +47,9 @@ public: bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } +protected: + void resizeEvent(QResizeEvent* size); + private: Ui::LocalOutputGui* ui; diff --git a/plugins/samplesink/localoutput/localoutputgui.ui b/plugins/samplesink/localoutput/localoutputgui.ui index 7e66d09c9..1037d177e 100644 --- a/plugins/samplesink/localoutput/localoutputgui.ui +++ b/plugins/samplesink/localoutput/localoutputgui.ui @@ -7,13 +7,25 @@ 0 0 360 - 68 + 47
    + + + 0 + 0 + + 360 - 0 + 47 + + + + + 360 + 47 diff --git a/plugins/samplesink/plutosdroutput/plutosdroutputgui.cpp b/plugins/samplesink/plutosdroutput/plutosdroutputgui.cpp index edb24a8d7..5140da0ff 100644 --- a/plugins/samplesink/plutosdroutput/plutosdroutputgui.cpp +++ b/plugins/samplesink/plutosdroutput/plutosdroutputgui.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include "dsp/dspengine.h" #include "dsp/dspcommands.h" @@ -48,6 +49,7 @@ PlutoSDROutputGUI::PlutoSDROutputGUI(DeviceUISet *deviceUISet, QWidget* parent) m_sampleSink = (PlutoSDROutput*) m_deviceUISet->m_deviceAPI->getSampleSink(); ui->setupUi(getContents()); + setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); getContents()->setStyleSheet(QString(tr("#PlutoSDROutputGUI { border: 1px solid %1 }") .arg(palette().highlight().color().darker(115).name()))); m_helpURL = "plugins/samplesink/plutosdroutput/readme.md"; @@ -120,6 +122,12 @@ bool PlutoSDROutputGUI::deserialize(const QByteArray& data) } } +void PlutoSDROutputGUI::resizeEvent(QResizeEvent* size) +{ + adjustSize(); + size->accept(); +} + bool PlutoSDROutputGUI::handleMessage(const Message& message) { (void) message; diff --git a/plugins/samplesink/plutosdroutput/plutosdroutputgui.h b/plugins/samplesink/plutosdroutput/plutosdroutputgui.h index 46bbd040d..50c74d13c 100644 --- a/plugins/samplesink/plutosdroutput/plutosdroutputgui.h +++ b/plugins/samplesink/plutosdroutput/plutosdroutputgui.h @@ -48,6 +48,9 @@ public: virtual bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } +protected: + void resizeEvent(QResizeEvent* size); + private: Ui::PlutoSDROutputGUI* ui; DeviceUISet* m_deviceUISet; diff --git a/plugins/samplesink/plutosdroutput/plutosdroutputgui.ui b/plugins/samplesink/plutosdroutput/plutosdroutputgui.ui index 2133d8be2..f60be2671 100644 --- a/plugins/samplesink/plutosdroutput/plutosdroutputgui.ui +++ b/plugins/samplesink/plutosdroutput/plutosdroutputgui.ui @@ -7,11 +7,11 @@ 0 0 360 - 250 + 197 - + 0 0 @@ -19,7 +19,13 @@ 360 - 0 + 197 + + + + + 360 + 197 @@ -769,17 +775,17 @@ + + ButtonSwitch + QToolButton +
    gui/buttonswitch.h
    +
    ValueDial QWidget
    gui/valuedial.h
    1
    - - ButtonSwitch - QToolButton -
    gui/buttonswitch.h
    -
    TransverterButton QPushButton diff --git a/plugins/samplesink/remoteoutput/remoteoutputgui.cpp b/plugins/samplesink/remoteoutput/remoteoutputgui.cpp index 6a384c80c..3fb588ca4 100644 --- a/plugins/samplesink/remoteoutput/remoteoutputgui.cpp +++ b/plugins/samplesink/remoteoutput/remoteoutputgui.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -67,6 +68,7 @@ RemoteOutputSinkGui::RemoteOutputSinkGui(DeviceUISet *deviceUISet, QWidget* pare m_paletteWhiteText.setColor(QPalette::WindowText, Qt::white); ui->setupUi(getContents()); + setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); getContents()->setStyleSheet(QString(tr("#RemoteOutputGui { border: 1px solid %1 }") .arg(palette().highlight().color().darker(115).name()))); m_helpURL = "plugins/samplesink/remoteoutput/readme.md"; @@ -141,6 +143,12 @@ bool RemoteOutputSinkGui::deserialize(const QByteArray& data) } } +void RemoteOutputSinkGui::resizeEvent(QResizeEvent* size) +{ + adjustSize(); + size->accept(); +} + bool RemoteOutputSinkGui::handleMessage(const Message& message) { if (RemoteOutput::MsgConfigureRemoteOutput::match(message)) diff --git a/plugins/samplesink/remoteoutput/remoteoutputgui.h b/plugins/samplesink/remoteoutput/remoteoutputgui.h index 3e7df619f..cbf0d522f 100644 --- a/plugins/samplesink/remoteoutput/remoteoutputgui.h +++ b/plugins/samplesink/remoteoutput/remoteoutputgui.h @@ -78,6 +78,9 @@ public: bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } +protected: + void resizeEvent(QResizeEvent* size); + private: Ui::RemoteOutputGui* ui; diff --git a/plugins/samplesink/remoteoutput/remoteoutputgui.ui b/plugins/samplesink/remoteoutput/remoteoutputgui.ui index b6b7a7a51..7e2b69535 100644 --- a/plugins/samplesink/remoteoutput/remoteoutputgui.ui +++ b/plugins/samplesink/remoteoutput/remoteoutputgui.ui @@ -9,20 +9,26 @@ 0 0 - 360 - 284 + 380 + 231
    - + 0 0 - 360 - 0 + 380 + 231 + + + + + 380 + 231 diff --git a/plugins/samplesink/soapysdroutput/soapysdroutputgui.cpp b/plugins/samplesink/soapysdroutput/soapysdroutputgui.cpp index d6525a0d6..427c2af74 100644 --- a/plugins/samplesink/soapysdroutput/soapysdroutputgui.cpp +++ b/plugins/samplesink/soapysdroutput/soapysdroutputgui.cpp @@ -17,6 +17,7 @@ #include #include +#include #include "dsp/dspengine.h" #include "dsp/dspcommands.h" @@ -59,6 +60,7 @@ SoapySDROutputGui::SoapySDROutputGui(DeviceUISet *deviceUISet, QWidget* parent) setAttribute(Qt::WA_DeleteOnClose, true); m_sampleSink = (SoapySDROutput*) m_deviceUISet->m_deviceAPI->getSampleSink(); ui->setupUi(getContents()); + setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding); getContents()->setStyleSheet(QString(tr("#SoapySDROutputGui { border: 1px solid %1 }") .arg(palette().highlight().color().darker(115).name()))); m_helpURL = "plugins/samplesink/soapysdroutput/readme.md"; @@ -443,6 +445,11 @@ bool SoapySDROutputGui::deserialize(const QByteArray& data) } } +void SoapySDROutputGui::resizeEvent(QResizeEvent* size) +{ + resize(360, height()); + size->accept(); +} bool SoapySDROutputGui::handleMessage(const Message& message) { diff --git a/plugins/samplesink/soapysdroutput/soapysdroutputgui.h b/plugins/samplesink/soapysdroutput/soapysdroutputgui.h index a17339dfe..1021006cd 100644 --- a/plugins/samplesink/soapysdroutput/soapysdroutputgui.h +++ b/plugins/samplesink/soapysdroutput/soapysdroutputgui.h @@ -54,6 +54,9 @@ public: virtual bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } +protected: + void resizeEvent(QResizeEvent* size); + private: void createRangesControl( ItemSettingGUI **settingGUI, diff --git a/plugins/samplesink/soapysdroutput/soapysdroutputgui.ui b/plugins/samplesink/soapysdroutput/soapysdroutputgui.ui index 615c6e736..6ea2b933b 100644 --- a/plugins/samplesink/soapysdroutput/soapysdroutputgui.ui +++ b/plugins/samplesink/soapysdroutput/soapysdroutputgui.ui @@ -10,10 +10,16 @@ 236 + + + 0 + 0 + + 360 - 0 + 236 @@ -301,6 +307,18 @@
    + + + 0 + 0 + + + + + 356 + 0 + + true @@ -336,17 +354,17 @@ + + ButtonSwitch + QToolButton +
    gui/buttonswitch.h
    +
    ValueDial QWidget
    gui/valuedial.h
    1
    - - ButtonSwitch - QToolButton -
    gui/buttonswitch.h
    -
    TransverterButton QPushButton diff --git a/plugins/samplesink/testsink/testsinkgui.cpp b/plugins/samplesink/testsink/testsinkgui.cpp index 08b6bcb59..85ab065a6 100644 --- a/plugins/samplesink/testsink/testsinkgui.cpp +++ b/plugins/samplesink/testsink/testsinkgui.cpp @@ -51,6 +51,8 @@ TestSinkGui::TestSinkGui(DeviceUISet *deviceUISet, QWidget* parent) : { setAttribute(Qt::WA_DeleteOnClose, true); ui->setupUi(getContents()); + setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + setMinimumSize(m_MinimumWidth, m_MinimumHeight); getContents()->setStyleSheet(QString(tr("#TestSinkGui { border: 1px solid %1 }") .arg(palette().highlight().color().darker(115).name()))); m_helpURL = "plugins/samplesink/testsink/readme.md"; diff --git a/plugins/samplesink/testsink/testsinkgui.h b/plugins/samplesink/testsink/testsinkgui.h index 6e861d24b..76d7e0a95 100644 --- a/plugins/samplesink/testsink/testsinkgui.h +++ b/plugins/samplesink/testsink/testsinkgui.h @@ -67,6 +67,8 @@ private: int m_lastEngineState; MessageQueue m_inputMessageQueue; SpectrumVis* m_spectrumVis; + static const int m_MinimumWidth = 360; + static const int m_MinimumHeight = 200 + 20 + 10 + 4*22 + 5; void blockApplySettings(bool block) { m_doApplySettings = !block; } void displaySettings(); diff --git a/plugins/samplesink/testsink/testsinkgui.ui b/plugins/samplesink/testsink/testsinkgui.ui index 267f0a318..4aff8ac27 100644 --- a/plugins/samplesink/testsink/testsinkgui.ui +++ b/plugins/samplesink/testsink/testsinkgui.ui @@ -11,7 +11,7 @@ - + 0 0 @@ -316,7 +316,7 @@ - + 0 0 diff --git a/plugins/samplesink/usrpoutput/usrpoutputgui.cpp b/plugins/samplesink/usrpoutput/usrpoutputgui.cpp index 3b8ac6f6b..aaa57aa14 100644 --- a/plugins/samplesink/usrpoutput/usrpoutputgui.cpp +++ b/plugins/samplesink/usrpoutput/usrpoutputgui.cpp @@ -18,6 +18,7 @@ #include #include +#include #include "ui_usrpoutputgui.h" #include "gui/colormapper.h" @@ -46,6 +47,7 @@ USRPOutputGUI::USRPOutputGUI(DeviceUISet *deviceUISet, QWidget* parent) : m_usrpOutput = (USRPOutput*) m_deviceUISet->m_deviceAPI->getSampleSink(); ui->setupUi(getContents()); + setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); getContents()->setStyleSheet(QString(tr("#USRPOutputGUI { border: 1px solid %1 }") .arg(palette().highlight().color().darker(115).name()))); m_helpURL = "plugins/samplesink/usrpoutput/readme.md"; @@ -151,6 +153,12 @@ bool USRPOutputGUI::deserialize(const QByteArray& data) } } +void USRPOutputGUI::resizeEvent(QResizeEvent* size) +{ + adjustSize(); + size->accept(); +} + void USRPOutputGUI::updateFrequencyLimits() { // values in kHz diff --git a/plugins/samplesink/usrpoutput/usrpoutputgui.h b/plugins/samplesink/usrpoutput/usrpoutputgui.h index ac6205488..481fd841f 100644 --- a/plugins/samplesink/usrpoutput/usrpoutputgui.h +++ b/plugins/samplesink/usrpoutput/usrpoutputgui.h @@ -53,6 +53,9 @@ public: virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } virtual bool handleMessage(const Message& message); +protected: + void resizeEvent(QResizeEvent* size); + private: Ui::USRPOutputGUI* ui; diff --git a/plugins/samplesink/usrpoutput/usrpoutputgui.ui b/plugins/samplesink/usrpoutput/usrpoutputgui.ui index 090f2b32a..a148de972 100644 --- a/plugins/samplesink/usrpoutput/usrpoutputgui.ui +++ b/plugins/samplesink/usrpoutput/usrpoutputgui.ui @@ -7,11 +7,11 @@ 0 0 360 - 214 + 163 - + 0 0 @@ -19,7 +19,13 @@ 360 - 0 + 163 + + + + + 360 + 163 @@ -708,6 +714,11 @@ + + ButtonSwitch + QToolButton +
    gui/buttonswitch.h
    +
    ValueDial QWidget @@ -715,21 +726,16 @@ 1 - ButtonSwitch - QToolButton -
    gui/buttonswitch.h
    + ValueDialZ + QWidget +
    gui/valuedialz.h
    + 1
    TransverterButton QPushButton
    gui/transverterbutton.h
    - - ValueDialZ - QWidget -
    gui/valuedialz.h
    - 1 -
    diff --git a/plugins/samplesink/xtrxoutput/xtrxoutputgui.cpp b/plugins/samplesink/xtrxoutput/xtrxoutputgui.cpp index b2bccc8a8..2a034d5ad 100644 --- a/plugins/samplesink/xtrxoutput/xtrxoutputgui.cpp +++ b/plugins/samplesink/xtrxoutput/xtrxoutputgui.cpp @@ -19,6 +19,7 @@ #include #include +#include #include "ui_xtrxoutputgui.h" #include "gui/colormapper.h" @@ -47,6 +48,7 @@ XTRXOutputGUI::XTRXOutputGUI(DeviceUISet *deviceUISet, QWidget* parent) : m_XTRXOutput = (XTRXOutput*) m_deviceUISet->m_deviceAPI->getSampleSink(); ui->setupUi(getContents()); + setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); getContents()->setStyleSheet(QString(tr("#XTRXOutputGUI { border: 1px solid %1 }") .arg(palette().highlight().color().darker(115).name()))); m_helpURL = "plugins/samplesink/xtrxoutput/readme.md"; @@ -119,6 +121,12 @@ bool XTRXOutputGUI::deserialize(const QByteArray& data) } } +void XTRXOutputGUI::resizeEvent(QResizeEvent* size) +{ + adjustSize(); + size->accept(); +} + bool XTRXOutputGUI::handleMessage(const Message& message) { diff --git a/plugins/samplesink/xtrxoutput/xtrxoutputgui.h b/plugins/samplesink/xtrxoutput/xtrxoutputgui.h index 1e724396c..73ff03d7b 100644 --- a/plugins/samplesink/xtrxoutput/xtrxoutputgui.h +++ b/plugins/samplesink/xtrxoutput/xtrxoutputgui.h @@ -45,6 +45,9 @@ public: bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } +protected: + void resizeEvent(QResizeEvent* size); + private: Ui::XTRXOutputGUI* ui; diff --git a/plugins/samplesink/xtrxoutput/xtrxoutputgui.ui b/plugins/samplesink/xtrxoutput/xtrxoutputgui.ui index 7e661afcf..52734149e 100644 --- a/plugins/samplesink/xtrxoutput/xtrxoutputgui.ui +++ b/plugins/samplesink/xtrxoutput/xtrxoutputgui.ui @@ -7,11 +7,11 @@ 0 0 360 - 264 + 207
    - + 0 0 @@ -19,7 +19,13 @@ 360 - 0 + 207 + + + + + 360 + 207 @@ -880,17 +886,17 @@ QToolTip{background-color: white; color: black;} + + ButtonSwitch + QToolButton +
    gui/buttonswitch.h
    +
    ValueDial QWidget
    gui/valuedial.h
    1
    - - ButtonSwitch - QToolButton -
    gui/buttonswitch.h
    -
    ValueDialZ QWidget From 5a265d7fe368c26b6a2d744fac837c48fce50ea2 Mon Sep 17 00:00:00 2001 From: f4exb Date: Thu, 21 Apr 2022 21:44:57 +0200 Subject: [PATCH 047/115] Massive UI revamping (v7): fixed widget size handling: sample MIMO. Part of #1209 --- .../bladerf2mimo/bladerf2mimogui.cpp | 8 +++++ .../samplemimo/bladerf2mimo/bladerf2mimogui.h | 3 ++ .../bladerf2mimo/bladerf2mimogui.ui | 26 +++++++++------ .../samplemimo/limesdrmimo/limesdrmimogui.cpp | 8 +++++ .../samplemimo/limesdrmimo/limesdrmimogui.h | 3 ++ .../samplemimo/limesdrmimo/limesdrmimogui.ui | 32 +++++++++++-------- plugins/samplemimo/metismiso/metismisogui.cpp | 8 +++++ plugins/samplemimo/metismiso/metismisogui.h | 3 ++ plugins/samplemimo/metismiso/metismisogui.ui | 20 ++++++++---- .../plutosdrmimo/plutosdrmimogui.cpp | 8 +++++ .../samplemimo/plutosdrmimo/plutosdrmimogui.h | 3 ++ .../plutosdrmimo/plutosdrmimogui.ui | 26 +++++++++------ plugins/samplemimo/testmi/testmigui.cpp | 8 +++++ plugins/samplemimo/testmi/testmigui.h | 3 ++ plugins/samplemimo/testmi/testmigui.ui | 26 +++++++++------ .../samplemimo/testmosync/testmosyncgui.cpp | 1 + .../samplemimo/testmosync/testmosyncgui.ui | 30 +++++++++++++---- plugins/samplemimo/xtrxmimo/xtrxmimogui.cpp | 8 +++++ plugins/samplemimo/xtrxmimo/xtrxmimogui.h | 3 ++ plugins/samplemimo/xtrxmimo/xtrxmimogui.ui | 26 +++++++++------ 20 files changed, 187 insertions(+), 66 deletions(-) diff --git a/plugins/samplemimo/bladerf2mimo/bladerf2mimogui.cpp b/plugins/samplemimo/bladerf2mimo/bladerf2mimogui.cpp index 6621d25ac..433ebb37a 100644 --- a/plugins/samplemimo/bladerf2mimo/bladerf2mimogui.cpp +++ b/plugins/samplemimo/bladerf2mimo/bladerf2mimogui.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include "plugin/pluginapi.h" #include "device/deviceapi.h" @@ -67,6 +68,7 @@ BladeRF2MIMOGui::BladeRF2MIMOGui(DeviceUISet *deviceUISet, QWidget* parent) : qDebug("BladeRF2MIMOGui::BladeRF2MIMOGui"); setAttribute(Qt::WA_DeleteOnClose, true); ui->setupUi(getContents()); + setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); getContents()->setStyleSheet(QString(tr("#BladeRF2MIMOGui { border: 1px solid %1 }") .arg(palette().highlight().color().darker(115).name()))); m_helpURL = "plugins/samplemimo/bladerf2mimo/readme.md"; @@ -143,6 +145,12 @@ bool BladeRF2MIMOGui::deserialize(const QByteArray& data) } } +void BladeRF2MIMOGui::resizeEvent(QResizeEvent* size) +{ + adjustSize(); + size->accept(); +} + void BladeRF2MIMOGui::displaySettings() { updateFrequencyLimits(); diff --git a/plugins/samplemimo/bladerf2mimo/bladerf2mimogui.h b/plugins/samplemimo/bladerf2mimo/bladerf2mimogui.h index b8ae967f6..66627d824 100644 --- a/plugins/samplemimo/bladerf2mimo/bladerf2mimogui.h +++ b/plugins/samplemimo/bladerf2mimo/bladerf2mimogui.h @@ -45,6 +45,9 @@ public: bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } +protected: + void resizeEvent(QResizeEvent* size); + private: Ui::BladeRF2MIMOGui* ui; diff --git a/plugins/samplemimo/bladerf2mimo/bladerf2mimogui.ui b/plugins/samplemimo/bladerf2mimo/bladerf2mimogui.ui index 12fc5c79a..b4ea73810 100644 --- a/plugins/samplemimo/bladerf2mimo/bladerf2mimogui.ui +++ b/plugins/samplemimo/bladerf2mimo/bladerf2mimogui.ui @@ -6,20 +6,26 @@ 0 0 - 360 - 220 + 370 + 208
    - + 0 0 - 360 - 0 + 370 + 208 + + + + + 370 + 208 @@ -764,17 +770,17 @@ + + ButtonSwitch + QToolButton +
    gui/buttonswitch.h
    +
    ValueDial QWidget
    gui/valuedial.h
    1
    - - ButtonSwitch - QToolButton -
    gui/buttonswitch.h
    -
    TransverterButton QPushButton diff --git a/plugins/samplemimo/limesdrmimo/limesdrmimogui.cpp b/plugins/samplemimo/limesdrmimo/limesdrmimogui.cpp index 0e94a7ec5..5c6e9227c 100644 --- a/plugins/samplemimo/limesdrmimo/limesdrmimogui.cpp +++ b/plugins/samplemimo/limesdrmimo/limesdrmimogui.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include "plugin/pluginapi.h" #include "device/deviceapi.h" @@ -69,6 +70,7 @@ LimeSDRMIMOGUI::LimeSDRMIMOGUI(DeviceUISet *deviceUISet, QWidget* parent) : qDebug("LimeSDRMIMOGUI::LimeSDRMIMOGUI"); setAttribute(Qt::WA_DeleteOnClose, true); ui->setupUi(getContents()); + setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); getContents()->setStyleSheet(QString(tr("#LimeSDRMIMOGUI { border: 1px solid %1 }") .arg(palette().highlight().color().darker(115).name()))); m_helpURL = "plugins/samplemimo/limesdrmimo/readme.md"; @@ -143,6 +145,12 @@ bool LimeSDRMIMOGUI::deserialize(const QByteArray& data) } } +void LimeSDRMIMOGUI::resizeEvent(QResizeEvent* size) +{ + adjustSize(); + size->accept(); +} + void LimeSDRMIMOGUI::handleInputMessages() { Message* message; diff --git a/plugins/samplemimo/limesdrmimo/limesdrmimogui.h b/plugins/samplemimo/limesdrmimo/limesdrmimogui.h index 2352938c7..42f0a7845 100644 --- a/plugins/samplemimo/limesdrmimo/limesdrmimogui.h +++ b/plugins/samplemimo/limesdrmimo/limesdrmimogui.h @@ -45,6 +45,9 @@ public: bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } +protected: + void resizeEvent(QResizeEvent* size); + private: Ui::LimeSDRMIMOGUI* ui; diff --git a/plugins/samplemimo/limesdrmimo/limesdrmimogui.ui b/plugins/samplemimo/limesdrmimo/limesdrmimogui.ui index f5e2166b0..141c06234 100644 --- a/plugins/samplemimo/limesdrmimo/limesdrmimogui.ui +++ b/plugins/samplemimo/limesdrmimo/limesdrmimogui.ui @@ -6,8 +6,8 @@ 0 0 - 360 - 286 + 370 + 244
    @@ -18,8 +18,14 @@ - 360 - 0 + 370 + 244 + + + + + 370 + 244 @@ -1323,6 +1329,11 @@ QToolTip{background-color: white; color: black;} + + ButtonSwitch + QToolButton +
    gui/buttonswitch.h
    +
    ValueDial QWidget @@ -1330,21 +1341,16 @@ QToolTip{background-color: white; color: black;} 1 - ButtonSwitch - QToolButton -
    gui/buttonswitch.h
    + ValueDialZ + QWidget +
    gui/valuedialz.h
    + 1
    TransverterButton QPushButton
    gui/transverterbutton.h
    - - ValueDialZ - QWidget -
    gui/valuedialz.h
    - 1 -
    ExternalClockButton QToolButton diff --git a/plugins/samplemimo/metismiso/metismisogui.cpp b/plugins/samplemimo/metismiso/metismisogui.cpp index b7d5ce714..e359fae7a 100644 --- a/plugins/samplemimo/metismiso/metismisogui.cpp +++ b/plugins/samplemimo/metismiso/metismisogui.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include "plugin/pluginapi.h" #include "device/deviceapi.h" @@ -57,6 +58,7 @@ MetisMISOGui::MetisMISOGui(DeviceUISet *deviceUISet, QWidget* parent) : m_txSampleRate = 48000; ui->setupUi(getContents()); + setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); getContents()->setStyleSheet(QString(tr("#MetisMISOGui { border: 1px solid %1 }") .arg(palette().highlight().color().darker(115).name()))); m_helpURL = "plugins/samplemimo/metismiso/readme.md"; @@ -106,6 +108,12 @@ void MetisMISOGui::setCenterFrequency(qint64 centerFrequency) sendSettings(); } +void MetisMISOGui::resizeEvent(QResizeEvent* size) +{ + adjustSize(); + size->accept(); +} + QByteArray MetisMISOGui::serialize() const { return m_settings.serialize(); diff --git a/plugins/samplemimo/metismiso/metismisogui.h b/plugins/samplemimo/metismiso/metismisogui.h index adaa68cd2..5a79f96f2 100644 --- a/plugins/samplemimo/metismiso/metismisogui.h +++ b/plugins/samplemimo/metismiso/metismisogui.h @@ -46,6 +46,9 @@ public: bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } +protected: + void resizeEvent(QResizeEvent* size); + private: Ui::MetisMISOGui* ui; diff --git a/plugins/samplemimo/metismiso/metismisogui.ui b/plugins/samplemimo/metismiso/metismisogui.ui index c04ddc532..dd5259f47 100644 --- a/plugins/samplemimo/metismiso/metismisogui.ui +++ b/plugins/samplemimo/metismiso/metismisogui.ui @@ -7,7 +7,7 @@ 0 0 360 - 234 + 200
    @@ -19,7 +19,13 @@ 360 - 0 + 200 + + + + + 360 + 200 @@ -810,17 +816,17 @@ + + ButtonSwitch + QToolButton +
    gui/buttonswitch.h
    +
    ValueDial QWidget
    gui/valuedial.h
    1
    - - ButtonSwitch - QToolButton -
    gui/buttonswitch.h
    -
    TransverterButton QPushButton diff --git a/plugins/samplemimo/plutosdrmimo/plutosdrmimogui.cpp b/plugins/samplemimo/plutosdrmimo/plutosdrmimogui.cpp index 497e18a6e..cd4e12f49 100644 --- a/plugins/samplemimo/plutosdrmimo/plutosdrmimogui.cpp +++ b/plugins/samplemimo/plutosdrmimo/plutosdrmimogui.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include "plugin/pluginapi.h" #include "device/deviceapi.h" @@ -69,6 +70,7 @@ PlutoSDRMIMOGUI::PlutoSDRMIMOGUI(DeviceUISet *deviceUISet, QWidget* parent) : qDebug("PlutoSDRMIMOGui::PlutoSDRMIMOGui"); setAttribute(Qt::WA_DeleteOnClose, true); ui->setupUi(getContents()); + setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); getContents()->setStyleSheet(QString(tr("#PlutoSDRMIMOGUI { border: 1px solid %1 }") .arg(palette().highlight().color().darker(115).name()))); m_helpURL = "plugins/samplemimo/plutosdrmimo/readme.md"; @@ -146,6 +148,12 @@ bool PlutoSDRMIMOGUI::deserialize(const QByteArray& data) } } +void PlutoSDRMIMOGUI::resizeEvent(QResizeEvent* size) +{ + adjustSize(); + size->accept(); +} + void PlutoSDRMIMOGUI::displaySettings() { if (m_rxElseTx) diff --git a/plugins/samplemimo/plutosdrmimo/plutosdrmimogui.h b/plugins/samplemimo/plutosdrmimo/plutosdrmimogui.h index 3984ee6cc..0c32f2c07 100644 --- a/plugins/samplemimo/plutosdrmimo/plutosdrmimogui.h +++ b/plugins/samplemimo/plutosdrmimo/plutosdrmimogui.h @@ -45,6 +45,9 @@ public: bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } +protected: + void resizeEvent(QResizeEvent* size); + private: Ui::PlutoSDRMIMOGUI* ui; diff --git a/plugins/samplemimo/plutosdrmimo/plutosdrmimogui.ui b/plugins/samplemimo/plutosdrmimo/plutosdrmimogui.ui index ff49f0cbc..2ecdf064d 100644 --- a/plugins/samplemimo/plutosdrmimo/plutosdrmimogui.ui +++ b/plugins/samplemimo/plutosdrmimo/plutosdrmimogui.ui @@ -6,20 +6,26 @@ 0 0 - 360 - 319 + 370 + 278
    - + 0 0 - 360 - 0 + 370 + 278 + + + + + 385 + 278 @@ -1260,17 +1266,17 @@ + + ButtonSwitch + QToolButton +
    gui/buttonswitch.h
    +
    ValueDial QWidget
    gui/valuedial.h
    1
    - - ButtonSwitch - QToolButton -
    gui/buttonswitch.h
    -
    TransverterButton QPushButton diff --git a/plugins/samplemimo/testmi/testmigui.cpp b/plugins/samplemimo/testmi/testmigui.cpp index 061305401..f5052d6b4 100644 --- a/plugins/samplemimo/testmi/testmigui.cpp +++ b/plugins/samplemimo/testmi/testmigui.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include "plugin/pluginapi.h" #include "device/deviceapi.h" @@ -62,6 +63,7 @@ TestMIGui::TestMIGui(DeviceUISet *deviceUISet, QWidget* parent) : m_deviceSampleRates.push_back(m_settings.m_streams[1].m_sampleRate / (1<setupUi(getContents()); + setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); getContents()->setStyleSheet(QString(tr("#TestMIGui { border: 1px solid %1 }") .arg(palette().highlight().color().darker(115).name()))); m_helpURL = "plugins/samplemimo/testmi/readme.md"; @@ -124,6 +126,12 @@ bool TestMIGui::deserialize(const QByteArray& data) } } +void TestMIGui::resizeEvent(QResizeEvent* size) +{ + adjustSize(); + size->accept(); +} + void TestMIGui::on_startStop_toggled(bool checked) { if (m_doApplySettings) diff --git a/plugins/samplemimo/testmi/testmigui.h b/plugins/samplemimo/testmi/testmigui.h index 410fa1099..0fff5a4f9 100644 --- a/plugins/samplemimo/testmi/testmigui.h +++ b/plugins/samplemimo/testmi/testmigui.h @@ -46,6 +46,9 @@ public: bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } +protected: + void resizeEvent(QResizeEvent* size); + private: Ui::TestMIGui* ui; diff --git a/plugins/samplemimo/testmi/testmigui.ui b/plugins/samplemimo/testmi/testmigui.ui index 0baf16c66..162172ed1 100644 --- a/plugins/samplemimo/testmi/testmigui.ui +++ b/plugins/samplemimo/testmi/testmigui.ui @@ -6,20 +6,26 @@ 0 0 - 360 - 368 + 370 + 297
    - + 0 0 - 360 - 0 + 370 + 297 + + + + + 370 + 297 @@ -1057,17 +1063,17 @@ + + ButtonSwitch + QToolButton +
    gui/buttonswitch.h
    +
    ValueDial QWidget
    gui/valuedial.h
    1
    - - ButtonSwitch - QToolButton -
    gui/buttonswitch.h
    -
    ValueDialZ QWidget diff --git a/plugins/samplemimo/testmosync/testmosyncgui.cpp b/plugins/samplemimo/testmosync/testmosyncgui.cpp index c4e3f45aa..92fbd050c 100644 --- a/plugins/samplemimo/testmosync/testmosyncgui.cpp +++ b/plugins/samplemimo/testmosync/testmosyncgui.cpp @@ -52,6 +52,7 @@ TestMOSyncGui::TestMOSyncGui(DeviceUISet *deviceUISet, QWidget* parent) : { setAttribute(Qt::WA_DeleteOnClose, true); ui->setupUi(getContents()); + setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); getContents()->setStyleSheet(QString(tr("#TestMOSyncGui { border: 1px solid %1 }") .arg(palette().highlight().color().darker(115).name()))); m_helpURL = "plugins/samplemimo/testmosync/readme.md"; diff --git a/plugins/samplemimo/testmosync/testmosyncgui.ui b/plugins/samplemimo/testmosync/testmosyncgui.ui index 8573a1ee6..a5b902822 100644 --- a/plugins/samplemimo/testmosync/testmosyncgui.ui +++ b/plugins/samplemimo/testmosync/testmosyncgui.ui @@ -11,7 +11,7 @@
    - + 0 0 @@ -147,6 +147,12 @@
    + + + 0 + 47 + + kHz @@ -263,6 +269,12 @@ + + + 0 + 29 + + SR @@ -326,6 +338,12 @@ + + + 0 + 0 + + 200 @@ -348,17 +366,17 @@ + + ButtonSwitch + QToolButton +
    gui/buttonswitch.h
    +
    ValueDial QWidget
    gui/valuedial.h
    1
    - - ButtonSwitch - QToolButton -
    gui/buttonswitch.h
    -
    GLSpectrum QWidget diff --git a/plugins/samplemimo/xtrxmimo/xtrxmimogui.cpp b/plugins/samplemimo/xtrxmimo/xtrxmimogui.cpp index dea49ea2d..1aaab21f3 100644 --- a/plugins/samplemimo/xtrxmimo/xtrxmimogui.cpp +++ b/plugins/samplemimo/xtrxmimo/xtrxmimogui.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include "plugin/pluginapi.h" #include "device/deviceapi.h" @@ -66,6 +67,7 @@ XTRXMIMOGUI::XTRXMIMOGUI(DeviceUISet *deviceUISet, QWidget* parent) : qDebug("XTRXMIMOGUI::XTRXMIMOGUI"); setAttribute(Qt::WA_DeleteOnClose, true); ui->setupUi(getContents()); + setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); getContents()->setStyleSheet(QString(tr("#XTRXMIMOGUI { border: 1px solid %1 }") .arg(palette().highlight().color().darker(115).name()))); m_helpURL = "plugins/samplemimo/xtrxmimo/readme.md"; @@ -140,6 +142,12 @@ bool XTRXMIMOGUI::deserialize(const QByteArray& data) } } +void XTRXMIMOGUI::resizeEvent(QResizeEvent* size) +{ + adjustSize(); + size->accept(); +} + void XTRXMIMOGUI::handleInputMessages() { Message* message; diff --git a/plugins/samplemimo/xtrxmimo/xtrxmimogui.h b/plugins/samplemimo/xtrxmimo/xtrxmimogui.h index a41eee57b..9b72ab2a9 100644 --- a/plugins/samplemimo/xtrxmimo/xtrxmimogui.h +++ b/plugins/samplemimo/xtrxmimo/xtrxmimogui.h @@ -45,6 +45,9 @@ public: bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } +protected: + void resizeEvent(QResizeEvent* size); + private: Ui::XTRXMIMOGUI* ui; diff --git a/plugins/samplemimo/xtrxmimo/xtrxmimogui.ui b/plugins/samplemimo/xtrxmimo/xtrxmimogui.ui index 6fcea580a..19ba68c81 100644 --- a/plugins/samplemimo/xtrxmimo/xtrxmimogui.ui +++ b/plugins/samplemimo/xtrxmimo/xtrxmimogui.ui @@ -6,20 +6,26 @@ 0 0 - 360 - 284 + 370 + 242 - + 0 0 - 360 - 0 + 370 + 242 + + + + + 370 + 242 @@ -1278,17 +1284,17 @@ QToolTip{background-color: white; color: black;} + + ButtonSwitch + QToolButton +
    gui/buttonswitch.h
    +
    ValueDial QWidget
    gui/valuedial.h
    1
    - - ButtonSwitch - QToolButton -
    gui/buttonswitch.h
    -
    ValueDialZ QWidget From 90386842cac905718d4f051ff1b464eea273de33 Mon Sep 17 00:00:00 2001 From: f4exb Date: Thu, 21 Apr 2022 23:31:01 +0200 Subject: [PATCH 048/115] Massive UI revamping (v7): fixed widget size handling: features. Part of #1209 --- plugins/feature/afc/afcgui.cpp | 8 +++++ plugins/feature/afc/afcgui.h | 3 ++ plugins/feature/afc/afcgui.ui | 16 ++++++--- .../feature/antennatools/antennatoolsgui.cpp | 9 +++++ .../feature/antennatools/antennatoolsgui.h | 3 ++ .../feature/antennatools/antennatoolsgui.ui | 22 ++++++++----- .../gs232controller/gs232controllergui.cpp | 8 +++++ .../gs232controller/gs232controllergui.h | 3 ++ .../gs232controller/gs232controllergui.ui | 2 +- .../jogdialcontrollergui.cpp | 8 +++++ .../jogdialcontroller/jogdialcontrollergui.h | 1 + .../jogdialcontroller/jogdialcontrollergui.ui | 33 +++++++------------ plugins/feature/pertester/pertestergui.cpp | 8 +++++ plugins/feature/pertester/pertestergui.h | 3 ++ plugins/feature/pertester/pertestergui.ui | 32 ++++++++++-------- .../feature/rigctlserver/rigctlservergui.cpp | 8 +++++ .../feature/rigctlserver/rigctlservergui.h | 3 ++ .../feature/rigctlserver/rigctlservergui.ui | 20 +++++------ plugins/feature/simpleptt/simplepttgui.cpp | 8 +++++ plugins/feature/simpleptt/simplepttgui.h | 3 ++ plugins/feature/simpleptt/simplepttgui.ui | 20 +++++------ 21 files changed, 152 insertions(+), 69 deletions(-) diff --git a/plugins/feature/afc/afcgui.cpp b/plugins/feature/afc/afcgui.cpp index a5924e9ea..c21fa1eba 100644 --- a/plugins/feature/afc/afcgui.cpp +++ b/plugins/feature/afc/afcgui.cpp @@ -16,6 +16,7 @@ /////////////////////////////////////////////////////////////////////////////////// #include +#include #include "feature/featureuiset.h" #include "device/deviceset.h" @@ -65,6 +66,12 @@ bool AFCGUI::deserialize(const QByteArray& data) } } +void AFCGUI::resizeEvent(QResizeEvent* size) +{ + adjustSize(); + size->accept(); +} + bool AFCGUI::handleMessage(const Message& message) { if (AFC::MsgConfigureAFC::match(message)) @@ -131,6 +138,7 @@ AFCGUI::AFCGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISet, Feature *featur m_lastFeatureState(0) { ui->setupUi(getRollupContents()); + setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); getRollupContents()->arrangeRollups(); m_helpURL = "plugins/feature/afc/readme.md"; setAttribute(Qt::WA_DeleteOnClose, true); diff --git a/plugins/feature/afc/afcgui.h b/plugins/feature/afc/afcgui.h index 83c73a537..2a93e25c2 100644 --- a/plugins/feature/afc/afcgui.h +++ b/plugins/feature/afc/afcgui.h @@ -48,6 +48,9 @@ public: virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; } virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; } +protected: + void resizeEvent(QResizeEvent* size); + private: Ui::AFCGUI* ui; PluginAPI* m_pluginAPI; diff --git a/plugins/feature/afc/afcgui.ui b/plugins/feature/afc/afcgui.ui index 373da46f5..de65c6172 100644 --- a/plugins/feature/afc/afcgui.ui +++ b/plugins/feature/afc/afcgui.ui @@ -6,19 +6,25 @@ 0 0 - 340 - 160 + 320 + 100
    - + 0 0 - 340 + 320 + 100 + + + + + 320 100 @@ -37,7 +43,7 @@ 0 0 340 - 151 + 111 diff --git a/plugins/feature/antennatools/antennatoolsgui.cpp b/plugins/feature/antennatools/antennatoolsgui.cpp index b1e55847f..53bb417f0 100644 --- a/plugins/feature/antennatools/antennatoolsgui.cpp +++ b/plugins/feature/antennatools/antennatoolsgui.cpp @@ -18,6 +18,8 @@ #include +#include + #include "feature/featureuiset.h" #include "gui/basicfeaturesettingsdialog.h" #include "channel/channelwebapiutils.h" @@ -68,6 +70,12 @@ bool AntennaToolsGUI::deserialize(const QByteArray& data) } } +void AntennaToolsGUI::resizeEvent(QResizeEvent* size) +{ + adjustSize(); + size->accept(); +} + bool AntennaToolsGUI::handleMessage(const Message& message) { if (AntennaTools::MsgConfigureAntennaTools::match(message)) @@ -115,6 +123,7 @@ AntennaToolsGUI::AntennaToolsGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISe m_deviceSets(0) { ui->setupUi(getRollupContents()); + setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); getRollupContents()->arrangeRollups(); m_helpURL = "plugins/feature/antennatools/readme.md"; diff --git a/plugins/feature/antennatools/antennatoolsgui.h b/plugins/feature/antennatools/antennatoolsgui.h index 95823f9a3..764e82c17 100644 --- a/plugins/feature/antennatools/antennatoolsgui.h +++ b/plugins/feature/antennatools/antennatoolsgui.h @@ -51,6 +51,9 @@ public: virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; } virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; } +protected: + void resizeEvent(QResizeEvent* size); + private: Ui::AntennaToolsGUI* ui; PluginAPI* m_pluginAPI; diff --git a/plugins/feature/antennatools/antennatoolsgui.ui b/plugins/feature/antennatools/antennatoolsgui.ui index 73cff9790..69f88452a 100644 --- a/plugins/feature/antennatools/antennatoolsgui.ui +++ b/plugins/feature/antennatools/antennatoolsgui.ui @@ -6,20 +6,26 @@ 0 0 - 394 - 523 + 374 + 584 - + 0 0 - 360 - 0 + 374 + 584 + + + + + 374 + 584 @@ -37,10 +43,10 @@ - 16 - 19 + 0 + 0 371 - 481 + 581 diff --git a/plugins/feature/gs232controller/gs232controllergui.cpp b/plugins/feature/gs232controller/gs232controllergui.cpp index e777ee052..9fdcc2e55 100644 --- a/plugins/feature/gs232controller/gs232controllergui.cpp +++ b/plugins/feature/gs232controller/gs232controllergui.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include "SWGTargetAzimuthElevation.h" @@ -70,6 +71,12 @@ bool GS232ControllerGUI::deserialize(const QByteArray& data) } } +void GS232ControllerGUI::resizeEvent(QResizeEvent* size) +{ + adjustSize(); + size->accept(); +} + bool GS232ControllerGUI::handleMessage(const Message& message) { if (GS232Controller::MsgConfigureGS232Controller::match(message)) @@ -141,6 +148,7 @@ GS232ControllerGUI::GS232ControllerGUI(PluginAPI* pluginAPI, FeatureUISet *featu m_lastOnTarget(false) { ui->setupUi(getRollupContents()); + setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); getRollupContents()->arrangeRollups(); m_helpURL = "plugins/feature/gs232controller/readme.md"; setAttribute(Qt::WA_DeleteOnClose, true); diff --git a/plugins/feature/gs232controller/gs232controllergui.h b/plugins/feature/gs232controller/gs232controllergui.h index 887d5d783..6761b87c6 100644 --- a/plugins/feature/gs232controller/gs232controllergui.h +++ b/plugins/feature/gs232controller/gs232controllergui.h @@ -50,6 +50,9 @@ public: virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; } virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; } +protected: + void resizeEvent(QResizeEvent* size); + private: Ui::GS232ControllerGUI* ui; PluginAPI* m_pluginAPI; diff --git a/plugins/feature/gs232controller/gs232controllergui.ui b/plugins/feature/gs232controller/gs232controllergui.ui index 3b7ff827b..e159e79ea 100644 --- a/plugins/feature/gs232controller/gs232controllergui.ui +++ b/plugins/feature/gs232controller/gs232controllergui.ui @@ -11,7 +11,7 @@ - + 0 0 diff --git a/plugins/feature/jogdialcontroller/jogdialcontrollergui.cpp b/plugins/feature/jogdialcontroller/jogdialcontrollergui.cpp index 3db3ca647..357ed4485 100644 --- a/plugins/feature/jogdialcontroller/jogdialcontrollergui.cpp +++ b/plugins/feature/jogdialcontroller/jogdialcontrollergui.cpp @@ -17,6 +17,7 @@ #include #include +#include #include "feature/featureuiset.h" #include "gui/basicfeaturesettingsdialog.h" @@ -66,6 +67,12 @@ bool JogdialControllerGUI::deserialize(const QByteArray& data) } } +void JogdialControllerGUI::resizeEvent(QResizeEvent* size) +{ + adjustSize(); + size->accept(); +} + bool JogdialControllerGUI::handleMessage(const Message& message) { if (JogdialController::MsgConfigureJogdialController::match(message)) @@ -146,6 +153,7 @@ JogdialControllerGUI::JogdialControllerGUI(PluginAPI* pluginAPI, FeatureUISet *f m_selectedChannel(nullptr) { ui->setupUi(getRollupContents()); + setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); getRollupContents()->arrangeRollups(); m_helpURL = "plugins/feature/jogdialcontroller/readme.md"; setAttribute(Qt::WA_DeleteOnClose, true); diff --git a/plugins/feature/jogdialcontroller/jogdialcontrollergui.h b/plugins/feature/jogdialcontroller/jogdialcontrollergui.h index 932b5592d..72f2bf90a 100644 --- a/plugins/feature/jogdialcontroller/jogdialcontrollergui.h +++ b/plugins/feature/jogdialcontroller/jogdialcontrollergui.h @@ -55,6 +55,7 @@ public: protected: void focusInEvent(QFocusEvent* e); void focusOutEvent(QFocusEvent *e); + void resizeEvent(QResizeEvent* size); private: Ui::JogdialControllerGUI* ui; diff --git a/plugins/feature/jogdialcontroller/jogdialcontrollergui.ui b/plugins/feature/jogdialcontroller/jogdialcontrollergui.ui index 142ba45fc..26ed65d0f 100644 --- a/plugins/feature/jogdialcontroller/jogdialcontrollergui.ui +++ b/plugins/feature/jogdialcontroller/jogdialcontrollergui.ui @@ -6,12 +6,12 @@ 0 0 - 365 - 105 + 340 + 50 - + 0 0 @@ -19,7 +19,13 @@ 340 - 100 + 50 + + + + + 340 + 50 @@ -37,7 +43,7 @@ 0 0 360 - 81 + 51 @@ -172,23 +178,6 @@
    - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - diff --git a/plugins/feature/pertester/pertestergui.cpp b/plugins/feature/pertester/pertestergui.cpp index 55e9be8d6..156ecd659 100644 --- a/plugins/feature/pertester/pertestergui.cpp +++ b/plugins/feature/pertester/pertestergui.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include "feature/featureuiset.h" #include "gui/basicfeaturesettingsdialog.h" @@ -68,6 +69,12 @@ bool PERTesterGUI::deserialize(const QByteArray& data) } } +void PERTesterGUI::resizeEvent(QResizeEvent* size) +{ + adjustSize(); + size->accept(); +} + bool PERTesterGUI::handleMessage(const Message& message) { if (PERTester::MsgConfigurePERTester::match(message)) @@ -127,6 +134,7 @@ PERTesterGUI::PERTesterGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISet, Fea m_lastFeatureState(0) { ui->setupUi(getRollupContents()); + setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); getRollupContents()->arrangeRollups(); m_helpURL = "plugins/feature/pertester/readme.md"; setAttribute(Qt::WA_DeleteOnClose, true); diff --git a/plugins/feature/pertester/pertestergui.h b/plugins/feature/pertester/pertestergui.h index 2306bf792..9a6ce6ecf 100644 --- a/plugins/feature/pertester/pertestergui.h +++ b/plugins/feature/pertester/pertestergui.h @@ -50,6 +50,9 @@ public: virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; } virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; } +protected: + void resizeEvent(QResizeEvent* size); + private: Ui::PERTesterGUI* ui; PluginAPI* m_pluginAPI; diff --git a/plugins/feature/pertester/pertestergui.ui b/plugins/feature/pertester/pertestergui.ui index adc0d8da9..aa295bd45 100644 --- a/plugins/feature/pertester/pertestergui.ui +++ b/plugins/feature/pertester/pertestergui.ui @@ -6,26 +6,26 @@ 0 0 - 350 - 430 + 335 + 472 - + 0 0 - 320 - 100 + 335 + 472 - 350 - 16777215 + 335 + 472 @@ -40,10 +40,10 @@ - 10 - 10 + 2 + 2 331 - 291 + 341 @@ -157,6 +157,12 @@ + + + 0 + 0 + + Packets @@ -456,10 +462,10 @@ Substitutions: - 10 - 310 + 2 + 352 331 - 91 + 117 diff --git a/plugins/feature/rigctlserver/rigctlservergui.cpp b/plugins/feature/rigctlserver/rigctlservergui.cpp index 352a36076..2894d6b0a 100644 --- a/plugins/feature/rigctlserver/rigctlservergui.cpp +++ b/plugins/feature/rigctlserver/rigctlservergui.cpp @@ -17,6 +17,7 @@ /////////////////////////////////////////////////////////////////////////////////// #include +#include #include "feature/featureuiset.h" #include "gui/basicfeaturesettingsdialog.h" @@ -68,6 +69,12 @@ bool RigCtlServerGUI::deserialize(const QByteArray& data) } } +void RigCtlServerGUI::resizeEvent(QResizeEvent* size) +{ + adjustSize(); + size->accept(); +} + bool RigCtlServerGUI::handleMessage(const Message& message) { if (RigCtlServer::MsgConfigureRigCtlServer::match(message)) @@ -127,6 +134,7 @@ RigCtlServerGUI::RigCtlServerGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISe m_lastFeatureState(0) { ui->setupUi(getRollupContents()); + setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); getRollupContents()->arrangeRollups(); m_helpURL = "plugins/feature/rigctlserver/readme.md"; setAttribute(Qt::WA_DeleteOnClose, true); diff --git a/plugins/feature/rigctlserver/rigctlservergui.h b/plugins/feature/rigctlserver/rigctlservergui.h index 51f429a09..bd7c990cb 100644 --- a/plugins/feature/rigctlserver/rigctlservergui.h +++ b/plugins/feature/rigctlserver/rigctlservergui.h @@ -50,6 +50,9 @@ public: virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; } virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; } +protected: + void resizeEvent(QResizeEvent* size); + private: Ui::RigCtlServerGUI* ui; PluginAPI* m_pluginAPI; diff --git a/plugins/feature/rigctlserver/rigctlservergui.ui b/plugins/feature/rigctlserver/rigctlservergui.ui index 6a5992d14..1d896fa27 100644 --- a/plugins/feature/rigctlserver/rigctlservergui.ui +++ b/plugins/feature/rigctlserver/rigctlservergui.ui @@ -6,26 +6,26 @@ 0 0 - 320 - 189 + 304 + 173 - + 0 0 - 320 - 100 + 304 + 173 - 320 - 16777215 + 304 + 173 @@ -40,9 +40,9 @@ - 10 - 10 - 301 + 2 + 2 + 300 171 diff --git a/plugins/feature/simpleptt/simplepttgui.cpp b/plugins/feature/simpleptt/simplepttgui.cpp index 163094290..20705415e 100644 --- a/plugins/feature/simpleptt/simplepttgui.cpp +++ b/plugins/feature/simpleptt/simplepttgui.cpp @@ -16,6 +16,7 @@ /////////////////////////////////////////////////////////////////////////////////// #include +#include #include "feature/featureuiset.h" #include "gui/basicfeaturesettingsdialog.h" @@ -69,6 +70,12 @@ bool SimplePTTGUI::deserialize(const QByteArray& data) } } +void SimplePTTGUI::resizeEvent(QResizeEvent* size) +{ + adjustSize(); + size->accept(); +} + bool SimplePTTGUI::handleMessage(const Message& message) { if (SimplePTT::MsgConfigureSimplePTT::match(message)) @@ -151,6 +158,7 @@ SimplePTTGUI::SimplePTTGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISet, Fea m_lastFeatureState(0) { ui->setupUi(getRollupContents()); + setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); getRollupContents()->arrangeRollups(); m_helpURL = "plugins/feature/simpleptt/readme.md"; setAttribute(Qt::WA_DeleteOnClose, true); diff --git a/plugins/feature/simpleptt/simplepttgui.h b/plugins/feature/simpleptt/simplepttgui.h index bee5244d8..c04046e4a 100644 --- a/plugins/feature/simpleptt/simplepttgui.h +++ b/plugins/feature/simpleptt/simplepttgui.h @@ -49,6 +49,9 @@ public: virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; } virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; } +protected: + void resizeEvent(QResizeEvent* size); + private: Ui::SimplePTTGUI* ui; PluginAPI* m_pluginAPI; diff --git a/plugins/feature/simpleptt/simplepttgui.ui b/plugins/feature/simpleptt/simplepttgui.ui index 0293fff1a..51e2fccd6 100644 --- a/plugins/feature/simpleptt/simplepttgui.ui +++ b/plugins/feature/simpleptt/simplepttgui.ui @@ -6,26 +6,26 @@ 0 0 - 320 - 181 + 304 + 155 - + 0 0 - 320 - 100 + 304 + 155 - 320 - 16777215 + 304 + 155 @@ -40,9 +40,9 @@ - 10 - 10 - 301 + 2 + 2 + 300 151 From 99fce0dce0f9018c548c80aa0fc672c89123df81 Mon Sep 17 00:00:00 2001 From: Jon Beniston Date: Fri, 22 Apr 2022 18:21:24 +0100 Subject: [PATCH 049/115] Add FramelessWindowResizer class for resizig frameless windows. Remove top-right resize grip from windows. In channels and features, make sure enterEvent and leaveEvent are passed to parent class. --- .../channelrx/chanalyzer/chanalyzergui.cpp | 6 +- plugins/channelrx/demodadsb/adsbdemodgui.cpp | 6 +- plugins/channelrx/demodais/aisdemodgui.cpp | 6 +- plugins/channelrx/demodam/amdemodgui.cpp | 6 +- plugins/channelrx/demodapt/aptdemodgui.cpp | 6 +- plugins/channelrx/demodatv/atvdemodgui.cpp | 6 +- plugins/channelrx/demodbfm/bfmdemodgui.cpp | 6 +- plugins/channelrx/demoddab/dabdemodgui.cpp | 6 +- plugins/channelrx/demoddatv/datvdemodgui.cpp | 6 +- plugins/channelrx/demoddsd/dsddemodgui.cpp | 6 +- .../channelrx/demodfreedv/freedvdemodgui.cpp | 6 +- plugins/channelrx/demodnfm/nfmdemodgui.cpp | 6 +- .../channelrx/demodpacket/packetdemodgui.cpp | 6 +- .../channelrx/demodpager/pagerdemodgui.cpp | 6 +- .../demodradiosonde/radiosondedemodgui.cpp | 6 +- plugins/channelrx/demodssb/ssbdemodgui.cpp | 6 +- plugins/channelrx/demodvor/vordemodgui.cpp | 6 +- .../channelrx/demodvorsc/vordemodscgui.cpp | 6 +- plugins/channelrx/demodwfm/wfmdemodgui.cpp | 6 +- plugins/channelrx/filesink/filesinkgui.cpp | 6 +- .../channelrx/freqtracker/freqtrackergui.cpp | 6 +- plugins/channelrx/localsink/localsinkgui.cpp | 6 +- .../channelrx/noisefigure/noisefiguregui.cpp | 6 +- .../radioastronomy/radioastronomygui.cpp | 6 +- .../channelrx/radioclock/radioclockgui.cpp | 6 +- .../channelrx/remotesink/remotesinkgui.cpp | 6 +- .../sigmffilesink/sigmffilesinkgui.cpp | 6 +- plugins/channelrx/udpsink/udpsinkgui.cpp | 6 +- .../channeltx/filesource/filesourcegui.cpp | 6 +- .../channeltx/localsource/localsourcegui.cpp | 6 +- .../mod802.15.4/ieee_802_15_4_modgui.cpp | 6 +- plugins/channeltx/modais/aismodgui.cpp | 6 +- plugins/channeltx/modam/ammodgui.cpp | 6 +- plugins/channeltx/modatv/atvmodgui.cpp | 6 +- .../modchirpchat/chirpchatmodgui.cpp | 6 +- plugins/channeltx/moddatv/datvmodgui.cpp | 6 +- plugins/channeltx/modfreedv/freedvmodgui.cpp | 6 +- plugins/channeltx/modnfm/nfmmodgui.cpp | 6 +- plugins/channeltx/modpacket/packetmodgui.cpp | 6 +- plugins/channeltx/modssb/ssbmodgui.cpp | 6 +- plugins/channeltx/modwfm/wfmmodgui.cpp | 6 +- .../remotesource/remotesourcegui.cpp | 6 +- plugins/channeltx/udpsource/udpsourcegui.cpp | 6 +- plugins/feature/afc/afcgui.cpp | 8 - plugins/feature/afc/afcgui.h | 3 - plugins/feature/ais/aisgui.cpp | 8 - plugins/feature/ais/aisgui.h | 3 - .../feature/antennatools/antennatoolsgui.cpp | 8 - .../feature/antennatools/antennatoolsgui.h | 3 - plugins/feature/aprs/aprsgui.cpp | 8 - plugins/feature/aprs/aprsgui.h | 3 - .../demodanalyzer/demodanalyzergui.cpp | 8 - .../feature/demodanalyzer/demodanalyzergui.h | 3 - .../gs232controller/gs232controllergui.cpp | 8 - .../gs232controller/gs232controllergui.h | 3 - .../jogdialcontrollergui.cpp | 8 - .../jogdialcontroller/jogdialcontrollergui.h | 3 - plugins/feature/map/mapgui.cpp | 8 - plugins/feature/map/mapgui.h | 3 - plugins/feature/pertester/pertestergui.cpp | 8 - plugins/feature/pertester/pertestergui.h | 3 - plugins/feature/radiosonde/radiosondegui.cpp | 8 - plugins/feature/radiosonde/radiosondegui.h | 3 - .../feature/rigctlserver/rigctlservergui.cpp | 8 - .../feature/rigctlserver/rigctlservergui.h | 3 - .../satellitetracker/satellitetrackergui.cpp | 8 - .../satellitetracker/satellitetrackergui.h | 3 - plugins/feature/simpleptt/simplepttgui.cpp | 8 - plugins/feature/simpleptt/simplepttgui.h | 3 - .../feature/startracker/startrackergui.cpp | 8 - plugins/feature/startracker/startrackergui.h | 3 - .../feature/vorlocalizer/vorlocalizergui.cpp | 8 - .../feature/vorlocalizer/vorlocalizergui.h | 3 - sdrgui/CMakeLists.txt | 2 + sdrgui/channel/channelgui.cpp | 30 ++- sdrgui/channel/channelgui.h | 11 +- sdrgui/device/devicegui.cpp | 30 ++- sdrgui/device/devicegui.h | 11 +- sdrgui/feature/featuregui.cpp | 30 ++- sdrgui/feature/featuregui.h | 11 +- sdrgui/gui/framelesswindowresizer.cpp | 236 ++++++++++++++++++ sdrgui/gui/framelesswindowresizer.h | 70 ++++++ sdrgui/mainspectrum/mainspectrumgui.cpp | 30 ++- sdrgui/mainspectrum/mainspectrumgui.h | 14 +- 84 files changed, 599 insertions(+), 299 deletions(-) create mode 100644 sdrgui/gui/framelesswindowresizer.cpp create mode 100644 sdrgui/gui/framelesswindowresizer.h diff --git a/plugins/channelrx/chanalyzer/chanalyzergui.cpp b/plugins/channelrx/chanalyzer/chanalyzergui.cpp index 4be9991f5..aa5289e1e 100644 --- a/plugins/channelrx/chanalyzer/chanalyzergui.cpp +++ b/plugins/channelrx/chanalyzer/chanalyzergui.cpp @@ -696,14 +696,16 @@ void ChannelAnalyzerGUI::applySettings(bool force) } } -void ChannelAnalyzerGUI::leaveEvent(QEvent*) +void ChannelAnalyzerGUI::leaveEvent(QEvent* event) { m_channelMarker.setHighlighted(false); + ChannelGUI::leaveEvent(event); } -void ChannelAnalyzerGUI::enterEvent(QEvent*) +void ChannelAnalyzerGUI::enterEvent(QEvent* event) { m_channelMarker.setHighlighted(true); + ChannelGUI::enterEvent(event); } void ChannelAnalyzerGUI::makeUIConnections() diff --git a/plugins/channelrx/demodadsb/adsbdemodgui.cpp b/plugins/channelrx/demodadsb/adsbdemodgui.cpp index e8dca5747..4005e67a6 100644 --- a/plugins/channelrx/demodadsb/adsbdemodgui.cpp +++ b/plugins/channelrx/demodadsb/adsbdemodgui.cpp @@ -4005,14 +4005,16 @@ void ADSBDemodGUI::displaySettings() blockApplySettings(false); } -void ADSBDemodGUI::leaveEvent(QEvent*) +void ADSBDemodGUI::leaveEvent(QEvent* event) { m_channelMarker.setHighlighted(false); + ChannelGUI::leaveEvent(event); } -void ADSBDemodGUI::enterEvent(QEvent*) +void ADSBDemodGUI::enterEvent(QEvent* event) { m_channelMarker.setHighlighted(true); + ChannelGUI::enterEvent(event); } void ADSBDemodGUI::blockApplySettings(bool block) diff --git a/plugins/channelrx/demodais/aisdemodgui.cpp b/plugins/channelrx/demodais/aisdemodgui.cpp index 2a70598f8..c1095e8f5 100644 --- a/plugins/channelrx/demodais/aisdemodgui.cpp +++ b/plugins/channelrx/demodais/aisdemodgui.cpp @@ -635,14 +635,16 @@ void AISDemodGUI::displaySettings() blockApplySettings(false); } -void AISDemodGUI::leaveEvent(QEvent*) +void AISDemodGUI::leaveEvent(QEvent* event) { m_channelMarker.setHighlighted(false); + ChannelGUI::leaveEvent(event); } -void AISDemodGUI::enterEvent(QEvent*) +void AISDemodGUI::enterEvent(QEvent* event) { m_channelMarker.setHighlighted(true); + ChannelGUI::enterEvent(event); } void AISDemodGUI::tick() diff --git a/plugins/channelrx/demodam/amdemodgui.cpp b/plugins/channelrx/demodam/amdemodgui.cpp index 35641d3e9..a50a2ef5c 100644 --- a/plugins/channelrx/demodam/amdemodgui.cpp +++ b/plugins/channelrx/demodam/amdemodgui.cpp @@ -396,14 +396,16 @@ void AMDemodGUI::displaySettings() blockApplySettings(false); } -void AMDemodGUI::leaveEvent(QEvent*) +void AMDemodGUI::leaveEvent(QEvent* event) { m_channelMarker.setHighlighted(false); + ChannelGUI::leaveEvent(event); } -void AMDemodGUI::enterEvent(QEvent*) +void AMDemodGUI::enterEvent(QEvent* event) { m_channelMarker.setHighlighted(true); + ChannelGUI::enterEvent(event); } void AMDemodGUI::audioSelect() diff --git a/plugins/channelrx/demodapt/aptdemodgui.cpp b/plugins/channelrx/demodapt/aptdemodgui.cpp index 19e0b08c7..f7889956a 100644 --- a/plugins/channelrx/demodapt/aptdemodgui.cpp +++ b/plugins/channelrx/demodapt/aptdemodgui.cpp @@ -814,14 +814,16 @@ void APTDemodGUI::displayPalettes() ui->channels->blockSignals(false); } -void APTDemodGUI::leaveEvent(QEvent*) +void APTDemodGUI::leaveEvent(QEvent* event) { m_channelMarker.setHighlighted(false); + ChannelGUI::leaveEvent(event); } -void APTDemodGUI::enterEvent(QEvent*) +void APTDemodGUI::enterEvent(QEvent* event) { m_channelMarker.setHighlighted(true); + ChannelGUI::enterEvent(event); } void APTDemodGUI::tick() diff --git a/plugins/channelrx/demodatv/atvdemodgui.cpp b/plugins/channelrx/demodatv/atvdemodgui.cpp index 21cda893d..bc0ca5eab 100644 --- a/plugins/channelrx/demodatv/atvdemodgui.cpp +++ b/plugins/channelrx/demodatv/atvdemodgui.cpp @@ -407,14 +407,16 @@ void ATVDemodGUI::setRFFiltersSlidersRange(int sampleRate) ui->rfOppBWText->setText(QString("%1k").arg((ui->rfOppBW->value() * m_rfSliderDivisor) / 1000.0, 0, 'f', 0)); } -void ATVDemodGUI::leaveEvent(QEvent*) +void ATVDemodGUI::leaveEvent(QEvent* event) { m_channelMarker.setHighlighted(false); + ChannelGUI::leaveEvent(event); } -void ATVDemodGUI::enterEvent(QEvent*) +void ATVDemodGUI::enterEvent(QEvent* event) { m_channelMarker.setHighlighted(true); + ChannelGUI::enterEvent(event); } void ATVDemodGUI::tick() diff --git a/plugins/channelrx/demodbfm/bfmdemodgui.cpp b/plugins/channelrx/demodbfm/bfmdemodgui.cpp index 02fc49f35..9917db345 100644 --- a/plugins/channelrx/demodbfm/bfmdemodgui.cpp +++ b/plugins/channelrx/demodbfm/bfmdemodgui.cpp @@ -500,14 +500,16 @@ void BFMDemodGUI::displaySettings() blockApplySettings(false); } -void BFMDemodGUI::leaveEvent(QEvent*) +void BFMDemodGUI::leaveEvent(QEvent* event) { m_channelMarker.setHighlighted(false); + ChannelGUI::leaveEvent(event); } -void BFMDemodGUI::enterEvent(QEvent*) +void BFMDemodGUI::enterEvent(QEvent* event) { m_channelMarker.setHighlighted(true); + ChannelGUI::enterEvent(event); } void BFMDemodGUI::audioSelect() diff --git a/plugins/channelrx/demoddab/dabdemodgui.cpp b/plugins/channelrx/demoddab/dabdemodgui.cpp index cee8f38a5..dd8e36f21 100644 --- a/plugins/channelrx/demoddab/dabdemodgui.cpp +++ b/plugins/channelrx/demoddab/dabdemodgui.cpp @@ -588,14 +588,16 @@ void DABDemodGUI::displaySettings() blockApplySettings(false); } -void DABDemodGUI::leaveEvent(QEvent*) +void DABDemodGUI::leaveEvent(QEvent* event) { m_channelMarker.setHighlighted(false); + ChannelGUI::leaveEvent(event); } -void DABDemodGUI::enterEvent(QEvent*) +void DABDemodGUI::enterEvent(QEvent* event) { m_channelMarker.setHighlighted(true); + ChannelGUI::enterEvent(event); } void DABDemodGUI::clearProgram() diff --git a/plugins/channelrx/demoddatv/datvdemodgui.cpp b/plugins/channelrx/demoddatv/datvdemodgui.cpp index c2ed28dae..eeb480588 100644 --- a/plugins/channelrx/demoddatv/datvdemodgui.cpp +++ b/plugins/channelrx/demoddatv/datvdemodgui.cpp @@ -479,18 +479,20 @@ void DATVDemodGUI::applySettings(bool force) } } -void DATVDemodGUI::leaveEvent(QEvent*) +void DATVDemodGUI::leaveEvent(QEvent* event) { blockApplySettings(true); m_channelMarker.setHighlighted(false); blockApplySettings(false); + ChannelGUI::leaveEvent(event); } -void DATVDemodGUI::enterEvent(QEvent*) +void DATVDemodGUI::enterEvent(QEvent* event) { blockApplySettings(true); m_channelMarker.setHighlighted(true); blockApplySettings(false); + ChannelGUI::enterEvent(event); } void DATVDemodGUI::audioSelect() diff --git a/plugins/channelrx/demoddsd/dsddemodgui.cpp b/plugins/channelrx/demoddsd/dsddemodgui.cpp index 201d95bd0..3b0ee3fae 100644 --- a/plugins/channelrx/demoddsd/dsddemodgui.cpp +++ b/plugins/channelrx/demoddsd/dsddemodgui.cpp @@ -497,14 +497,16 @@ void DSDDemodGUI::applySettings(bool force) } } -void DSDDemodGUI::leaveEvent(QEvent*) +void DSDDemodGUI::leaveEvent(QEvent* event) { m_channelMarker.setHighlighted(false); + ChannelGUI::leaveEvent(event); } -void DSDDemodGUI::enterEvent(QEvent*) +void DSDDemodGUI::enterEvent(QEvent* event) { m_channelMarker.setHighlighted(true); + ChannelGUI::enterEvent(event); } void DSDDemodGUI::blockApplySettings(bool block) diff --git a/plugins/channelrx/demodfreedv/freedvdemodgui.cpp b/plugins/channelrx/demodfreedv/freedvdemodgui.cpp index 0e46817b6..6ea0372a8 100644 --- a/plugins/channelrx/demodfreedv/freedvdemodgui.cpp +++ b/plugins/channelrx/demodfreedv/freedvdemodgui.cpp @@ -414,14 +414,16 @@ void FreeDVDemodGUI::displaySettings() blockApplySettings(false); } -void FreeDVDemodGUI::leaveEvent(QEvent*) +void FreeDVDemodGUI::leaveEvent(QEvent* event) { m_channelMarker.setHighlighted(false); + ChannelGUI::leaveEvent(event); } -void FreeDVDemodGUI::enterEvent(QEvent*) +void FreeDVDemodGUI::enterEvent(QEvent* event) { m_channelMarker.setHighlighted(true); + ChannelGUI::enterEvent(event); } void FreeDVDemodGUI::audioSelect() diff --git a/plugins/channelrx/demodnfm/nfmdemodgui.cpp b/plugins/channelrx/demodnfm/nfmdemodgui.cpp index a6e670963..ebf9628d3 100644 --- a/plugins/channelrx/demodnfm/nfmdemodgui.cpp +++ b/plugins/channelrx/demodnfm/nfmdemodgui.cpp @@ -532,14 +532,16 @@ void NFMDemodGUI::displaySettings() blockApplySettings(false); } -void NFMDemodGUI::leaveEvent(QEvent*) +void NFMDemodGUI::leaveEvent(QEvent* event) { m_channelMarker.setHighlighted(false); + ChannelGUI::leaveEvent(event); } -void NFMDemodGUI::enterEvent(QEvent*) +void NFMDemodGUI::enterEvent(QEvent* event) { m_channelMarker.setHighlighted(true); + ChannelGUI::enterEvent(event); } void NFMDemodGUI::setCtcssFreq(Real ctcssFreq) diff --git a/plugins/channelrx/demodpacket/packetdemodgui.cpp b/plugins/channelrx/demodpacket/packetdemodgui.cpp index afae8ef80..28bbf01db 100644 --- a/plugins/channelrx/demodpacket/packetdemodgui.cpp +++ b/plugins/channelrx/demodpacket/packetdemodgui.cpp @@ -569,14 +569,16 @@ void PacketDemodGUI::displaySettings() blockApplySettings(false); } -void PacketDemodGUI::leaveEvent(QEvent*) +void PacketDemodGUI::leaveEvent(QEvent* event) { m_channelMarker.setHighlighted(false); + ChannelGUI::leaveEvent(event); } -void PacketDemodGUI::enterEvent(QEvent*) +void PacketDemodGUI::enterEvent(QEvent* event) { m_channelMarker.setHighlighted(true); + ChannelGUI::enterEvent(event); } void PacketDemodGUI::tick() diff --git a/plugins/channelrx/demodpager/pagerdemodgui.cpp b/plugins/channelrx/demodpager/pagerdemodgui.cpp index 9989ba94b..bbab5548b 100644 --- a/plugins/channelrx/demodpager/pagerdemodgui.cpp +++ b/plugins/channelrx/demodpager/pagerdemodgui.cpp @@ -668,14 +668,16 @@ void PagerDemodGUI::displaySettings() blockApplySettings(false); } -void PagerDemodGUI::leaveEvent(QEvent*) +void PagerDemodGUI::leaveEvent(QEvent* event) { m_channelMarker.setHighlighted(false); + ChannelGUI::leaveEvent(event); } -void PagerDemodGUI::enterEvent(QEvent*) +void PagerDemodGUI::enterEvent(QEvent* event) { m_channelMarker.setHighlighted(true); + ChannelGUI::enterEvent(event); } void PagerDemodGUI::tick() diff --git a/plugins/channelrx/demodradiosonde/radiosondedemodgui.cpp b/plugins/channelrx/demodradiosonde/radiosondedemodgui.cpp index 1a76963fb..d45f552a4 100644 --- a/plugins/channelrx/demodradiosonde/radiosondedemodgui.cpp +++ b/plugins/channelrx/demodradiosonde/radiosondedemodgui.cpp @@ -772,14 +772,16 @@ void RadiosondeDemodGUI::displaySettings() blockApplySettings(false); } -void RadiosondeDemodGUI::leaveEvent(QEvent*) +void RadiosondeDemodGUI::leaveEvent(QEvent* event) { m_channelMarker.setHighlighted(false); + ChannelGUI::leaveEvent(event); } -void RadiosondeDemodGUI::enterEvent(QEvent*) +void RadiosondeDemodGUI::enterEvent(QEvent* event) { m_channelMarker.setHighlighted(true); + ChannelGUI::enterEvent(event); } void RadiosondeDemodGUI::tick() diff --git a/plugins/channelrx/demodssb/ssbdemodgui.cpp b/plugins/channelrx/demodssb/ssbdemodgui.cpp index 05eba5be5..1f4e02e73 100644 --- a/plugins/channelrx/demodssb/ssbdemodgui.cpp +++ b/plugins/channelrx/demodssb/ssbdemodgui.cpp @@ -622,14 +622,16 @@ void SSBDemodGUI::displayAGCThresholdGate(int value) ui->agcThresholdGate->setValue(dialValue); } -void SSBDemodGUI::leaveEvent(QEvent*) +void SSBDemodGUI::leaveEvent(QEvent* event) { m_channelMarker.setHighlighted(false); + ChannelGUI::leaveEvent(event); } -void SSBDemodGUI::enterEvent(QEvent*) +void SSBDemodGUI::enterEvent(QEvent* event) { m_channelMarker.setHighlighted(true); + ChannelGUI::enterEvent(event); } void SSBDemodGUI::audioSelect() diff --git a/plugins/channelrx/demodvor/vordemodgui.cpp b/plugins/channelrx/demodvor/vordemodgui.cpp index 5c96af2d6..18df17879 100644 --- a/plugins/channelrx/demodvor/vordemodgui.cpp +++ b/plugins/channelrx/demodvor/vordemodgui.cpp @@ -1345,14 +1345,16 @@ void VORDemodGUI::displaySettings() blockApplySettings(false); } -void VORDemodGUI::leaveEvent(QEvent*) +void VORDemodGUI::leaveEvent(QEvent* event) { m_channelMarker.setHighlighted(false); + ChannelGUI::leaveEvent(event); } -void VORDemodGUI::enterEvent(QEvent*) +void VORDemodGUI::enterEvent(QEvent* event) { m_channelMarker.setHighlighted(true); + ChannelGUI::enterEvent(event); } void VORDemodGUI::audioSelect() diff --git a/plugins/channelrx/demodvorsc/vordemodscgui.cpp b/plugins/channelrx/demodvorsc/vordemodscgui.cpp index d0e7c0e0a..2cfe7740e 100644 --- a/plugins/channelrx/demodvorsc/vordemodscgui.cpp +++ b/plugins/channelrx/demodvorsc/vordemodscgui.cpp @@ -388,14 +388,16 @@ void VORDemodSCGUI::displaySettings() blockApplySettings(false); } -void VORDemodSCGUI::leaveEvent(QEvent*) +void VORDemodSCGUI::leaveEvent(QEvent* event) { m_channelMarker.setHighlighted(false); + ChannelGUI::leaveEvent(event); } -void VORDemodSCGUI::enterEvent(QEvent*) +void VORDemodSCGUI::enterEvent(QEvent* event) { m_channelMarker.setHighlighted(true); + ChannelGUI::enterEvent(event); } void VORDemodSCGUI::audioSelect() diff --git a/plugins/channelrx/demodwfm/wfmdemodgui.cpp b/plugins/channelrx/demodwfm/wfmdemodgui.cpp index 50a5b723f..5d90fd636 100644 --- a/plugins/channelrx/demodwfm/wfmdemodgui.cpp +++ b/plugins/channelrx/demodwfm/wfmdemodgui.cpp @@ -315,14 +315,16 @@ void WFMDemodGUI::displaySettings() blockApplySettings(false); } -void WFMDemodGUI::leaveEvent(QEvent*) +void WFMDemodGUI::leaveEvent(QEvent* event) { m_channelMarker.setHighlighted(false); + ChannelGUI::leaveEvent(event); } -void WFMDemodGUI::enterEvent(QEvent*) +void WFMDemodGUI::enterEvent(QEvent* event) { m_channelMarker.setHighlighted(true); + ChannelGUI::enterEvent(event); } void WFMDemodGUI::audioSelect() diff --git a/plugins/channelrx/filesink/filesinkgui.cpp b/plugins/channelrx/filesink/filesinkgui.cpp index 04f51aa65..e2ab550d1 100644 --- a/plugins/channelrx/filesink/filesinkgui.cpp +++ b/plugins/channelrx/filesink/filesinkgui.cpp @@ -305,14 +305,16 @@ void FileSinkGUI::displayPos() ui->filterChainIndex->setText(tr("%1").arg(m_fixedShiftIndex)); } -void FileSinkGUI::leaveEvent(QEvent*) +void FileSinkGUI::leaveEvent(QEvent* event) { m_channelMarker.setHighlighted(false); + ChannelGUI::leaveEvent(event); } -void FileSinkGUI::enterEvent(QEvent*) +void FileSinkGUI::enterEvent(QEvent* event) { m_channelMarker.setHighlighted(true); + ChannelGUI::enterEvent(event); } void FileSinkGUI::channelMarkerChangedByCursor() diff --git a/plugins/channelrx/freqtracker/freqtrackergui.cpp b/plugins/channelrx/freqtracker/freqtrackergui.cpp index 7aa2512c6..3e946492f 100644 --- a/plugins/channelrx/freqtracker/freqtrackergui.cpp +++ b/plugins/channelrx/freqtracker/freqtrackergui.cpp @@ -465,14 +465,16 @@ void FreqTrackerGUI::displaySpectrumBandwidth(int spanLog2) ui->glSpectrum->setSampleRate(spectrumRate); } -void FreqTrackerGUI::leaveEvent(QEvent*) +void FreqTrackerGUI::leaveEvent(QEvent* event) { m_channelMarker.setHighlighted(false); + ChannelGUI::leaveEvent(event); } -void FreqTrackerGUI::enterEvent(QEvent*) +void FreqTrackerGUI::enterEvent(QEvent* event) { m_channelMarker.setHighlighted(true); + ChannelGUI::enterEvent(event); } void FreqTrackerGUI::tick() diff --git a/plugins/channelrx/localsink/localsinkgui.cpp b/plugins/channelrx/localsink/localsinkgui.cpp index 89d85d450..399cae251 100644 --- a/plugins/channelrx/localsink/localsinkgui.cpp +++ b/plugins/channelrx/localsink/localsinkgui.cpp @@ -223,14 +223,16 @@ int LocalSinkGUI::getLocalDeviceIndexInCombo(int localDeviceIndex) return -1; } -void LocalSinkGUI::leaveEvent(QEvent*) +void LocalSinkGUI::leaveEvent(QEvent* event) { m_channelMarker.setHighlighted(false); + ChannelGUI::leaveEvent(event); } -void LocalSinkGUI::enterEvent(QEvent*) +void LocalSinkGUI::enterEvent(QEvent* event) { m_channelMarker.setHighlighted(true); + ChannelGUI::enterEvent(event); } void LocalSinkGUI::handleSourceMessages() diff --git a/plugins/channelrx/noisefigure/noisefiguregui.cpp b/plugins/channelrx/noisefigure/noisefiguregui.cpp index c2bc9a0db..765d13515 100644 --- a/plugins/channelrx/noisefigure/noisefiguregui.cpp +++ b/plugins/channelrx/noisefigure/noisefiguregui.cpp @@ -758,14 +758,16 @@ void NoiseFigureGUI::displaySettings() blockApplySettings(false); } -void NoiseFigureGUI::leaveEvent(QEvent*) +void NoiseFigureGUI::leaveEvent(QEvent* event) { m_channelMarker.setHighlighted(false); + ChannelGUI::leaveEvent(event); } -void NoiseFigureGUI::enterEvent(QEvent*) +void NoiseFigureGUI::enterEvent(QEvent* event) { m_channelMarker.setHighlighted(true); + ChannelGUI::enterEvent(event); } void NoiseFigureGUI::tick() diff --git a/plugins/channelrx/radioastronomy/radioastronomygui.cpp b/plugins/channelrx/radioastronomy/radioastronomygui.cpp index 8a3996162..5fe80f66a 100644 --- a/plugins/channelrx/radioastronomy/radioastronomygui.cpp +++ b/plugins/channelrx/radioastronomy/radioastronomygui.cpp @@ -2569,14 +2569,16 @@ void RadioAstronomyGUI::displaySettings() getRollupContents()->arrangeRollups(); } -void RadioAstronomyGUI::leaveEvent(QEvent*) +void RadioAstronomyGUI::leaveEvent(QEvent* event) { m_channelMarker.setHighlighted(false); + ChannelGUI::leaveEvent(event); } -void RadioAstronomyGUI::enterEvent(QEvent*) +void RadioAstronomyGUI::enterEvent(QEvent* event) { m_channelMarker.setHighlighted(true); + ChannelGUI::enterEvent(event); } void RadioAstronomyGUI::tick() diff --git a/plugins/channelrx/radioclock/radioclockgui.cpp b/plugins/channelrx/radioclock/radioclockgui.cpp index 675463e1e..826736eab 100644 --- a/plugins/channelrx/radioclock/radioclockgui.cpp +++ b/plugins/channelrx/radioclock/radioclockgui.cpp @@ -390,14 +390,16 @@ void RadioClockGUI::displaySettings() blockApplySettings(false); } -void RadioClockGUI::leaveEvent(QEvent*) +void RadioClockGUI::leaveEvent(QEvent* event) { m_channelMarker.setHighlighted(false); + ChannelGUI::leaveEvent(event); } -void RadioClockGUI::enterEvent(QEvent*) +void RadioClockGUI::enterEvent(QEvent* event) { m_channelMarker.setHighlighted(true); + ChannelGUI::enterEvent(event); } void RadioClockGUI::tick() diff --git a/plugins/channelrx/remotesink/remotesinkgui.cpp b/plugins/channelrx/remotesink/remotesinkgui.cpp index 50f4cf9ad..eaeae5cb2 100644 --- a/plugins/channelrx/remotesink/remotesinkgui.cpp +++ b/plugins/channelrx/remotesink/remotesinkgui.cpp @@ -192,14 +192,16 @@ void RemoteSinkGUI::displayRateAndShift() m_channelMarker.setBandwidth(channelSampleRate); } -void RemoteSinkGUI::leaveEvent(QEvent*) +void RemoteSinkGUI::leaveEvent(QEvent* event) { m_channelMarker.setHighlighted(false); + ChannelGUI::leaveEvent(event); } -void RemoteSinkGUI::enterEvent(QEvent*) +void RemoteSinkGUI::enterEvent(QEvent* event) { m_channelMarker.setHighlighted(true); + ChannelGUI::enterEvent(event); } void RemoteSinkGUI::handleSourceMessages() diff --git a/plugins/channelrx/sigmffilesink/sigmffilesinkgui.cpp b/plugins/channelrx/sigmffilesink/sigmffilesinkgui.cpp index 28606ff7a..48c10287e 100644 --- a/plugins/channelrx/sigmffilesink/sigmffilesinkgui.cpp +++ b/plugins/channelrx/sigmffilesink/sigmffilesinkgui.cpp @@ -297,14 +297,16 @@ void SigMFFileSinkGUI::displayPos() ui->filterChainIndex->setText(tr("%1").arg(m_fixedShiftIndex)); } -void SigMFFileSinkGUI::leaveEvent(QEvent*) +void SigMFFileSinkGUI::leaveEvent(QEvent* event) { m_channelMarker.setHighlighted(false); + ChannelGUI::leaveEvent(event); } -void SigMFFileSinkGUI::enterEvent(QEvent*) +void SigMFFileSinkGUI::enterEvent(QEvent* event) { m_channelMarker.setHighlighted(true); + ChannelGUI::enterEvent(event); } void SigMFFileSinkGUI::channelMarkerChangedByCursor() diff --git a/plugins/channelrx/udpsink/udpsinkgui.cpp b/plugins/channelrx/udpsink/udpsinkgui.cpp index 855c7751d..8f2269cf4 100644 --- a/plugins/channelrx/udpsink/udpsinkgui.cpp +++ b/plugins/channelrx/udpsink/udpsinkgui.cpp @@ -649,14 +649,16 @@ void UDPSinkGUI::onMenuDialogCalled(const QPoint &p) resetContextMenuType(); } -void UDPSinkGUI::leaveEvent(QEvent*) +void UDPSinkGUI::leaveEvent(QEvent* event) { m_channelMarker.setHighlighted(false); + ChannelGUI::leaveEvent(event); } -void UDPSinkGUI::enterEvent(QEvent*) +void UDPSinkGUI::enterEvent(QEvent* event) { m_channelMarker.setHighlighted(true); + ChannelGUI::enterEvent(event); } void UDPSinkGUI::makeUIConnections() diff --git a/plugins/channeltx/filesource/filesourcegui.cpp b/plugins/channeltx/filesource/filesourcegui.cpp index f0e0bdda7..6bff969f8 100644 --- a/plugins/channeltx/filesource/filesourcegui.cpp +++ b/plugins/channeltx/filesource/filesourcegui.cpp @@ -333,14 +333,16 @@ void FileSourceGUI::displayRateAndShift() m_channelMarker.setBandwidth(channelSampleRate); } -void FileSourceGUI::leaveEvent(QEvent*) +void FileSourceGUI::leaveEvent(QEvent* event) { m_channelMarker.setHighlighted(false); + ChannelGUI::leaveEvent(event); } -void FileSourceGUI::enterEvent(QEvent*) +void FileSourceGUI::enterEvent(QEvent* event) { m_channelMarker.setHighlighted(true); + ChannelGUI::enterEvent(event); } void FileSourceGUI::handleSourceMessages() diff --git a/plugins/channeltx/localsource/localsourcegui.cpp b/plugins/channeltx/localsource/localsourcegui.cpp index 1a1e346c1..e18769251 100644 --- a/plugins/channeltx/localsource/localsourcegui.cpp +++ b/plugins/channeltx/localsource/localsourcegui.cpp @@ -198,14 +198,16 @@ void LocalSourceGUI::updateLocalDevices() } } -void LocalSourceGUI::leaveEvent(QEvent*) +void LocalSourceGUI::leaveEvent(QEvent* event) { m_channelMarker.setHighlighted(false); + ChannelGUI::leaveEvent(event); } -void LocalSourceGUI::enterEvent(QEvent*) +void LocalSourceGUI::enterEvent(QEvent* event) { m_channelMarker.setHighlighted(true); + ChannelGUI::enterEvent(event); } void LocalSourceGUI::handleSourceMessages() diff --git a/plugins/channeltx/mod802.15.4/ieee_802_15_4_modgui.cpp b/plugins/channeltx/mod802.15.4/ieee_802_15_4_modgui.cpp index c5cbe8e36..fa95810e0 100644 --- a/plugins/channeltx/mod802.15.4/ieee_802_15_4_modgui.cpp +++ b/plugins/channeltx/mod802.15.4/ieee_802_15_4_modgui.cpp @@ -605,14 +605,16 @@ QString IEEE_802_15_4_ModGUI::getDisplayValueWithMultiplier(int value) } } -void IEEE_802_15_4_ModGUI::leaveEvent(QEvent*) +void IEEE_802_15_4_ModGUI::leaveEvent(QEvent* event) { m_channelMarker.setHighlighted(false); + ChannelGUI::leaveEvent(event); } -void IEEE_802_15_4_ModGUI::enterEvent(QEvent*) +void IEEE_802_15_4_ModGUI::enterEvent(QEvent* event) { m_channelMarker.setHighlighted(true); + ChannelGUI::enterEvent(event); } void IEEE_802_15_4_ModGUI::tick() diff --git a/plugins/channeltx/modais/aismodgui.cpp b/plugins/channeltx/modais/aismodgui.cpp index 2348d50c1..4a6f74a47 100644 --- a/plugins/channeltx/modais/aismodgui.cpp +++ b/plugins/channeltx/modais/aismodgui.cpp @@ -575,14 +575,16 @@ void AISModGUI::displaySettings() blockApplySettings(false); } -void AISModGUI::leaveEvent(QEvent*) +void AISModGUI::leaveEvent(QEvent* event) { m_channelMarker.setHighlighted(false); + ChannelGUI::leaveEvent(event); } -void AISModGUI::enterEvent(QEvent*) +void AISModGUI::enterEvent(QEvent* event) { m_channelMarker.setHighlighted(true); + ChannelGUI::enterEvent(event); } void AISModGUI::tick() diff --git a/plugins/channeltx/modam/ammodgui.cpp b/plugins/channeltx/modam/ammodgui.cpp index ba71b516c..e7b71bfb4 100644 --- a/plugins/channeltx/modam/ammodgui.cpp +++ b/plugins/channeltx/modam/ammodgui.cpp @@ -480,14 +480,16 @@ void AMModGUI::displaySettings() blockApplySettings(false); } -void AMModGUI::leaveEvent(QEvent*) +void AMModGUI::leaveEvent(QEvent* event) { m_channelMarker.setHighlighted(false); + ChannelGUI::leaveEvent(event); } -void AMModGUI::enterEvent(QEvent*) +void AMModGUI::enterEvent(QEvent* event) { m_channelMarker.setHighlighted(true); + ChannelGUI::enterEvent(event); } void AMModGUI::audioSelect() diff --git a/plugins/channeltx/modatv/atvmodgui.cpp b/plugins/channeltx/modatv/atvmodgui.cpp index c3dc7d76e..511765c84 100644 --- a/plugins/channeltx/modatv/atvmodgui.cpp +++ b/plugins/channeltx/modatv/atvmodgui.cpp @@ -818,14 +818,16 @@ void ATVModGUI::displaySettings() blockApplySettings(false); } -void ATVModGUI::leaveEvent(QEvent*) +void ATVModGUI::leaveEvent(QEvent* event) { m_channelMarker.setHighlighted(false); + ChannelGUI::leaveEvent(event); } -void ATVModGUI::enterEvent(QEvent*) +void ATVModGUI::enterEvent(QEvent* event) { m_channelMarker.setHighlighted(true); + ChannelGUI::enterEvent(event); } void ATVModGUI::tick() diff --git a/plugins/channeltx/modchirpchat/chirpchatmodgui.cpp b/plugins/channeltx/modchirpchat/chirpchatmodgui.cpp index b8af4b4ec..c30310e72 100644 --- a/plugins/channeltx/modchirpchat/chirpchatmodgui.cpp +++ b/plugins/channeltx/modchirpchat/chirpchatmodgui.cpp @@ -592,14 +592,16 @@ void ChirpChatModGUI::setBandwidths() } } -void ChirpChatModGUI::leaveEvent(QEvent*) +void ChirpChatModGUI::leaveEvent(QEvent* event) { m_channelMarker.setHighlighted(false); + ChannelGUI::leaveEvent(event); } -void ChirpChatModGUI::enterEvent(QEvent*) +void ChirpChatModGUI::enterEvent(QEvent* event) { m_channelMarker.setHighlighted(true); + ChannelGUI::enterEvent(event); } void ChirpChatModGUI::tick() diff --git a/plugins/channeltx/moddatv/datvmodgui.cpp b/plugins/channeltx/moddatv/datvmodgui.cpp index 370c2945d..5040dd02a 100644 --- a/plugins/channeltx/moddatv/datvmodgui.cpp +++ b/plugins/channeltx/moddatv/datvmodgui.cpp @@ -604,14 +604,16 @@ void DATVModGUI::displaySettings() blockApplySettings(false); } -void DATVModGUI::leaveEvent(QEvent*) +void DATVModGUI::leaveEvent(QEvent* event) { m_channelMarker.setHighlighted(false); + ChannelGUI::leaveEvent(event); } -void DATVModGUI::enterEvent(QEvent*) +void DATVModGUI::enterEvent(QEvent* event) { m_channelMarker.setHighlighted(true); + ChannelGUI::enterEvent(event); } void DATVModGUI::tick() diff --git a/plugins/channeltx/modfreedv/freedvmodgui.cpp b/plugins/channeltx/modfreedv/freedvmodgui.cpp index 406f65e2e..6adc7b4fe 100644 --- a/plugins/channeltx/modfreedv/freedvmodgui.cpp +++ b/plugins/channeltx/modfreedv/freedvmodgui.cpp @@ -522,14 +522,16 @@ void FreeDVModGUI::displaySettings() blockApplySettings(false); } -void FreeDVModGUI::leaveEvent(QEvent*) +void FreeDVModGUI::leaveEvent(QEvent* event) { m_channelMarker.setHighlighted(false); + ChannelGUI::leaveEvent(event); } -void FreeDVModGUI::enterEvent(QEvent*) +void FreeDVModGUI::enterEvent(QEvent* event) { m_channelMarker.setHighlighted(true); + ChannelGUI::enterEvent(event); } void FreeDVModGUI::audioSelect() diff --git a/plugins/channeltx/modnfm/nfmmodgui.cpp b/plugins/channeltx/modnfm/nfmmodgui.cpp index e1023b923..c5221d99d 100644 --- a/plugins/channeltx/modnfm/nfmmodgui.cpp +++ b/plugins/channeltx/modnfm/nfmmodgui.cpp @@ -579,14 +579,16 @@ void NFMModGUI::displaySettings() blockApplySettings(false); } -void NFMModGUI::leaveEvent(QEvent*) +void NFMModGUI::leaveEvent(QEvent* event) { m_channelMarker.setHighlighted(false); + ChannelGUI::leaveEvent(event); } -void NFMModGUI::enterEvent(QEvent*) +void NFMModGUI::enterEvent(QEvent* event) { m_channelMarker.setHighlighted(true); + ChannelGUI::enterEvent(event); } void NFMModGUI::audioSelect() diff --git a/plugins/channeltx/modpacket/packetmodgui.cpp b/plugins/channeltx/modpacket/packetmodgui.cpp index a1bd5ac8e..621314241 100644 --- a/plugins/channeltx/modpacket/packetmodgui.cpp +++ b/plugins/channeltx/modpacket/packetmodgui.cpp @@ -595,14 +595,16 @@ void PacketModGUI::displaySettings() blockApplySettings(false); } -void PacketModGUI::leaveEvent(QEvent*) +void PacketModGUI::leaveEvent(QEvent* event) { m_channelMarker.setHighlighted(false); + ChannelGUI::leaveEvent(event); } -void PacketModGUI::enterEvent(QEvent*) +void PacketModGUI::enterEvent(QEvent* event) { m_channelMarker.setHighlighted(true); + ChannelGUI::enterEvent(event); } void PacketModGUI::tick() diff --git a/plugins/channeltx/modssb/ssbmodgui.cpp b/plugins/channeltx/modssb/ssbmodgui.cpp index b55f10b46..c9a8696f3 100644 --- a/plugins/channeltx/modssb/ssbmodgui.cpp +++ b/plugins/channeltx/modssb/ssbmodgui.cpp @@ -736,14 +736,16 @@ void SSBModGUI::displaySettings() blockApplySettings(false); } -void SSBModGUI::leaveEvent(QEvent*) +void SSBModGUI::leaveEvent(QEvent* event) { m_channelMarker.setHighlighted(false); + ChannelGUI::leaveEvent(event); } -void SSBModGUI::enterEvent(QEvent*) +void SSBModGUI::enterEvent(QEvent* event) { m_channelMarker.setHighlighted(true); + ChannelGUI::enterEvent(event); } void SSBModGUI::audioSelect() diff --git a/plugins/channeltx/modwfm/wfmmodgui.cpp b/plugins/channeltx/modwfm/wfmmodgui.cpp index 1da374cfd..d9d744c22 100644 --- a/plugins/channeltx/modwfm/wfmmodgui.cpp +++ b/plugins/channeltx/modwfm/wfmmodgui.cpp @@ -497,14 +497,16 @@ void WFMModGUI::displaySettings() blockApplySettings(false); } -void WFMModGUI::leaveEvent(QEvent*) +void WFMModGUI::leaveEvent(QEvent* event) { m_channelMarker.setHighlighted(false); + ChannelGUI::leaveEvent(event); } -void WFMModGUI::enterEvent(QEvent*) +void WFMModGUI::enterEvent(QEvent* event) { m_channelMarker.setHighlighted(true); + ChannelGUI::enterEvent(event); } void WFMModGUI::audioSelect() diff --git a/plugins/channeltx/remotesource/remotesourcegui.cpp b/plugins/channeltx/remotesource/remotesourcegui.cpp index 7bf6f5198..5ca564926 100644 --- a/plugins/channeltx/remotesource/remotesourcegui.cpp +++ b/plugins/channeltx/remotesource/remotesourcegui.cpp @@ -271,14 +271,16 @@ void RemoteSourceGUI::displayPosition() ui->filterChainText->setText(s); } -void RemoteSourceGUI::leaveEvent(QEvent*) +void RemoteSourceGUI::leaveEvent(QEvent* event) { m_channelMarker.setHighlighted(false); + ChannelGUI::leaveEvent(event); } -void RemoteSourceGUI::enterEvent(QEvent*) +void RemoteSourceGUI::enterEvent(QEvent* event) { m_channelMarker.setHighlighted(true); + ChannelGUI::enterEvent(event); } void RemoteSourceGUI::handleSourceMessages() diff --git a/plugins/channeltx/udpsource/udpsourcegui.cpp b/plugins/channeltx/udpsource/udpsourcegui.cpp index da0f41275..2e48c1598 100644 --- a/plugins/channeltx/udpsource/udpsourcegui.cpp +++ b/plugins/channeltx/udpsource/udpsourcegui.cpp @@ -530,14 +530,16 @@ void UDPSourceGUI::onMenuDialogCalled(const QPoint &p) resetContextMenuType(); } -void UDPSourceGUI::leaveEvent(QEvent*) +void UDPSourceGUI::leaveEvent(QEvent* event) { m_channelMarker.setHighlighted(false); + ChannelGUI::leaveEvent(event); } -void UDPSourceGUI::enterEvent(QEvent*) +void UDPSourceGUI::enterEvent(QEvent* event) { m_channelMarker.setHighlighted(true); + ChannelGUI::enterEvent(event); } void UDPSourceGUI::tick() diff --git a/plugins/feature/afc/afcgui.cpp b/plugins/feature/afc/afcgui.cpp index a5924e9ea..ebf176d26 100644 --- a/plugins/feature/afc/afcgui.cpp +++ b/plugins/feature/afc/afcgui.cpp @@ -255,14 +255,6 @@ void AFCGUI::updateDeviceSetLists(const AFC::MsgDeviceSetListsReport& report) ui->trackedDevice->blockSignals(false); } -void AFCGUI::leaveEvent(QEvent*) -{ -} - -void AFCGUI::enterEvent(QEvent*) -{ -} - void AFCGUI::onMenuDialogCalled(const QPoint &p) { if (m_contextMenuType == ContextMenuChannelSettings) diff --git a/plugins/feature/afc/afcgui.h b/plugins/feature/afc/afcgui.h index 83c73a537..a29e49503 100644 --- a/plugins/feature/afc/afcgui.h +++ b/plugins/feature/afc/afcgui.h @@ -73,9 +73,6 @@ private: bool handleMessage(const Message& message); void makeUIConnections(); - void leaveEvent(QEvent*); - void enterEvent(QEvent*); - private slots: void onMenuDialogCalled(const QPoint &p); void onWidgetRolled(QWidget* widget, bool rollDown); diff --git a/plugins/feature/ais/aisgui.cpp b/plugins/feature/ais/aisgui.cpp index 79a6656cc..4ea066702 100644 --- a/plugins/feature/ais/aisgui.cpp +++ b/plugins/feature/ais/aisgui.cpp @@ -257,14 +257,6 @@ void AISGUI::displaySettings() getRollupContents()->arrangeRollups(); } -void AISGUI::leaveEvent(QEvent*) -{ -} - -void AISGUI::enterEvent(QEvent*) -{ -} - void AISGUI::onMenuDialogCalled(const QPoint &p) { if (m_contextMenuType == ContextMenuChannelSettings) diff --git a/plugins/feature/ais/aisgui.h b/plugins/feature/ais/aisgui.h index 7bc3651a6..107276ffb 100644 --- a/plugins/feature/ais/aisgui.h +++ b/plugins/feature/ais/aisgui.h @@ -92,9 +92,6 @@ private: void displaySettings(); bool handleMessage(const Message& message); - void leaveEvent(QEvent*); - void enterEvent(QEvent*); - void sendToMap(const QString &name, const QString &label, const QString &image, const QString &text, const QString &model, float modelOffset, float labelOffset, diff --git a/plugins/feature/antennatools/antennatoolsgui.cpp b/plugins/feature/antennatools/antennatoolsgui.cpp index b1e55847f..d3051f407 100644 --- a/plugins/feature/antennatools/antennatoolsgui.cpp +++ b/plugins/feature/antennatools/antennatoolsgui.cpp @@ -190,14 +190,6 @@ void AntennaToolsGUI::makeUIConnections() QObject::connect(ui->dishSurfaceError, qOverload(&QDoubleSpinBox::valueChanged), this, &AntennaToolsGUI::on_dishSurfaceError_valueChanged); } -void AntennaToolsGUI::leaveEvent(QEvent*) -{ -} - -void AntennaToolsGUI::enterEvent(QEvent*) -{ -} - void AntennaToolsGUI::onMenuDialogCalled(const QPoint &p) { if (m_contextMenuType == ContextMenuChannelSettings) diff --git a/plugins/feature/antennatools/antennatoolsgui.h b/plugins/feature/antennatools/antennatoolsgui.h index 95823f9a3..ad4b74ceb 100644 --- a/plugins/feature/antennatools/antennatoolsgui.h +++ b/plugins/feature/antennatools/antennatoolsgui.h @@ -73,9 +73,6 @@ private: bool handleMessage(const Message& message); void makeUIConnections(); - void leaveEvent(QEvent*); - void enterEvent(QEvent*); - void calcDipoleLength(); double calcDipoleFrequency(double totalLength); void calcDishFocalLength(); diff --git a/plugins/feature/aprs/aprsgui.cpp b/plugins/feature/aprs/aprsgui.cpp index f83e50f60..80ed4108c 100644 --- a/plugins/feature/aprs/aprsgui.cpp +++ b/plugins/feature/aprs/aprsgui.cpp @@ -660,14 +660,6 @@ void APRSGUI::updateChannelList() ui->sourcePipes->blockSignals(false); } -void APRSGUI::leaveEvent(QEvent*) -{ -} - -void APRSGUI::enterEvent(QEvent*) -{ -} - void APRSGUI::resizeEvent(QResizeEvent* size) { // Replot graphs to ensure Axis are visible diff --git a/plugins/feature/aprs/aprsgui.h b/plugins/feature/aprs/aprsgui.h index f1a89d1e5..4b9c67442 100644 --- a/plugins/feature/aprs/aprsgui.h +++ b/plugins/feature/aprs/aprsgui.h @@ -161,9 +161,6 @@ private: bool handleMessage(const Message& message); void makeUIConnections(); - void leaveEvent(QEvent*); - void enterEvent(QEvent*); - void filterMessageRow(int row); void filterMessages(); void resizeTable(); diff --git a/plugins/feature/demodanalyzer/demodanalyzergui.cpp b/plugins/feature/demodanalyzer/demodanalyzergui.cpp index 0fefd371e..9872a0bfb 100644 --- a/plugins/feature/demodanalyzer/demodanalyzergui.cpp +++ b/plugins/feature/demodanalyzer/demodanalyzergui.cpp @@ -239,14 +239,6 @@ void DemodAnalyzerGUI::updateChannelList() } } -void DemodAnalyzerGUI::leaveEvent(QEvent*) -{ -} - -void DemodAnalyzerGUI::enterEvent(QEvent*) -{ -} - void DemodAnalyzerGUI::onMenuDialogCalled(const QPoint &p) { if (m_contextMenuType == ContextMenuChannelSettings) diff --git a/plugins/feature/demodanalyzer/demodanalyzergui.h b/plugins/feature/demodanalyzer/demodanalyzergui.h index e26fddd60..d21c0b1c3 100644 --- a/plugins/feature/demodanalyzer/demodanalyzergui.h +++ b/plugins/feature/demodanalyzer/demodanalyzergui.h @@ -84,9 +84,6 @@ private: bool handleMessage(const Message& message); void makeUIConnections(); - void leaveEvent(QEvent*); - void enterEvent(QEvent*); - private slots: void onMenuDialogCalled(const QPoint &p); void onWidgetRolled(QWidget* widget, bool rollDown); diff --git a/plugins/feature/gs232controller/gs232controllergui.cpp b/plugins/feature/gs232controller/gs232controllergui.cpp index e777ee052..d90da437e 100644 --- a/plugins/feature/gs232controller/gs232controllergui.cpp +++ b/plugins/feature/gs232controller/gs232controllergui.cpp @@ -284,14 +284,6 @@ void GS232ControllerGUI::updatePipeList(const QList m_radioTimeTransmitters; diff --git a/plugins/feature/pertester/pertestergui.cpp b/plugins/feature/pertester/pertestergui.cpp index 55e9be8d6..e8b15309a 100644 --- a/plugins/feature/pertester/pertestergui.cpp +++ b/plugins/feature/pertester/pertestergui.cpp @@ -181,14 +181,6 @@ void PERTesterGUI::displaySettings() getRollupContents()->arrangeRollups(); } -void PERTesterGUI::leaveEvent(QEvent*) -{ -} - -void PERTesterGUI::enterEvent(QEvent*) -{ -} - void PERTesterGUI::onMenuDialogCalled(const QPoint &p) { if (m_contextMenuType == ContextMenuChannelSettings) diff --git a/plugins/feature/pertester/pertestergui.h b/plugins/feature/pertester/pertestergui.h index 2306bf792..3f4557c1e 100644 --- a/plugins/feature/pertester/pertestergui.h +++ b/plugins/feature/pertester/pertestergui.h @@ -72,9 +72,6 @@ private: bool handleMessage(const Message& message); void makeUIConnections(); - void leaveEvent(QEvent*); - void enterEvent(QEvent*); - private slots: void onMenuDialogCalled(const QPoint &p); void onWidgetRolled(QWidget* widget, bool rollDown); diff --git a/plugins/feature/radiosonde/radiosondegui.cpp b/plugins/feature/radiosonde/radiosondegui.cpp index ba68f6b28..da49edcec 100644 --- a/plugins/feature/radiosonde/radiosondegui.cpp +++ b/plugins/feature/radiosonde/radiosondegui.cpp @@ -222,14 +222,6 @@ void RadiosondeGUI::displaySettings() getRollupContents()->arrangeRollups(); } -void RadiosondeGUI::leaveEvent(QEvent*) -{ -} - -void RadiosondeGUI::enterEvent(QEvent*) -{ -} - void RadiosondeGUI::onMenuDialogCalled(const QPoint &p) { if (m_contextMenuType == ContextMenuChannelSettings) diff --git a/plugins/feature/radiosonde/radiosondegui.h b/plugins/feature/radiosonde/radiosondegui.h index 1bd135232..c42bd655c 100644 --- a/plugins/feature/radiosonde/radiosondegui.h +++ b/plugins/feature/radiosonde/radiosondegui.h @@ -104,9 +104,6 @@ private: bool handleMessage(const Message& message); void makeUIConnections(); - void leaveEvent(QEvent*); - void enterEvent(QEvent*); - void sendToMap(const QString &name, const QString &label, const QString &image, const QString &text, const QString &model, float labelOffset, diff --git a/plugins/feature/rigctlserver/rigctlservergui.cpp b/plugins/feature/rigctlserver/rigctlservergui.cpp index 352a36076..aa3798eba 100644 --- a/plugins/feature/rigctlserver/rigctlservergui.cpp +++ b/plugins/feature/rigctlserver/rigctlservergui.cpp @@ -268,14 +268,6 @@ bool RigCtlServerGUI::updateChannelList() return false; } -void RigCtlServerGUI::leaveEvent(QEvent*) -{ -} - -void RigCtlServerGUI::enterEvent(QEvent*) -{ -} - void RigCtlServerGUI::onMenuDialogCalled(const QPoint &p) { if (m_contextMenuType == ContextMenuChannelSettings) diff --git a/plugins/feature/rigctlserver/rigctlservergui.h b/plugins/feature/rigctlserver/rigctlservergui.h index 51f429a09..be69dcb12 100644 --- a/plugins/feature/rigctlserver/rigctlservergui.h +++ b/plugins/feature/rigctlserver/rigctlservergui.h @@ -74,9 +74,6 @@ private: bool handleMessage(const Message& message); void makeUIConnections(); - void leaveEvent(QEvent*); - void enterEvent(QEvent*); - private slots: void onMenuDialogCalled(const QPoint &p); void onWidgetRolled(QWidget* widget, bool rollDown); diff --git a/plugins/feature/satellitetracker/satellitetrackergui.cpp b/plugins/feature/satellitetracker/satellitetrackergui.cpp index fa0c4418f..6dd97e413 100644 --- a/plugins/feature/satellitetracker/satellitetrackergui.cpp +++ b/plugins/feature/satellitetracker/satellitetrackergui.cpp @@ -343,14 +343,6 @@ void SatelliteTrackerGUI::displaySettings() blockApplySettings(false); } -void SatelliteTrackerGUI::leaveEvent(QEvent*) -{ -} - -void SatelliteTrackerGUI::enterEvent(QEvent*) -{ -} - void SatelliteTrackerGUI::onMenuDialogCalled(const QPoint &p) { if (m_contextMenuType == ContextMenuChannelSettings) diff --git a/plugins/feature/satellitetracker/satellitetrackergui.h b/plugins/feature/satellitetracker/satellitetrackergui.h index 143d81bd6..301333216 100644 --- a/plugins/feature/satellitetracker/satellitetrackergui.h +++ b/plugins/feature/satellitetracker/satellitetrackergui.h @@ -136,9 +136,6 @@ private: void updateMapList(); void makeUIConnections(); - void leaveEvent(QEvent*); - void enterEvent(QEvent*); - private slots: void onMenuDialogCalled(const QPoint &p); void onWidgetRolled(QWidget* widget, bool rollDown); diff --git a/plugins/feature/simpleptt/simplepttgui.cpp b/plugins/feature/simpleptt/simplepttgui.cpp index 163094290..c7027f1a5 100644 --- a/plugins/feature/simpleptt/simplepttgui.cpp +++ b/plugins/feature/simpleptt/simplepttgui.cpp @@ -288,14 +288,6 @@ void SimplePTTGUI::updateDeviceSetLists() ui->txDevice->blockSignals(false); } -void SimplePTTGUI::leaveEvent(QEvent*) -{ -} - -void SimplePTTGUI::enterEvent(QEvent*) -{ -} - void SimplePTTGUI::onMenuDialogCalled(const QPoint &p) { if (m_contextMenuType == ContextMenuChannelSettings) diff --git a/plugins/feature/simpleptt/simplepttgui.h b/plugins/feature/simpleptt/simplepttgui.h index bee5244d8..284cbeebc 100644 --- a/plugins/feature/simpleptt/simplepttgui.h +++ b/plugins/feature/simpleptt/simplepttgui.h @@ -75,9 +75,6 @@ private: bool handleMessage(const Message& message); void makeUIConnections(); - void leaveEvent(QEvent*); - void enterEvent(QEvent*); - private slots: void onMenuDialogCalled(const QPoint &p); void onWidgetRolled(QWidget* widget, bool rollDown); diff --git a/plugins/feature/startracker/startrackergui.cpp b/plugins/feature/startracker/startrackergui.cpp index 33db89876..8ff5f40fe 100644 --- a/plugins/feature/startracker/startrackergui.cpp +++ b/plugins/feature/startracker/startrackergui.cpp @@ -451,14 +451,6 @@ void StarTrackerGUI::displaySettings() blockApplySettings(false); } -void StarTrackerGUI::leaveEvent(QEvent*) -{ -} - -void StarTrackerGUI::enterEvent(QEvent*) -{ -} - void StarTrackerGUI::onMenuDialogCalled(const QPoint &p) { if (m_contextMenuType == ContextMenuChannelSettings) diff --git a/plugins/feature/startracker/startrackergui.h b/plugins/feature/startracker/startrackergui.h index 802cfae69..689b00b39 100644 --- a/plugins/feature/startracker/startrackergui.h +++ b/plugins/feature/startracker/startrackergui.h @@ -159,9 +159,6 @@ private: void updateSolarFlux(bool all); void makeUIConnections(); - void leaveEvent(QEvent*); - void enterEvent(QEvent*); - private slots: void onMenuDialogCalled(const QPoint &p); void onWidgetRolled(QWidget* widget, bool rollDown); diff --git a/plugins/feature/vorlocalizer/vorlocalizergui.cpp b/plugins/feature/vorlocalizer/vorlocalizergui.cpp index 63648aa2e..dd5c5b0f2 100644 --- a/plugins/feature/vorlocalizer/vorlocalizergui.cpp +++ b/plugins/feature/vorlocalizer/vorlocalizergui.cpp @@ -1383,14 +1383,6 @@ void VORLocalizerGUI::displaySettings() blockApplySettings(false); } -void VORLocalizerGUI::leaveEvent(QEvent*) -{ -} - -void VORLocalizerGUI::enterEvent(QEvent*) -{ -} - void VORLocalizerGUI::updateStatus() { int state = m_vorLocalizer->getState(); diff --git a/plugins/feature/vorlocalizer/vorlocalizergui.h b/plugins/feature/vorlocalizer/vorlocalizergui.h index 0f498a930..0f741c952 100644 --- a/plugins/feature/vorlocalizer/vorlocalizergui.h +++ b/plugins/feature/vorlocalizer/vorlocalizergui.h @@ -258,9 +258,6 @@ private: bool handleMessage(const Message& message); void makeUIConnections(); - void leaveEvent(QEvent*); - void enterEvent(QEvent*); - void resizeTable(); QAction *createCheckableItem(QString& text, int idx, bool checked); diff --git a/sdrgui/CMakeLists.txt b/sdrgui/CMakeLists.txt index d83e53ec2..086def148 100644 --- a/sdrgui/CMakeLists.txt +++ b/sdrgui/CMakeLists.txt @@ -41,6 +41,7 @@ set(sdrgui_SOURCES gui/featurepresetsdialog.cpp gui/fftwisdomdialog.cpp gui/flowlayout.cpp + gui/framelesswindowresizer.cpp gui/glscope.cpp gui/glscopegui.cpp gui/glshadercolors.cpp @@ -140,6 +141,7 @@ set(sdrgui_HEADERS gui/featurepresetsdialog.h gui/fftwisdomdialog.h gui/flowlayout.h + gui/framelesswindowresizer.h gui/glscope.h gui/glscopegui.h gui/glshadercolors.h diff --git a/sdrgui/channel/channelgui.cpp b/sdrgui/channel/channelgui.cpp index 63297c098..6a76d5066 100644 --- a/sdrgui/channel/channelgui.cpp +++ b/sdrgui/channel/channelgui.cpp @@ -39,7 +39,8 @@ ChannelGUI::ChannelGUI(QWidget *parent) : m_deviceSetIndex(0), m_channelIndex(0), m_contextMenuType(ContextMenuNone), - m_drag(false) + m_drag(false), + m_resizer(this) { qDebug("ChannelGUI::ChannelGUI"); setWindowFlags(windowFlags() | Qt::FramelessWindowHint); @@ -123,7 +124,7 @@ ChannelGUI::ChannelGUI(QWidget *parent) : m_statusLabel->setToolTip("Channel status"); m_layouts = new QVBoxLayout(); - m_layouts->setContentsMargins(0, 4, 0, 4); + m_layouts->setContentsMargins(m_resizer.m_gripSize, 4, m_resizer.m_gripSize, 4); m_layouts->setSpacing(2); m_topLayout = new QHBoxLayout(); @@ -137,10 +138,6 @@ ChannelGUI::ChannelGUI(QWidget *parent) : m_topLayout->addWidget(m_shrinkButton); m_topLayout->addWidget(m_hideButton); m_topLayout->addWidget(m_closeButton); - m_sizeGripTopRight = new QSizeGrip(this); - m_sizeGripTopRight->setStyleSheet("QSizeGrip { background-color: rgb(128, 128, 128); width: 10px; height: 10px; }"); - m_sizeGripTopRight->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); - m_topLayout->addWidget(m_sizeGripTopRight, 0, Qt::AlignTop | Qt::AlignRight); m_centerLayout = new QHBoxLayout(); m_rollupContents = new RollupContents(); // Do not delete! Done in child's destructor with "delete ui" @@ -153,7 +150,6 @@ ChannelGUI::ChannelGUI(QWidget *parent) : m_bottomLayout->addWidget(m_statusFrequency); m_bottomLayout->addWidget(m_statusLabel); m_sizeGripBottomRight = new QSizeGrip(this); - m_sizeGripBottomRight->setStyleSheet("QSizeGrip { background-color: rgb(128, 128, 128); width: 10px; height: 10px; }"); m_sizeGripBottomRight->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); // m_bottomLayout->addStretch(1); m_bottomLayout->addWidget(m_sizeGripBottomRight, 0, Qt::AlignBottom | Qt::AlignRight); @@ -189,7 +185,6 @@ ChannelGUI::~ChannelGUI() delete m_sizeGripBottomRight; delete m_bottomLayout; delete m_centerLayout; - delete m_sizeGripTopRight; delete m_topLayout; delete m_layouts; delete m_statusLabel; @@ -222,6 +217,15 @@ void ChannelGUI::mousePressEvent(QMouseEvent* event) m_DragPosition = event->globalPos() - pos(); event->accept(); } + else + { + m_resizer.mousePressEvent(event); + } +} + +void ChannelGUI::mouseReleaseEvent(QMouseEvent* event) +{ + m_resizer.mouseReleaseEvent(event); } void ChannelGUI::mouseMoveEvent(QMouseEvent* event) @@ -231,6 +235,16 @@ void ChannelGUI::mouseMoveEvent(QMouseEvent* event) move(event->globalPos() - m_DragPosition); event->accept(); } + else + { + m_resizer.mouseMoveEvent(event); + } +} + +void ChannelGUI::leaveEvent(QEvent* event) +{ + m_resizer.leaveEvent(event); + QMdiSubWindow::leaveEvent(event); } void ChannelGUI::activateSettingsDialog() diff --git a/sdrgui/channel/channelgui.h b/sdrgui/channel/channelgui.h index a933ac339..889638adf 100644 --- a/sdrgui/channel/channelgui.h +++ b/sdrgui/channel/channelgui.h @@ -21,6 +21,7 @@ #include #include +#include "gui/framelesswindowresizer.h" #include "export.h" class QCloseEvent; @@ -87,9 +88,11 @@ public: void setStatusText(const QString& text); protected: - void closeEvent(QCloseEvent *event); - void mousePressEvent(QMouseEvent* event); - void mouseMoveEvent(QMouseEvent* event); + void closeEvent(QCloseEvent *event) override; + void leaveEvent(QEvent *event) override; + void mousePressEvent(QMouseEvent* event) override; + void mouseReleaseEvent(QMouseEvent* event) override; + void mouseMoveEvent(QMouseEvent* event) override; void resetContextMenuType() { m_contextMenuType = ContextMenuNone; } void updateIndexLabel(); @@ -125,11 +128,11 @@ private: QHBoxLayout *m_topLayout; QHBoxLayout *m_centerLayout; QHBoxLayout *m_bottomLayout; - QSizeGrip *m_sizeGripTopRight; QSizeGrip *m_sizeGripBottomRight; bool m_drag; QPoint m_DragPosition; QMap m_heightsMap; + FramelessWindowResizer m_resizer; private slots: void activateSettingsDialog(); diff --git a/sdrgui/device/devicegui.cpp b/sdrgui/device/devicegui.cpp index 6bcb2c671..b0a24260f 100644 --- a/sdrgui/device/devicegui.cpp +++ b/sdrgui/device/devicegui.cpp @@ -37,7 +37,8 @@ DeviceGUI::DeviceGUI(QWidget *parent) : m_deviceSetIndex(0), m_contextMenuType(ContextMenuNone), m_drag(false), - m_currentDeviceIndex(-1) + m_currentDeviceIndex(-1), + m_resizer(this) { qDebug("DeviceGUI::DeviceGUI: %p", parent); setWindowFlags(windowFlags() | Qt::FramelessWindowHint); @@ -128,7 +129,7 @@ DeviceGUI::DeviceGUI(QWidget *parent) : m_showAllChannelsButton->setToolTip("Show all channels"); m_layouts = new QVBoxLayout(); - m_layouts->setContentsMargins(0, 4, 0, 4); + m_layouts->setContentsMargins(m_resizer.m_gripSize, 4, m_resizer.m_gripSize, 4); m_layouts->setSpacing(2); m_topLayout = new QHBoxLayout(); @@ -145,10 +146,6 @@ DeviceGUI::DeviceGUI(QWidget *parent) : m_topLayout->addWidget(m_moveButton); m_topLayout->addWidget(m_shrinkButton); m_topLayout->addWidget(m_closeButton); - m_sizeGripTopRight = new QSizeGrip(this); - m_sizeGripTopRight->setStyleSheet("QSizeGrip { background-color: rgb(128, 128, 128); width: 10px; height: 10px; }"); - m_sizeGripTopRight->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); - m_topLayout->addWidget(m_sizeGripTopRight, 0, Qt::AlignTop | Qt::AlignRight); m_centerLayout = new QHBoxLayout(); m_contents = new QWidget(); // Do not delete! Done in child's destructor with "delete ui" @@ -160,7 +157,6 @@ DeviceGUI::DeviceGUI(QWidget *parent) : m_bottomLayout->addWidget(m_showAllChannelsButton); m_bottomLayout->addWidget(m_statusLabel); m_sizeGripBottomRight = new QSizeGrip(this); - m_sizeGripBottomRight->setStyleSheet("QSizeGrip { background-color: rgb(128, 128, 128); width: 10px; height: 10px; }"); m_sizeGripBottomRight->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); // m_bottomLayout->addStretch(1); m_bottomLayout->addWidget(m_sizeGripBottomRight, 0, Qt::AlignBottom | Qt::AlignRight); @@ -199,7 +195,6 @@ DeviceGUI::~DeviceGUI() delete m_sizeGripBottomRight; delete m_bottomLayout; delete m_centerLayout; - delete m_sizeGripTopRight; delete m_topLayout; delete m_layouts; delete m_showAllChannelsButton; @@ -234,6 +229,15 @@ void DeviceGUI::mousePressEvent(QMouseEvent* event) m_DragPosition = event->globalPos() - pos(); event->accept(); } + else + { + m_resizer.mousePressEvent(event); + } +} + +void DeviceGUI::mouseReleaseEvent(QMouseEvent* event) +{ + m_resizer.mouseReleaseEvent(event); } void DeviceGUI::mouseMoveEvent(QMouseEvent* event) @@ -243,6 +247,16 @@ void DeviceGUI::mouseMoveEvent(QMouseEvent* event) move(event->globalPos() - m_DragPosition); event->accept(); } + else + { + m_resizer.mouseMoveEvent(event); + } +} + +void DeviceGUI::leaveEvent(QEvent* event) +{ + m_resizer.leaveEvent(event); + QMdiSubWindow::leaveEvent(event); } void DeviceGUI::openChangeDeviceDialog() diff --git a/sdrgui/device/devicegui.h b/sdrgui/device/devicegui.h index d4899c84d..69831084b 100644 --- a/sdrgui/device/devicegui.h +++ b/sdrgui/device/devicegui.h @@ -26,6 +26,7 @@ #include #include "gui/channeladddialog.h" +#include "gui/framelesswindowresizer.h" #include "export.h" class QCloseEvent; @@ -76,9 +77,11 @@ public: void setChannelNames(const QStringList& channelNames) { m_channelAddDialog.addChannelNames(channelNames); } protected: - void closeEvent(QCloseEvent *event); - void mousePressEvent(QMouseEvent* event); - void mouseMoveEvent(QMouseEvent* event); + void closeEvent(QCloseEvent *event) override; + void leaveEvent(QEvent *event) override; + void mousePressEvent(QMouseEvent* event) override; + void mouseReleaseEvent(QMouseEvent* event) override; + void mouseMoveEvent(QMouseEvent* event) override; void resetContextMenuType() { m_contextMenuType = ContextMenuNone; } DeviceType m_deviceType; @@ -114,12 +117,12 @@ private: QHBoxLayout *m_topLayout; QHBoxLayout *m_centerLayout; QHBoxLayout *m_bottomLayout; - QSizeGrip *m_sizeGripTopRight; QSizeGrip *m_sizeGripBottomRight; bool m_drag; QPoint m_DragPosition; int m_currentDeviceIndex; //!< Index in device plugins registrations ChannelAddDialog m_channelAddDialog; + FramelessWindowResizer m_resizer; private slots: void activateSettingsDialog(); diff --git a/sdrgui/feature/featuregui.cpp b/sdrgui/feature/featuregui.cpp index b4d587b8e..764e8706b 100644 --- a/sdrgui/feature/featuregui.cpp +++ b/sdrgui/feature/featuregui.cpp @@ -34,7 +34,8 @@ FeatureGUI::FeatureGUI(QWidget *parent) : QMdiSubWindow(parent), m_featureIndex(0), m_contextMenuType(ContextMenuNone), - m_drag(false) + m_drag(false), + m_resizer(this) { qDebug("FeatureGUI::FeatureGUI"); setWindowFlags(windowFlags() | Qt::FramelessWindowHint); @@ -89,7 +90,7 @@ FeatureGUI::FeatureGUI(QWidget *parent) : m_statusLabel->setToolTip("Feature status"); m_layouts = new QVBoxLayout(); - m_layouts->setContentsMargins(0, 4, 0, 4); + m_layouts->setContentsMargins(m_resizer.m_gripSize, 4, m_resizer.m_gripSize, 4); m_layouts->setSpacing(2); m_topLayout = new QHBoxLayout(); @@ -102,10 +103,6 @@ FeatureGUI::FeatureGUI(QWidget *parent) : m_topLayout->addWidget(m_moveButton); m_topLayout->addWidget(m_shrinkButton); m_topLayout->addWidget(m_closeButton); - m_sizeGripTopRight = new QSizeGrip(this); - m_sizeGripTopRight->setStyleSheet("QSizeGrip { background-color: rgb(128, 128, 128); width: 10px; height: 10px; }"); - m_sizeGripTopRight->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); - m_topLayout->addWidget(m_sizeGripTopRight, 0, Qt::AlignTop | Qt::AlignRight); m_centerLayout = new QHBoxLayout(); m_centerLayout->addWidget(&m_rollupContents); @@ -114,7 +111,6 @@ FeatureGUI::FeatureGUI(QWidget *parent) : m_bottomLayout->setContentsMargins(0, 0, 0, 0); m_bottomLayout->addWidget(m_statusLabel); m_sizeGripBottomRight = new QSizeGrip(this); - m_sizeGripBottomRight->setStyleSheet("QSizeGrip { background-color: rgb(128, 128, 128); width: 10px; height: 10px; }"); m_sizeGripBottomRight->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); // m_bottomLayout->addStretch(1); m_bottomLayout->addWidget(m_sizeGripBottomRight, 0, Qt::AlignBottom | Qt::AlignRight); @@ -147,7 +143,6 @@ FeatureGUI::~FeatureGUI() delete m_sizeGripBottomRight; delete m_bottomLayout; delete m_centerLayout; - delete m_sizeGripTopRight; delete m_topLayout; delete m_layouts; delete m_statusLabel; @@ -176,6 +171,15 @@ void FeatureGUI::mousePressEvent(QMouseEvent* event) m_DragPosition = event->globalPos() - pos(); event->accept(); } + else + { + m_resizer.mousePressEvent(event); + } +} + +void FeatureGUI::mouseReleaseEvent(QMouseEvent* event) +{ + m_resizer.mouseReleaseEvent(event); } void FeatureGUI::mouseMoveEvent(QMouseEvent* event) @@ -185,6 +189,16 @@ void FeatureGUI::mouseMoveEvent(QMouseEvent* event) move(event->globalPos() - m_DragPosition); event->accept(); } + else + { + m_resizer.mouseMoveEvent(event); + } +} + +void FeatureGUI::leaveEvent(QEvent* event) +{ + m_resizer.leaveEvent(event); + QMdiSubWindow::leaveEvent(event); } void FeatureGUI::activateSettingsDialog() diff --git a/sdrgui/feature/featuregui.h b/sdrgui/feature/featuregui.h index 1c7f3e894..0d8e5ae78 100644 --- a/sdrgui/feature/featuregui.h +++ b/sdrgui/feature/featuregui.h @@ -21,6 +21,7 @@ #include #include +#include "gui/framelesswindowresizer.h" #include "gui/rollupcontents.h" #include "export.h" @@ -65,9 +66,11 @@ public: void setDisplayedame(const QString& name); protected: - void closeEvent(QCloseEvent *event); - void mousePressEvent(QMouseEvent* event); - void mouseMoveEvent(QMouseEvent* event); + void closeEvent(QCloseEvent *event) override; + void leaveEvent(QEvent *event) override; + void mousePressEvent(QMouseEvent* event) override; + void mouseReleaseEvent(QMouseEvent* event) override; + void mouseMoveEvent(QMouseEvent* event) override; void resetContextMenuType() { m_contextMenuType = ContextMenuNone; } int m_featureIndex; @@ -94,11 +97,11 @@ private: QHBoxLayout *m_topLayout; QHBoxLayout *m_centerLayout; QHBoxLayout *m_bottomLayout; - QSizeGrip *m_sizeGripTopRight; QSizeGrip *m_sizeGripBottomRight; bool m_drag; QPoint m_DragPosition; QMap m_heightsMap; + FramelessWindowResizer m_resizer; private slots: void activateSettingsDialog(); diff --git a/sdrgui/gui/framelesswindowresizer.cpp b/sdrgui/gui/framelesswindowresizer.cpp new file mode 100644 index 000000000..97119938c --- /dev/null +++ b/sdrgui/gui/framelesswindowresizer.cpp @@ -0,0 +1,236 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2022 Jon Beniston, M7RCE // +// // +// This program 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 as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program 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 V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#include +#include + +#include "framelesswindowresizer.h" + +FramelessWindowResizer::FramelessWindowResizer(QWidget *widget) : + m_widget(widget), + m_vResizing(false), + m_hResizing(false), + m_vMove(false), + m_hMove(false), + m_cursor(nullptr), + m_vCursor(Qt::SizeVerCursor), + m_hCursor(Qt::SizeHorCursor), + m_bCursor(Qt::SizeBDiagCursor), + m_fCursor(Qt::SizeFDiagCursor) +{ +} + +bool FramelessWindowResizer::mouseOnTopBorder(QPoint pos) const +{ + return (pos.y() >= 0) && (pos.y() < m_gripSize); +} + +bool FramelessWindowResizer::mouseOnBottomBorder(QPoint pos) const +{ + return (pos.y() > m_widget->height() - 1 - m_gripSize) && (pos.y() < m_widget->height()); +} + +bool FramelessWindowResizer::mouseOnLeftBorder(QPoint pos) const +{ + return (pos.x() >= 0) && (pos.x() < m_gripSize); +} + +bool FramelessWindowResizer::mouseOnRightBorder(QPoint pos) const +{ + return (pos.x() > m_widget->width() - 1 - m_gripSize) && (pos.x() < m_widget->width()); +} + +bool FramelessWindowResizer::mouseOnBorder(QPoint pos) const +{ + return mouseOnTopBorder(pos) || mouseOnBottomBorder(pos) + || mouseOnLeftBorder(pos) || mouseOnRightBorder(pos); +} + +void FramelessWindowResizer::setCursor(const QCursor &cursor) +{ + if (m_cursor != &cursor) + { + if (m_cursor != nullptr) { + QGuiApplication::restoreOverrideCursor(); + } + QGuiApplication::setOverrideCursor(cursor); + m_cursor = &cursor; + } +} + +void FramelessWindowResizer::clearCursor() +{ + if (m_cursor) + { + QGuiApplication::restoreOverrideCursor(); + m_cursor = nullptr; + } +} + +void FramelessWindowResizer::mousePressEvent(QMouseEvent* event) +{ + if ((event->buttons() & Qt::LeftButton) && mouseOnBorder(event->pos())) + { + if (mouseOnTopBorder(event->pos()) || mouseOnBottomBorder(event->pos())) { + m_vResizing = true; + } + if (mouseOnLeftBorder(event->pos()) || mouseOnRightBorder(event->pos())) { + m_hResizing = true; + } + if (mouseOnTopBorder(event->pos())) + { + m_vMove = true; + // Calculate difference between mouse position and top left corner of widget + m_mouseOffsetToPos = event->globalPos() - m_widget->pos(); + } + if (mouseOnLeftBorder(event->pos())) + { + m_hMove = true; + m_mouseOffsetToPos = event->globalPos() - m_widget->pos(); + } + // Save coords of bottom/right of widget + m_origBottomRight.setX(m_widget->pos().x() + m_widget->width()); + m_origBottomRight.setY(m_widget->pos().y() + m_widget->height()); + + m_initialMousePos = event->globalPos(); + m_initRect = m_widget->rect(); + + event->accept(); + } +} + +void FramelessWindowResizer::mouseReleaseEvent(QMouseEvent* event) +{ + if (!(event->buttons() & Qt::LeftButton)) + { + m_vResizing = false; + m_hResizing = false; + m_vMove = false; + m_hMove = false; + } +} + +void FramelessWindowResizer::leaveEvent(QEvent* event) +{ + clearCursor(); +} + +void FramelessWindowResizer::mouseMoveEvent(QMouseEvent* event) +{ + if (m_vResizing || m_hResizing) + { + + int x, y; + + // Calculate resize requested + int w = m_initRect.width(); + int h = m_initRect.height(); + QPoint delta = event->globalPos() - m_initialMousePos; + + x = delta.x(); + y = delta.y(); + if (m_hMove) { + x = -x; + } + if (m_vMove) { + y = -y; + } + if (m_hResizing) { + w += x; + } + if (m_vResizing) { + h += y; + } + QSize reqSize(w, h); + + // Get min and max size we can resize to + QSize minSize, maxSize; + if (m_widget->layout()) + { + minSize = m_widget->layout()->minimumSize(); + maxSize = m_widget->layout()->maximumSize(); + } + else + { + minSize = m_widget->minimumSize(); + maxSize = m_widget->maximumSize(); + } + + // Limit requested to size to allowed min/max + QSize size = reqSize; + size = size.expandedTo(minSize); + size = size.boundedTo(maxSize); + + // Move + if (m_vMove || m_hMove) + { + if (m_hMove) + { + x = event->globalPos().x() - m_mouseOffsetToPos.x(); + + if (x + minSize.width() > m_origBottomRight.x()) { + x = m_origBottomRight.x() - minSize.width(); + } + } + else + { + x = m_widget->pos().x(); + } + if (m_vMove) + { + y = event->globalPos().y() - m_mouseOffsetToPos.y(); + + if (y + minSize.height() > m_origBottomRight.y()) { + y = m_origBottomRight.y() - minSize.height(); + } + } + else + { + y = m_widget->pos().y(); + } + m_widget->move(x, y); + } + + m_widget->resize(size); + event->accept(); + } + else + { + QPoint pos = event->pos(); + if (mouseOnBorder(pos)) + { + // Set cursor according to edge or corner mouse is over + if (mouseOnTopBorder(pos) && mouseOnRightBorder(pos)) { + setCursor(m_bCursor); + } else if (mouseOnTopBorder(pos) && mouseOnLeftBorder(pos)) { + setCursor(m_fCursor); + } else if (mouseOnBottomBorder(pos) && mouseOnRightBorder(pos)) { + setCursor(m_fCursor); + } else if (mouseOnBottomBorder(pos) && mouseOnLeftBorder(pos)) { + setCursor(m_bCursor); + } else if (mouseOnTopBorder(pos) || mouseOnBottomBorder(pos)) { + setCursor(m_vCursor); + } else if (mouseOnLeftBorder(pos) || mouseOnRightBorder(pos)) { + setCursor(m_hCursor); + } + } + else + { + clearCursor(); + } + } +} diff --git a/sdrgui/gui/framelesswindowresizer.h b/sdrgui/gui/framelesswindowresizer.h new file mode 100644 index 000000000..931b36c63 --- /dev/null +++ b/sdrgui/gui/framelesswindowresizer.h @@ -0,0 +1,70 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2022 Jon Beniston, M7RCE // +// // +// This program 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 as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program 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 V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef SDRGUI_FRAMELESSWINDOWRESIZER_H_ +#define SDRGUI_FRAMELESSWINDOWRESIZER_H_ + +#include "export.h" + +#include +#include +#include + +// Class to allow frameless windows (Qt::FramelessWindowHint) to be resized +// by clicking and draging on the border +// The window needs to forward the mousePressEvent, mouseReleaseEvent, mouseMoveEvent +// and leaveEvent events to this class +class SDRGUI_API FramelessWindowResizer +{ +private: + QWidget *m_widget; // Widget to be resized + bool m_vResizing; // Whether we are resizing vertically + bool m_hResizing; + bool m_vMove; // Whether we are moving vertically + bool m_hMove; + QPoint m_mouseOffsetToPos;// Offset from mouse position to top/left of window + QPoint m_initialMousePos; // Position of mouse when resize started + QRect m_initRect; // Dimensions of widget when resize started + QPoint m_origBottomRight; // So we do not "push" the widget when resizing from top or left + const QCursor *m_cursor; // Current cursor + + const QCursor m_vCursor; // Ideally static, but when run: + const QCursor m_hCursor; // QPixmap: Must construct a QGuiApplication before a QPixmap + const QCursor m_bCursor; + const QCursor m_fCursor; + +public: + FramelessWindowResizer(QWidget *widget); + void mousePressEvent(QMouseEvent* event); + void mouseReleaseEvent(QMouseEvent* event); + void mouseMoveEvent(QMouseEvent* event); + void leaveEvent(QEvent* event); + + const int m_gripSize = 3; // Size in pixels of the border of the window that can be clicked in to resize it + +protected: + bool mouseOnTopBorder(QPoint pos) const; + bool mouseOnBottomBorder(QPoint pos) const; + bool mouseOnLeftBorder(QPoint pos) const; + bool mouseOnRightBorder(QPoint pos) const; + bool mouseOnBorder(QPoint pos) const; + void setCursor(const QCursor &cursor); + void clearCursor(); + +}; + +#endif // SDRGUI_FRAMELESSWINDOWRESIZER_H_ diff --git a/sdrgui/mainspectrum/mainspectrumgui.cpp b/sdrgui/mainspectrum/mainspectrumgui.cpp index 2567bdc41..d87da6e9e 100644 --- a/sdrgui/mainspectrum/mainspectrumgui.cpp +++ b/sdrgui/mainspectrum/mainspectrumgui.cpp @@ -36,7 +36,8 @@ MainSpectrumGUI::MainSpectrumGUI(GLSpectrum *spectrum, GLSpectrumGUI *spectrumGU m_spectrumGUI(spectrumGUI), m_deviceType(DeviceRx), m_deviceSetIndex(0), - m_drag(false) + m_drag(false), + m_resizer(this) { qDebug("MainSpectrumGUI::MainSpectrumGUI: %p", parent); setWindowFlags(windowFlags() | Qt::FramelessWindowHint); @@ -92,7 +93,7 @@ MainSpectrumGUI::MainSpectrumGUI(GLSpectrum *spectrum, GLSpectrumGUI *spectrumGU // m_statusLabel->setToolTip("Spectrum status"); m_layouts = new QVBoxLayout(); - m_layouts->setContentsMargins(0, 4, 0, 4); + m_layouts->setContentsMargins(m_resizer.m_gripSize, 4, m_resizer.m_gripSize, 4); m_layouts->setSpacing(0); m_topLayout = new QHBoxLayout(); @@ -105,10 +106,6 @@ MainSpectrumGUI::MainSpectrumGUI(GLSpectrum *spectrum, GLSpectrumGUI *spectrumGU m_topLayout->addWidget(m_moveButton); m_topLayout->addWidget(m_shrinkButton); m_topLayout->addWidget(m_hideButton); - m_sizeGripTopRight = new QSizeGrip(this); - m_sizeGripTopRight->setStyleSheet("QSizeGrip { background-color: rgb(128, 128, 128); width: 10px; height: 10px; }"); - m_sizeGripTopRight->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); - m_topLayout->addWidget(m_sizeGripTopRight, 0, Qt::AlignTop | Qt::AlignRight); m_spectrumLayout = new QHBoxLayout(); m_spectrumLayout->addWidget(spectrum); @@ -120,7 +117,6 @@ MainSpectrumGUI::MainSpectrumGUI(GLSpectrum *spectrum, GLSpectrumGUI *spectrumGU m_bottomLayout->setContentsMargins(0, 0, 0, 0); m_bottomLayout->addWidget(m_statusLabel); m_sizeGripBottomRight = new QSizeGrip(this); - m_sizeGripBottomRight->setStyleSheet("QSizeGrip { background-color: rgb(128, 128, 128); width: 10px; height: 10px; }"); m_sizeGripBottomRight->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); //m_bottomLayout->addStretch(1); m_bottomLayout->addWidget(m_sizeGripBottomRight, 0, Qt::AlignBottom | Qt::AlignRight); @@ -151,7 +147,6 @@ MainSpectrumGUI::~MainSpectrumGUI() delete m_bottomLayout; delete m_spectrumGUILayout; delete m_spectrumLayout; - delete m_sizeGripTopRight; delete m_topLayout; delete m_layouts; delete m_statusLabel; @@ -180,6 +175,15 @@ void MainSpectrumGUI::mousePressEvent(QMouseEvent* event) m_DragPosition = event->globalPos() - pos(); event->accept(); } + else + { + m_resizer.mousePressEvent(event); + } +} + +void MainSpectrumGUI::mouseReleaseEvent(QMouseEvent* event) +{ + m_resizer.mouseReleaseEvent(event); } void MainSpectrumGUI::mouseMoveEvent(QMouseEvent* event) @@ -189,6 +193,16 @@ void MainSpectrumGUI::mouseMoveEvent(QMouseEvent* event) move(event->globalPos() - m_DragPosition); event->accept(); } + else + { + m_resizer.mouseMoveEvent(event); + } +} + +void MainSpectrumGUI::leaveEvent(QEvent* event) +{ + m_resizer.leaveEvent(event); + QMdiSubWindow::leaveEvent(event); } void MainSpectrumGUI::showHelp() diff --git a/sdrgui/mainspectrum/mainspectrumgui.h b/sdrgui/mainspectrum/mainspectrumgui.h index 859e61678..9adf00a91 100644 --- a/sdrgui/mainspectrum/mainspectrumgui.h +++ b/sdrgui/mainspectrum/mainspectrumgui.h @@ -22,6 +22,7 @@ #include #include "util/messagequeue.h" +#include "gui/framelesswindowresizer.h" #include "export.h" class GLSpectrum; @@ -82,16 +83,21 @@ private: QHBoxLayout *m_spectrumLayout; QHBoxLayout *m_spectrumGUILayout; QHBoxLayout *m_bottomLayout; - QSizeGrip *m_sizeGripTopRight; QSizeGrip *m_sizeGripBottomRight; bool m_drag; QPoint m_DragPosition; + FramelessWindowResizer m_resizer; static const int m_MinimumWidth = 360; static const int m_MinimumHeight = 200 + 20 + 10 + 4*22 + 5; - void closeEvent(QCloseEvent *event); - void mousePressEvent(QMouseEvent* event); - void mouseMoveEvent(QMouseEvent* event); +protected: + void closeEvent(QCloseEvent *event) override; + void leaveEvent(QEvent *event) override; + void mousePressEvent(QMouseEvent* event) override; + void mouseReleaseEvent(QMouseEvent* event) override; + void mouseMoveEvent(QMouseEvent* event) override; + +private: bool isOnMovingPad(); QString getDeviceTypeColor(); QString getDeviceTypeTag(); From db71b9fcb2997c944a673bdc65ab32954dde1d56 Mon Sep 17 00:00:00 2001 From: f4exb Date: Sat, 23 Apr 2022 13:57:37 +0200 Subject: [PATCH 050/115] Massive UI revamping (v7): FramelessWindowResizer: prevent vertical expansion of vertically fixed widgets. Part of #1209 --- sdrgui/gui/framelesswindowresizer.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/sdrgui/gui/framelesswindowresizer.cpp b/sdrgui/gui/framelesswindowresizer.cpp index 97119938c..3ecf98262 100644 --- a/sdrgui/gui/framelesswindowresizer.cpp +++ b/sdrgui/gui/framelesswindowresizer.cpp @@ -124,7 +124,7 @@ void FramelessWindowResizer::mouseReleaseEvent(QMouseEvent* event) } } -void FramelessWindowResizer::leaveEvent(QEvent* event) +void FramelessWindowResizer::leaveEvent(QEvent*) { clearCursor(); } @@ -159,6 +159,7 @@ void FramelessWindowResizer::mouseMoveEvent(QMouseEvent* event) // Get min and max size we can resize to QSize minSize, maxSize; + if (m_widget->layout()) { minSize = m_widget->layout()->minimumSize(); @@ -175,6 +176,11 @@ void FramelessWindowResizer::mouseMoveEvent(QMouseEvent* event) size = size.expandedTo(minSize); size = size.boundedTo(maxSize); + // Prevent vertical expansion of vertically fixed widgets + if (m_widget->sizePolicy().verticalPolicy() == QSizePolicy::Fixed) { + size.setHeight(m_widget->height()); + } + // Move if (m_vMove || m_hMove) { From 5ba8b21dcc4b731bdf2db4c9dc6add589c4b2784 Mon Sep 17 00:00:00 2001 From: f4exb Date: Sat, 23 Apr 2022 13:59:22 +0200 Subject: [PATCH 051/115] Massive UI revamping (v7): better handling of expandable rollup sub widgets. Part of #1209 --- sdrgui/channel/channelgui.cpp | 1 + sdrgui/channel/channelgui.h | 1 + sdrgui/gui/rollupcontents.cpp | 30 ++++++++++++++++++++++++++++++ sdrgui/gui/rollupcontents.h | 2 ++ 4 files changed, 34 insertions(+) diff --git a/sdrgui/channel/channelgui.cpp b/sdrgui/channel/channelgui.cpp index 6a76d5066..69e70ad5f 100644 --- a/sdrgui/channel/channelgui.cpp +++ b/sdrgui/channel/channelgui.cpp @@ -140,6 +140,7 @@ ChannelGUI::ChannelGUI(QWidget *parent) : m_topLayout->addWidget(m_closeButton); m_centerLayout = new QHBoxLayout(); + m_centerLayout->setContentsMargins(0, 0, 0, 0); m_rollupContents = new RollupContents(); // Do not delete! Done in child's destructor with "delete ui" m_centerLayout->addWidget(m_rollupContents); diff --git a/sdrgui/channel/channelgui.h b/sdrgui/channel/channelgui.h index 889638adf..9e02236ef 100644 --- a/sdrgui/channel/channelgui.h +++ b/sdrgui/channel/channelgui.h @@ -95,6 +95,7 @@ protected: void mouseMoveEvent(QMouseEvent* event) override; void resetContextMenuType() { m_contextMenuType = ContextMenuNone; } void updateIndexLabel(); + int getAdditionalHeight() const { return 29 + 26; } DeviceType m_deviceType; int m_deviceSetIndex; diff --git a/sdrgui/gui/rollupcontents.cpp b/sdrgui/gui/rollupcontents.cpp index 19294e78e..cce640575 100644 --- a/sdrgui/gui/rollupcontents.cpp +++ b/sdrgui/gui/rollupcontents.cpp @@ -92,6 +92,36 @@ void RollupContents::setHighlighted(bool highlighted) } } +int RollupContents::getAdditionalHeiht() +{ + int pos = 0; + + for (int i = 0; i < children().count(); ++i) + { + QWidget* r = qobject_cast(children()[i]); + + if (r && isRollupChild(r) && !r->isHidden()) { + pos += 5; + } + } + + return pos; +} + +bool RollupContents::hasExpandableWidgets() +{ + for (int i = 0; i < children().count(); ++i) + { + QWidget* r = qobject_cast(children()[i]); + + if (r && isRollupChild(r) && !r->isHidden() && (r->sizePolicy().verticalPolicy() == QSizePolicy::Expanding)) { + return true; + } + } + + return false; +} + int RollupContents::arrangeRollups() { QFontMetrics fm(font()); diff --git a/sdrgui/gui/rollupcontents.h b/sdrgui/gui/rollupcontents.h index 649398a17..2777b6f78 100644 --- a/sdrgui/gui/rollupcontents.h +++ b/sdrgui/gui/rollupcontents.h @@ -34,6 +34,8 @@ public: void saveState(RollupState& state) const; void restoreState(const RollupState& state); int arrangeRollups(); + int getAdditionalHeiht(); + bool hasExpandableWidgets(); signals: void widgetRolled(QWidget* widget, bool rollDown); From 72ae871c4a4dfd88f23a72b2ee52438bab2a0bcc Mon Sep 17 00:00:00 2001 From: f4exb Date: Sat, 23 Apr 2022 13:59:43 +0200 Subject: [PATCH 052/115] Massive UI revamping (v7): better handling of expandable rollup sub widgets. Apply to SSB demod. Part of #1209 --- plugins/channelrx/demodssb/ssbdemodgui.cpp | 28 ++++++++++++++++++---- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/plugins/channelrx/demodssb/ssbdemodgui.cpp b/plugins/channelrx/demodssb/ssbdemodgui.cpp index 1f4e02e73..f6dd5d02d 100644 --- a/plugins/channelrx/demodssb/ssbdemodgui.cpp +++ b/plugins/channelrx/demodssb/ssbdemodgui.cpp @@ -288,7 +288,23 @@ void SSBDemodGUI::onWidgetRolled(QWidget* widget, bool rollDown) (void) widget; (void) rollDown; - getRollupContents()->saveState(m_rollupState); + RollupContents *rollupContents = getRollupContents(); + + if (rollupContents->hasExpandableWidgets()) + { + qDebug("SSBDemodGUI::onWidgetRolled: set vertical policy expanding"); + setSizePolicy(sizePolicy().horizontalPolicy(), QSizePolicy::Expanding); + } + else + { + qDebug("SSBDemodGUI::onWidgetRolled: set vertical policy fixed"); + setSizePolicy(sizePolicy().horizontalPolicy(), QSizePolicy::Fixed); + } + + int h = rollupContents->height() + rollupContents->getAdditionalHeiht() + getAdditionalHeight(); + resize(width(), h); + + rollupContents->saveState(m_rollupState); applySettings(); } @@ -308,11 +324,13 @@ SSBDemodGUI::SSBDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Baseban m_squelchOpen(false), m_audioSampleRate(-1) { - ui->setupUi(getRollupContents()); - getRollupContents()->arrangeRollups(); - m_helpURL = "plugins/channelrx/demodssb/readme.md"; setAttribute(Qt::WA_DeleteOnClose, true); - connect(getRollupContents(), SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + m_helpURL = "plugins/channelrx/demodssb/readme.md"; + RollupContents *rollupContents = getRollupContents(); + ui->setupUi(rollupContents); + setSizePolicy(rollupContents->sizePolicy()); + rollupContents->arrangeRollups(); + connect(rollupContents, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &))); m_ssbDemod = (SSBDemod*) rxChannel; From c803788dbbfaac9cfba297b4c1b75b743714299f Mon Sep 17 00:00:00 2001 From: f4exb Date: Sat, 23 Apr 2022 14:00:23 +0200 Subject: [PATCH 053/115] Massive UI revamping (v7): make AM, NFM and WFM demods horizontally resizable within limits. Part of #1209 --- plugins/channelrx/demodam/amdemodgui.cpp | 19 +++++++--- plugins/channelrx/demodam/amdemodgui.h | 3 ++ plugins/channelrx/demodam/amdemodgui.ui | 28 +++++++++------ plugins/channelrx/demodnfm/nfmdemodgui.cpp | 19 +++++++--- plugins/channelrx/demodnfm/nfmdemodgui.h | 3 ++ plugins/channelrx/demodnfm/nfmdemodgui.ui | 26 ++++++++------ plugins/channelrx/demodwfm/wfmdemodgui.cpp | 18 +++++++--- plugins/channelrx/demodwfm/wfmdemodgui.h | 3 ++ plugins/channelrx/demodwfm/wfmdemodgui.ui | 42 +++++++++++----------- 9 files changed, 106 insertions(+), 55 deletions(-) diff --git a/plugins/channelrx/demodam/amdemodgui.cpp b/plugins/channelrx/demodam/amdemodgui.cpp index a50a2ef5c..e4922f10d 100644 --- a/plugins/channelrx/demodam/amdemodgui.cpp +++ b/plugins/channelrx/demodam/amdemodgui.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include "amdemodgui.h" #include "amdemodssbdialog.h" @@ -72,6 +73,14 @@ bool AMDemodGUI::deserialize(const QByteArray& data) } } +void AMDemodGUI::resizeEvent(QResizeEvent* size) +{ + int maxWidth = getRollupContents()->maximumWidth(); + int minHeight = getRollupContents()->minimumHeight() + getRollupContents()->getAdditionalHeiht() + getAdditionalHeight(); + resize(width() < maxWidth ? width() : maxWidth, minHeight); + size->accept(); +} + bool AMDemodGUI::handleMessage(const Message& message) { if (AMDemod::MsgConfigureAMDemod::match(message)) @@ -259,11 +268,13 @@ AMDemodGUI::AMDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandS m_samUSB(true), m_tickCount(0) { - ui->setupUi(getRollupContents()); - getRollupContents()->arrangeRollups(); - m_helpURL = "plugins/channelrx/demodam/readme.md"; setAttribute(Qt::WA_DeleteOnClose, true); - connect(getRollupContents(), SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + m_helpURL = "plugins/channelrx/demodam/readme.md"; + RollupContents *rollupContents = getRollupContents(); + ui->setupUi(rollupContents); + setSizePolicy(rollupContents->sizePolicy()); + rollupContents->arrangeRollups(); + connect(rollupContents, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &))); m_amDemod = reinterpret_cast(rxChannel); diff --git a/plugins/channelrx/demodam/amdemodgui.h b/plugins/channelrx/demodam/amdemodgui.h index cedcf4b0c..f32db77a2 100644 --- a/plugins/channelrx/demodam/amdemodgui.h +++ b/plugins/channelrx/demodam/amdemodgui.h @@ -47,6 +47,9 @@ public slots: void channelMarkerChangedByCursor(); void channelMarkerHighlightedByCursor(); +protected: + void resizeEvent(QResizeEvent* size); + private: Ui::AMDemodGUI* ui; PluginAPI* m_pluginAPI; diff --git a/plugins/channelrx/demodam/amdemodgui.ui b/plugins/channelrx/demodam/amdemodgui.ui index d4f9a2cf7..a7b361034 100644 --- a/plugins/channelrx/demodam/amdemodgui.ui +++ b/plugins/channelrx/demodam/amdemodgui.ui @@ -7,11 +7,11 @@ 0 0 360 - 153 + 155 - + 0 0 @@ -22,6 +22,12 @@ 0 + + + 560 + 16777215 + + Liberation Sans @@ -428,9 +434,10 @@ - ButtonSwitch - QToolButton -
    gui/buttonswitch.h
    + ValueDialZ + QWidget +
    gui/valuedialz.h
    + 1
    RollupContents @@ -438,18 +445,17 @@
    gui/rollupcontents.h
    1
    - - ValueDialZ - QWidget -
    gui/valuedialz.h
    - 1 -
    LevelMeterSignalDB QWidget
    gui/levelmeter.h
    1
    + + ButtonSwitch + QToolButton +
    gui/buttonswitch.h
    +
    diff --git a/plugins/channelrx/demodnfm/nfmdemodgui.cpp b/plugins/channelrx/demodnfm/nfmdemodgui.cpp index ebf9628d3..43b36027e 100644 --- a/plugins/channelrx/demodnfm/nfmdemodgui.cpp +++ b/plugins/channelrx/demodnfm/nfmdemodgui.cpp @@ -55,6 +55,14 @@ bool NFMDemodGUI::deserialize(const QByteArray& data) } } +void NFMDemodGUI::resizeEvent(QResizeEvent* size) +{ + int maxWidth = getRollupContents()->maximumWidth(); + int minHeight = getRollupContents()->minimumHeight() + getRollupContents()->getAdditionalHeiht() + getAdditionalHeight(); + resize(width() < maxWidth ? width() : maxWidth, minHeight); + size->accept(); +} + bool NFMDemodGUI::handleMessage(const Message& message) { if (NFMDemodReport::MsgReportCTCSSFreq::match(message)) @@ -359,12 +367,13 @@ NFMDemodGUI::NFMDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Baseban m_dcsShowPositive(false), m_tickCount(0) { - ui->setupUi(getRollupContents()); - getRollupContents()->arrangeRollups(); - m_helpURL = "plugins/channelrx/demodnfm/readme.md"; setAttribute(Qt::WA_DeleteOnClose, true); - - connect(getRollupContents(), SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + m_helpURL = "plugins/channelrx/demodnfm/readme.md"; + RollupContents *rollupContents = getRollupContents(); + ui->setupUi(rollupContents); + setSizePolicy(rollupContents->sizePolicy()); + rollupContents->arrangeRollups(); + connect(rollupContents, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &))); m_nfmDemod = reinterpret_cast(rxChannel); diff --git a/plugins/channelrx/demodnfm/nfmdemodgui.h b/plugins/channelrx/demodnfm/nfmdemodgui.h index e320900d6..a2394ea8f 100644 --- a/plugins/channelrx/demodnfm/nfmdemodgui.h +++ b/plugins/channelrx/demodnfm/nfmdemodgui.h @@ -46,6 +46,9 @@ public slots: void channelMarkerChangedByCursor(); void channelMarkerHighlightedByCursor(); +protected: + void resizeEvent(QResizeEvent* size); + private: Ui::NFMDemodGUI* ui; PluginAPI* m_pluginAPI; diff --git a/plugins/channelrx/demodnfm/nfmdemodgui.ui b/plugins/channelrx/demodnfm/nfmdemodgui.ui index 6ff83f5c7..db0bcd9a9 100644 --- a/plugins/channelrx/demodnfm/nfmdemodgui.ui +++ b/plugins/channelrx/demodnfm/nfmdemodgui.ui @@ -11,7 +11,7 @@
    - + 0 0 @@ -22,6 +22,12 @@ 0 + + + 560 + 16777215 + + Liberation Sans @@ -779,9 +785,10 @@
    - ButtonSwitch - QToolButton -
    gui/buttonswitch.h
    + ValueDialZ + QWidget +
    gui/valuedialz.h
    + 1
    RollupContents @@ -789,18 +796,17 @@
    gui/rollupcontents.h
    1
    - - ValueDialZ - QWidget -
    gui/valuedialz.h
    - 1 -
    LevelMeterSignalDB QWidget
    gui/levelmeter.h
    1
    + + ButtonSwitch + QToolButton +
    gui/buttonswitch.h
    +
    diff --git a/plugins/channelrx/demodwfm/wfmdemodgui.cpp b/plugins/channelrx/demodwfm/wfmdemodgui.cpp index 5d90fd636..2e8f17130 100644 --- a/plugins/channelrx/demodwfm/wfmdemodgui.cpp +++ b/plugins/channelrx/demodwfm/wfmdemodgui.cpp @@ -55,6 +55,14 @@ bool WFMDemodGUI::deserialize(const QByteArray& data) } } +void WFMDemodGUI::resizeEvent(QResizeEvent* size) +{ + int maxWidth = getRollupContents()->maximumWidth(); + int minHeight = getRollupContents()->minimumHeight() + getRollupContents()->getAdditionalHeiht() + getAdditionalHeight(); + resize(width() < maxWidth ? width() : maxWidth, minHeight); + size->accept(); +} + bool WFMDemodGUI::handleMessage(const Message& message) { if (WFMDemod::MsgConfigureWFMDemod::match(message)) @@ -218,11 +226,13 @@ WFMDemodGUI::WFMDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Baseban m_squelchOpen(false), m_audioSampleRate(-1) { - ui->setupUi(getRollupContents()); - getRollupContents()->arrangeRollups(); - m_helpURL = "plugins/channelrx/demodwfm/readme.md"; setAttribute(Qt::WA_DeleteOnClose, true); - connect(getRollupContents(), SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + m_helpURL = "plugins/channelrx/demodwfm/readme.md"; + RollupContents *rollupContents = getRollupContents(); + ui->setupUi(rollupContents); + setSizePolicy(rollupContents->sizePolicy()); + rollupContents->arrangeRollups(); + connect(rollupContents, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &))); connect(getInputMessageQueue(), SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages())); diff --git a/plugins/channelrx/demodwfm/wfmdemodgui.h b/plugins/channelrx/demodwfm/wfmdemodgui.h index e456cd5fc..ad4442667 100644 --- a/plugins/channelrx/demodwfm/wfmdemodgui.h +++ b/plugins/channelrx/demodwfm/wfmdemodgui.h @@ -44,6 +44,9 @@ public slots: void channelMarkerChangedByCursor(); void channelMarkerHighlightedByCursor(); +protected: + void resizeEvent(QResizeEvent* size); + private: Ui::WFMDemodGUI* ui; PluginAPI* m_pluginAPI; diff --git a/plugins/channelrx/demodwfm/wfmdemodgui.ui b/plugins/channelrx/demodwfm/wfmdemodgui.ui index d6ef37d7e..7d6e85e69 100644 --- a/plugins/channelrx/demodwfm/wfmdemodgui.ui +++ b/plugins/channelrx/demodwfm/wfmdemodgui.ui @@ -10,16 +10,22 @@ 170
    + + + 0 + 0 + + 302 - 0 + 170 - 302 - 16777215 + 560 + 170 @@ -46,12 +52,6 @@ 0 - - - 300 - 16777215 - - Settings @@ -435,6 +435,18 @@ + + ValueDial + QWidget +
    gui/valuedial.h
    + 1 +
    + + ValueDialZ + QWidget +
    gui/valuedialz.h
    + 1 +
    RollupContents QWidget @@ -447,18 +459,6 @@
    gui/levelmeter.h
    1
    - - ValueDialZ - QWidget -
    gui/valuedialz.h
    - 1 -
    - - ValueDial - QWidget -
    gui/valuedial.h
    - 1 -
    From ee6de484023cfb51db74f551f2b62b321fe3f47b Mon Sep 17 00:00:00 2001 From: f4exb Date: Sat, 23 Apr 2022 18:46:08 +0200 Subject: [PATCH 054/115] Massive UI revamping (v7): FramelessWindowResizer: prevent horizontal expansion of horizontally fixed widgets. Part of #1209 --- sdrgui/gui/framelesswindowresizer.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sdrgui/gui/framelesswindowresizer.cpp b/sdrgui/gui/framelesswindowresizer.cpp index 3ecf98262..ba5c0d7c5 100644 --- a/sdrgui/gui/framelesswindowresizer.cpp +++ b/sdrgui/gui/framelesswindowresizer.cpp @@ -181,6 +181,11 @@ void FramelessWindowResizer::mouseMoveEvent(QMouseEvent* event) size.setHeight(m_widget->height()); } + // Prevent horizontal expansion of horizontal fixed widgets + if (m_widget->sizePolicy().horizontalPolicy() == QSizePolicy::Fixed) { + size.setWidth(m_widget->width()); + } + // Move if (m_vMove || m_hMove) { From f7bfd10bd7aced6345b047070c811331c1f85695 Mon Sep 17 00:00:00 2001 From: f4exb Date: Sat, 23 Apr 2022 18:48:16 +0200 Subject: [PATCH 055/115] Massive UI revamping (v7): sample sink and mimo: make sure it is all fixed sized --- plugins/samplemimo/testmosync/testmosyncgui.cpp | 2 +- plugins/samplemimo/testmosync/testmosyncgui.ui | 2 +- plugins/samplesink/testsink/testsinkgui.cpp | 2 +- plugins/samplesink/testsink/testsinkgui.ui | 6 +++--- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/plugins/samplemimo/testmosync/testmosyncgui.cpp b/plugins/samplemimo/testmosync/testmosyncgui.cpp index 92fbd050c..68d1be442 100644 --- a/plugins/samplemimo/testmosync/testmosyncgui.cpp +++ b/plugins/samplemimo/testmosync/testmosyncgui.cpp @@ -52,7 +52,7 @@ TestMOSyncGui::TestMOSyncGui(DeviceUISet *deviceUISet, QWidget* parent) : { setAttribute(Qt::WA_DeleteOnClose, true); ui->setupUi(getContents()); - setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); getContents()->setStyleSheet(QString(tr("#TestMOSyncGui { border: 1px solid %1 }") .arg(palette().highlight().color().darker(115).name()))); m_helpURL = "plugins/samplemimo/testmosync/readme.md"; diff --git a/plugins/samplemimo/testmosync/testmosyncgui.ui b/plugins/samplemimo/testmosync/testmosyncgui.ui index a5b902822..f1313b9b6 100644 --- a/plugins/samplemimo/testmosync/testmosyncgui.ui +++ b/plugins/samplemimo/testmosync/testmosyncgui.ui @@ -11,7 +11,7 @@ - + 0 0 diff --git a/plugins/samplesink/testsink/testsinkgui.cpp b/plugins/samplesink/testsink/testsinkgui.cpp index 85ab065a6..c9e99b719 100644 --- a/plugins/samplesink/testsink/testsinkgui.cpp +++ b/plugins/samplesink/testsink/testsinkgui.cpp @@ -51,7 +51,7 @@ TestSinkGui::TestSinkGui(DeviceUISet *deviceUISet, QWidget* parent) : { setAttribute(Qt::WA_DeleteOnClose, true); ui->setupUi(getContents()); - setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); setMinimumSize(m_MinimumWidth, m_MinimumHeight); getContents()->setStyleSheet(QString(tr("#TestSinkGui { border: 1px solid %1 }") .arg(palette().highlight().color().darker(115).name()))); diff --git a/plugins/samplesink/testsink/testsinkgui.ui b/plugins/samplesink/testsink/testsinkgui.ui index 4aff8ac27..c525546f6 100644 --- a/plugins/samplesink/testsink/testsinkgui.ui +++ b/plugins/samplesink/testsink/testsinkgui.ui @@ -6,19 +6,19 @@ 0 0 - 360 + 364 350 - + 0 0 - 360 + 364 0 From f762863a239cabfc1522ed800f46efcc1d585181 Mon Sep 17 00:00:00 2001 From: f4exb Date: Sun, 24 Apr 2022 01:22:39 +0200 Subject: [PATCH 056/115] Massive UI revamping (v7): Feature and channels: set the border on the outer side of the window. Part of #1213 --- sdrgui/channel/channelgui.cpp | 10 ++++++++-- sdrgui/channel/channelgui.h | 2 +- sdrgui/feature/featuregui.cpp | 10 ++++++++-- sdrgui/feature/featuregui.h | 1 + sdrgui/gui/framelesswindowresizer.h | 2 +- sdrgui/gui/rollupcontents.cpp | 24 +++++------------------- sdrgui/gui/rollupcontents.h | 1 - 7 files changed, 24 insertions(+), 26 deletions(-) diff --git a/sdrgui/channel/channelgui.cpp b/sdrgui/channel/channelgui.cpp index 69e70ad5f..106df5191 100644 --- a/sdrgui/channel/channelgui.cpp +++ b/sdrgui/channel/channelgui.cpp @@ -44,6 +44,10 @@ ChannelGUI::ChannelGUI(QWidget *parent) : { qDebug("ChannelGUI::ChannelGUI"); setWindowFlags(windowFlags() | Qt::FramelessWindowHint); + setObjectName("ChannelGUI"); + setStyleSheet(QString(tr("#ChannelGUI { border: 1px solid %1; background-color: %2; }") + .arg(palette().highlight().color().darker(115).name())) + .arg(palette().dark().color().darker(115).name())); m_indexLabel = new QLabel(); m_indexLabel->setFixedSize(50, 16); @@ -124,8 +128,8 @@ ChannelGUI::ChannelGUI(QWidget *parent) : m_statusLabel->setToolTip("Channel status"); m_layouts = new QVBoxLayout(); - m_layouts->setContentsMargins(m_resizer.m_gripSize, 4, m_resizer.m_gripSize, 4); - m_layouts->setSpacing(2); + m_layouts->setContentsMargins(m_resizer.m_gripSize, m_resizer.m_gripSize, m_resizer.m_gripSize, m_resizer.m_gripSize); + m_layouts->setSpacing(0); m_topLayout = new QHBoxLayout(); m_topLayout->setContentsMargins(0, 0, 0, 0); @@ -152,6 +156,7 @@ ChannelGUI::ChannelGUI(QWidget *parent) : m_bottomLayout->addWidget(m_statusLabel); m_sizeGripBottomRight = new QSizeGrip(this); m_sizeGripBottomRight->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + m_sizeGripBottomRight->setFixedHeight(20); // m_bottomLayout->addStretch(1); m_bottomLayout->addWidget(m_sizeGripBottomRight, 0, Qt::AlignBottom | Qt::AlignRight); @@ -318,6 +323,7 @@ void ChannelGUI::shrinkWindow() { qDebug("ChannelGUI::shrinkWindow"); adjustSize(); + resize(width(), m_rollupContents->height() + getAdditionalHeight()); } void ChannelGUI::setTitle(const QString& title) diff --git a/sdrgui/channel/channelgui.h b/sdrgui/channel/channelgui.h index 9e02236ef..79199dd33 100644 --- a/sdrgui/channel/channelgui.h +++ b/sdrgui/channel/channelgui.h @@ -95,7 +95,7 @@ protected: void mouseMoveEvent(QMouseEvent* event) override; void resetContextMenuType() { m_contextMenuType = ContextMenuNone; } void updateIndexLabel(); - int getAdditionalHeight() const { return 29 + 26; } + int getAdditionalHeight() const { return 25 + 22; } DeviceType m_deviceType; int m_deviceSetIndex; diff --git a/sdrgui/feature/featuregui.cpp b/sdrgui/feature/featuregui.cpp index 764e8706b..7b732652f 100644 --- a/sdrgui/feature/featuregui.cpp +++ b/sdrgui/feature/featuregui.cpp @@ -39,6 +39,10 @@ FeatureGUI::FeatureGUI(QWidget *parent) : { qDebug("FeatureGUI::FeatureGUI"); setWindowFlags(windowFlags() | Qt::FramelessWindowHint); + setObjectName("FeatureGUI"); + setStyleSheet(QString(tr("#FeatureGUI { border: 1px solid %1; background-color: %2; }") + .arg(palette().highlight().color().darker(115).name())) + .arg(palette().dark().color().darker(115).name())); m_indexLabel = new QLabel(); m_indexLabel->setFixedSize(40, 16); @@ -90,8 +94,8 @@ FeatureGUI::FeatureGUI(QWidget *parent) : m_statusLabel->setToolTip("Feature status"); m_layouts = new QVBoxLayout(); - m_layouts->setContentsMargins(m_resizer.m_gripSize, 4, m_resizer.m_gripSize, 4); - m_layouts->setSpacing(2); + m_layouts->setContentsMargins(m_resizer.m_gripSize, m_resizer.m_gripSize, m_resizer.m_gripSize, m_resizer.m_gripSize); + m_layouts->setSpacing(0); m_topLayout = new QHBoxLayout(); m_topLayout->setContentsMargins(0, 0, 0, 0); @@ -112,6 +116,7 @@ FeatureGUI::FeatureGUI(QWidget *parent) : m_bottomLayout->addWidget(m_statusLabel); m_sizeGripBottomRight = new QSizeGrip(this); m_sizeGripBottomRight->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + m_sizeGripBottomRight->setFixedHeight(20); // m_bottomLayout->addStretch(1); m_bottomLayout->addWidget(m_sizeGripBottomRight, 0, Qt::AlignBottom | Qt::AlignRight); @@ -256,6 +261,7 @@ void FeatureGUI::shrinkWindow() { qDebug("FeatureGUI::shrinkWindow"); adjustSize(); + resize(width(), m_rollupContents.height() + getAdditionalHeight()); } void FeatureGUI::setTitle(const QString& title) diff --git a/sdrgui/feature/featuregui.h b/sdrgui/feature/featuregui.h index 0d8e5ae78..fbccad7b6 100644 --- a/sdrgui/feature/featuregui.h +++ b/sdrgui/feature/featuregui.h @@ -72,6 +72,7 @@ protected: void mouseReleaseEvent(QMouseEvent* event) override; void mouseMoveEvent(QMouseEvent* event) override; void resetContextMenuType() { m_contextMenuType = ContextMenuNone; } + int getAdditionalHeight() const { return 25 + 22; } int m_featureIndex; QString m_helpURL; diff --git a/sdrgui/gui/framelesswindowresizer.h b/sdrgui/gui/framelesswindowresizer.h index 931b36c63..7b2e106eb 100644 --- a/sdrgui/gui/framelesswindowresizer.h +++ b/sdrgui/gui/framelesswindowresizer.h @@ -54,7 +54,7 @@ public: void mouseMoveEvent(QMouseEvent* event); void leaveEvent(QEvent* event); - const int m_gripSize = 3; // Size in pixels of the border of the window that can be clicked in to resize it + const int m_gripSize = 2; // Size in pixels of the border of the window that can be clicked in to resize it protected: bool mouseOnTopBorder(QPoint pos) const; diff --git a/sdrgui/gui/rollupcontents.cpp b/sdrgui/gui/rollupcontents.cpp index cce640575..aa5c90b5e 100644 --- a/sdrgui/gui/rollupcontents.cpp +++ b/sdrgui/gui/rollupcontents.cpp @@ -92,22 +92,6 @@ void RollupContents::setHighlighted(bool highlighted) } } -int RollupContents::getAdditionalHeiht() -{ - int pos = 0; - - for (int i = 0; i < children().count(); ++i) - { - QWidget* r = qobject_cast(children()[i]); - - if (r && isRollupChild(r) && !r->isHidden()) { - pos += 5; - } - } - - return pos; -} - bool RollupContents::hasExpandableWidgets() { for (int i = 0; i < children().count(); ++i) @@ -218,7 +202,8 @@ int RollupContents::arrangeRollups() void RollupContents::paintEvent(QPaintEvent*) { QPainter p(this); - QColor frameColor = palette().highlight().color().darker(115); + QColor frameColor = palette().highlight().color().darker(125); + // QColor frameColor = Qt::black; // Eigenbau QFontMetrics fm(font()); @@ -237,9 +222,10 @@ void RollupContents::paintEvent(QPaintEvent*) p.setPen(m_highlighted ? Qt::white : frameColor); p.setBrush(palette().window()); QRectF r(rect()); - r.adjust(0.5, 0.5, -0.5, -0.5); + // r.adjust(0.5, 0.5, -0.5, -0.5); // p.drawRoundedRect(r, 3.0, 3.0, Qt::AbsoluteSize); - p.drawRect(r); + // p.drawRect(r); + p.fillRect(r, palette().window()); // Rollups int pos = 2; // fm.height() + 4; diff --git a/sdrgui/gui/rollupcontents.h b/sdrgui/gui/rollupcontents.h index 2777b6f78..898505a59 100644 --- a/sdrgui/gui/rollupcontents.h +++ b/sdrgui/gui/rollupcontents.h @@ -34,7 +34,6 @@ public: void saveState(RollupState& state) const; void restoreState(const RollupState& state); int arrangeRollups(); - int getAdditionalHeiht(); bool hasExpandableWidgets(); signals: From 417d860635bd9ee3309471c7c287747dcf63358b Mon Sep 17 00:00:00 2001 From: f4exb Date: Sun, 24 Apr 2022 01:24:22 +0200 Subject: [PATCH 057/115] Massive UI revamping (v7): AM, DSD, NFM, SSB, WFM demods: improved resizing and rollup collapse/expand handling. Part of #1209 --- plugins/channelrx/demodam/amdemodgui.cpp | 2 +- plugins/channelrx/demoddsd/dsddemodgui.cpp | 19 +++++++++++++---- plugins/channelrx/demoddsd/dsddemodgui.h | 3 +++ plugins/channelrx/demoddsd/dsddemodgui.ui | 24 ++++++++++++++-------- plugins/channelrx/demodnfm/nfmdemodgui.cpp | 2 +- plugins/channelrx/demodssb/ssbdemodgui.cpp | 2 +- plugins/channelrx/demodwfm/wfmdemodgui.cpp | 2 +- 7 files changed, 37 insertions(+), 17 deletions(-) diff --git a/plugins/channelrx/demodam/amdemodgui.cpp b/plugins/channelrx/demodam/amdemodgui.cpp index e4922f10d..3718d1fcf 100644 --- a/plugins/channelrx/demodam/amdemodgui.cpp +++ b/plugins/channelrx/demodam/amdemodgui.cpp @@ -76,7 +76,7 @@ bool AMDemodGUI::deserialize(const QByteArray& data) void AMDemodGUI::resizeEvent(QResizeEvent* size) { int maxWidth = getRollupContents()->maximumWidth(); - int minHeight = getRollupContents()->minimumHeight() + getRollupContents()->getAdditionalHeiht() + getAdditionalHeight(); + int minHeight = getRollupContents()->minimumHeight() + getAdditionalHeight(); resize(width() < maxWidth ? width() : maxWidth, minHeight); size->accept(); } diff --git a/plugins/channelrx/demoddsd/dsddemodgui.cpp b/plugins/channelrx/demoddsd/dsddemodgui.cpp index 3b0ee3fae..5f5d30829 100644 --- a/plugins/channelrx/demoddsd/dsddemodgui.cpp +++ b/plugins/channelrx/demoddsd/dsddemodgui.cpp @@ -82,6 +82,14 @@ bool DSDDemodGUI::deserialize(const QByteArray& data) } } +void DSDDemodGUI::resizeEvent(QResizeEvent* size) +{ + int maxWidth = getRollupContents()->maximumWidth(); + int minHeight = getRollupContents()->minimumHeight() + getAdditionalHeight(); + resize(width() < maxWidth ? width() : maxWidth, minHeight); + size->accept(); +} + bool DSDDemodGUI::handleMessage(const Message& message) { if (DSDDemod::MsgConfigureDSDDemod::match(message)) @@ -336,14 +344,17 @@ DSDDemodGUI::DSDDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Baseban m_tickCount(0), m_dsdStatusTextDialog(0) { - ui->setupUi(getRollupContents()); - getRollupContents()->arrangeRollups(); + setAttribute(Qt::WA_DeleteOnClose, true); m_helpURL = "plugins/channelrx/demoddsd/readme.md"; + RollupContents *rollupContents = getRollupContents(); + ui->setupUi(rollupContents); + setSizePolicy(rollupContents->sizePolicy()); + rollupContents->arrangeRollups(); + connect(rollupContents, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + ui->screenTV->setColor(true); ui->screenTV->resizeTVScreen(200,200); - setAttribute(Qt::WA_DeleteOnClose, true); - connect(getRollupContents(), SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &))); connect(getInputMessageQueue(), SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages())); diff --git a/plugins/channelrx/demoddsd/dsddemodgui.h b/plugins/channelrx/demoddsd/dsddemodgui.h index 27589f019..e5de1b302 100644 --- a/plugins/channelrx/demoddsd/dsddemodgui.h +++ b/plugins/channelrx/demoddsd/dsddemodgui.h @@ -68,6 +68,9 @@ public slots: void channelMarkerChangedByCursor(); void channelMarkerHighlightedByCursor(); +protected: + void resizeEvent(QResizeEvent* size); + private: // typedef enum // { diff --git a/plugins/channelrx/demoddsd/dsddemodgui.ui b/plugins/channelrx/demoddsd/dsddemodgui.ui index 295059813..781d39d68 100644 --- a/plugins/channelrx/demoddsd/dsddemodgui.ui +++ b/plugins/channelrx/demoddsd/dsddemodgui.ui @@ -11,7 +11,7 @@ - + 0 0 @@ -19,7 +19,13 @@ 650 - 0 + 392 + + + + + 750 + 392 @@ -1238,9 +1244,10 @@ 1 - ButtonSwitch - QToolButton -
    gui/buttonswitch.h
    + ValueDialZ + QWidget +
    gui/valuedialz.h
    + 1
    LevelMeterSignalDB @@ -1249,10 +1256,9 @@ 1 - ValueDialZ - QWidget -
    gui/valuedialz.h
    - 1 + ButtonSwitch + QToolButton +
    gui/buttonswitch.h
    TVScreen diff --git a/plugins/channelrx/demodnfm/nfmdemodgui.cpp b/plugins/channelrx/demodnfm/nfmdemodgui.cpp index 43b36027e..ae06b8e1b 100644 --- a/plugins/channelrx/demodnfm/nfmdemodgui.cpp +++ b/plugins/channelrx/demodnfm/nfmdemodgui.cpp @@ -58,7 +58,7 @@ bool NFMDemodGUI::deserialize(const QByteArray& data) void NFMDemodGUI::resizeEvent(QResizeEvent* size) { int maxWidth = getRollupContents()->maximumWidth(); - int minHeight = getRollupContents()->minimumHeight() + getRollupContents()->getAdditionalHeiht() + getAdditionalHeight(); + int minHeight = getRollupContents()->minimumHeight() + getAdditionalHeight(); resize(width() < maxWidth ? width() : maxWidth, minHeight); size->accept(); } diff --git a/plugins/channelrx/demodssb/ssbdemodgui.cpp b/plugins/channelrx/demodssb/ssbdemodgui.cpp index f6dd5d02d..ad1e1c61b 100644 --- a/plugins/channelrx/demodssb/ssbdemodgui.cpp +++ b/plugins/channelrx/demodssb/ssbdemodgui.cpp @@ -301,7 +301,7 @@ void SSBDemodGUI::onWidgetRolled(QWidget* widget, bool rollDown) setSizePolicy(sizePolicy().horizontalPolicy(), QSizePolicy::Fixed); } - int h = rollupContents->height() + rollupContents->getAdditionalHeiht() + getAdditionalHeight(); + int h = rollupContents->height() + getAdditionalHeight(); resize(width(), h); rollupContents->saveState(m_rollupState); diff --git a/plugins/channelrx/demodwfm/wfmdemodgui.cpp b/plugins/channelrx/demodwfm/wfmdemodgui.cpp index 2e8f17130..a191a7601 100644 --- a/plugins/channelrx/demodwfm/wfmdemodgui.cpp +++ b/plugins/channelrx/demodwfm/wfmdemodgui.cpp @@ -58,7 +58,7 @@ bool WFMDemodGUI::deserialize(const QByteArray& data) void WFMDemodGUI::resizeEvent(QResizeEvent* size) { int maxWidth = getRollupContents()->maximumWidth(); - int minHeight = getRollupContents()->minimumHeight() + getRollupContents()->getAdditionalHeiht() + getAdditionalHeight(); + int minHeight = getRollupContents()->minimumHeight() + getAdditionalHeight(); resize(width() < maxWidth ? width() : maxWidth, minHeight); size->accept(); } From 218237c466be22f3080353aee15097f30ce5298b Mon Sep 17 00:00:00 2001 From: f4exb Date: Sun, 24 Apr 2022 02:13:18 +0200 Subject: [PATCH 058/115] Massive UI revamping (v7): Devices: set the border on the outer side of the window. Part of #1213 --- plugins/samplemimo/bladerf2mimo/bladerf2mimogui.cpp | 3 +-- plugins/samplemimo/limesdrmimo/limesdrmimogui.cpp | 3 +-- plugins/samplemimo/metismiso/metismisogui.cpp | 3 +-- plugins/samplemimo/plutosdrmimo/plutosdrmimogui.cpp | 3 +-- plugins/samplemimo/testmi/testmigui.cpp | 3 +-- plugins/samplemimo/testmosync/testmosyncgui.cpp | 3 +-- plugins/samplemimo/xtrxmimo/xtrxmimogui.cpp | 3 +-- plugins/samplesink/audiooutput/audiooutputgui.cpp | 3 +-- .../samplesink/bladerf1output/bladerf1outputgui.cpp | 3 +-- .../samplesink/bladerf2output/bladerf2outputgui.cpp | 3 +-- plugins/samplesink/fileoutput/fileoutputgui.cpp | 3 +-- plugins/samplesink/hackrfoutput/hackrfoutputgui.cpp | 3 +-- plugins/samplesink/limesdroutput/limesdroutputgui.cpp | 3 +-- plugins/samplesink/localoutput/localoutputgui.cpp | 3 +-- .../samplesink/plutosdroutput/plutosdroutputgui.cpp | 3 +-- plugins/samplesink/remoteoutput/remoteoutputgui.cpp | 3 +-- .../samplesink/soapysdroutput/soapysdroutputgui.cpp | 3 +-- plugins/samplesink/testsink/testsinkgui.cpp | 3 +-- plugins/samplesink/usrpoutput/usrpoutputgui.cpp | 3 +-- plugins/samplesink/xtrxoutput/xtrxoutputgui.cpp | 3 +-- plugins/samplesource/airspy/airspygui.cpp | 3 +-- plugins/samplesource/airspyhf/airspyhfgui.cpp | 3 +-- plugins/samplesource/audioinput/audioinputgui.cpp | 3 +-- .../samplesource/bladerf1input/bladerf1inputgui.cpp | 3 +-- .../samplesource/bladerf2input/bladerf2inputgui.cpp | 3 +-- plugins/samplesource/fcdpro/fcdprogui.cpp | 3 +-- plugins/samplesource/fcdproplus/fcdproplusgui.cpp | 3 +-- plugins/samplesource/fileinput/fileinputgui.cpp | 3 +-- plugins/samplesource/hackrfinput/hackrfinputgui.cpp | 3 +-- plugins/samplesource/kiwisdr/kiwisdrgui.cpp | 3 +-- plugins/samplesource/limesdrinput/limesdrinputgui.cpp | 3 +-- plugins/samplesource/localinput/localinputgui.cpp | 3 +-- plugins/samplesource/perseus/perseusgui.cpp | 3 +-- .../samplesource/plutosdrinput/plutosdrinputgui.cpp | 3 +-- plugins/samplesource/remoteinput/remoteinputgui.cpp | 3 +-- plugins/samplesource/rtlsdr/rtlsdrgui.cpp | 3 +-- plugins/samplesource/sdrplay/sdrplaygui.cpp | 3 +-- plugins/samplesource/sdrplayv3/sdrplayv3gui.cpp | 3 +-- .../samplesource/sigmffileinput/sigmffileinputgui.cpp | 3 +-- .../samplesource/soapysdrinput/soapysdrinputgui.cpp | 3 +-- plugins/samplesource/testsource/testsourcegui.cpp | 3 +-- plugins/samplesource/usrpinput/usrpinputgui.cpp | 3 +-- plugins/samplesource/xtrxinput/xtrxinputgui.cpp | 3 +-- sdrgui/device/devicegui.cpp | 10 ++++++++-- sdrgui/device/devicegui.h | 1 + 45 files changed, 52 insertions(+), 88 deletions(-) diff --git a/plugins/samplemimo/bladerf2mimo/bladerf2mimogui.cpp b/plugins/samplemimo/bladerf2mimo/bladerf2mimogui.cpp index 433ebb37a..4513b2bda 100644 --- a/plugins/samplemimo/bladerf2mimo/bladerf2mimogui.cpp +++ b/plugins/samplemimo/bladerf2mimo/bladerf2mimogui.cpp @@ -69,8 +69,7 @@ BladeRF2MIMOGui::BladeRF2MIMOGui(DeviceUISet *deviceUISet, QWidget* parent) : setAttribute(Qt::WA_DeleteOnClose, true); ui->setupUi(getContents()); setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); - getContents()->setStyleSheet(QString(tr("#BladeRF2MIMOGui { border: 1px solid %1 }") - .arg(palette().highlight().color().darker(115).name()))); + getContents()->setStyleSheet("#BladeRF2MIMOGui { background-color: rgb(64, 64, 64); }"); m_helpURL = "plugins/samplemimo/bladerf2mimo/readme.md"; m_sampleMIMO = (BladeRF2MIMO*) m_deviceUISet->m_deviceAPI->getSampleMIMO(); diff --git a/plugins/samplemimo/limesdrmimo/limesdrmimogui.cpp b/plugins/samplemimo/limesdrmimo/limesdrmimogui.cpp index 5c6e9227c..f8c9d1d79 100644 --- a/plugins/samplemimo/limesdrmimo/limesdrmimogui.cpp +++ b/plugins/samplemimo/limesdrmimo/limesdrmimogui.cpp @@ -71,8 +71,7 @@ LimeSDRMIMOGUI::LimeSDRMIMOGUI(DeviceUISet *deviceUISet, QWidget* parent) : setAttribute(Qt::WA_DeleteOnClose, true); ui->setupUi(getContents()); setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); - getContents()->setStyleSheet(QString(tr("#LimeSDRMIMOGUI { border: 1px solid %1 }") - .arg(palette().highlight().color().darker(115).name()))); + getContents()->setStyleSheet("#LimeSDRMIMOGUI { background-color: rgb(64, 64, 64); }"); m_helpURL = "plugins/samplemimo/limesdrmimo/readme.md"; m_limeSDRMIMO = (LimeSDRMIMO*) m_deviceUISet->m_deviceAPI->getSampleMIMO(); diff --git a/plugins/samplemimo/metismiso/metismisogui.cpp b/plugins/samplemimo/metismiso/metismisogui.cpp index e359fae7a..1dc7aaf60 100644 --- a/plugins/samplemimo/metismiso/metismisogui.cpp +++ b/plugins/samplemimo/metismiso/metismisogui.cpp @@ -59,8 +59,7 @@ MetisMISOGui::MetisMISOGui(DeviceUISet *deviceUISet, QWidget* parent) : ui->setupUi(getContents()); setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); - getContents()->setStyleSheet(QString(tr("#MetisMISOGui { border: 1px solid %1 }") - .arg(palette().highlight().color().darker(115).name()))); + getContents()->setStyleSheet("#MetisMISOGui { background-color: rgb(64, 64, 64); }"); m_helpURL = "plugins/samplemimo/metismiso/readme.md"; ui->centerFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold)); ui->centerFrequency->setValueRange(7, 0, m_absMaxFreq); diff --git a/plugins/samplemimo/plutosdrmimo/plutosdrmimogui.cpp b/plugins/samplemimo/plutosdrmimo/plutosdrmimogui.cpp index cd4e12f49..e95a61e65 100644 --- a/plugins/samplemimo/plutosdrmimo/plutosdrmimogui.cpp +++ b/plugins/samplemimo/plutosdrmimo/plutosdrmimogui.cpp @@ -71,8 +71,7 @@ PlutoSDRMIMOGUI::PlutoSDRMIMOGUI(DeviceUISet *deviceUISet, QWidget* parent) : setAttribute(Qt::WA_DeleteOnClose, true); ui->setupUi(getContents()); setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); - getContents()->setStyleSheet(QString(tr("#PlutoSDRMIMOGUI { border: 1px solid %1 }") - .arg(palette().highlight().color().darker(115).name()))); + getContents()->setStyleSheet("#PlutoSDRMIMOGUI { background-color: rgb(64, 64, 64); }"); m_helpURL = "plugins/samplemimo/plutosdrmimo/readme.md"; m_sampleMIMO = (PlutoSDRMIMO*) m_deviceUISet->m_deviceAPI->getSampleMIMO(); diff --git a/plugins/samplemimo/testmi/testmigui.cpp b/plugins/samplemimo/testmi/testmigui.cpp index f5052d6b4..1d4ece70a 100644 --- a/plugins/samplemimo/testmi/testmigui.cpp +++ b/plugins/samplemimo/testmi/testmigui.cpp @@ -64,8 +64,7 @@ TestMIGui::TestMIGui(DeviceUISet *deviceUISet, QWidget* parent) : ui->setupUi(getContents()); setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); - getContents()->setStyleSheet(QString(tr("#TestMIGui { border: 1px solid %1 }") - .arg(palette().highlight().color().darker(115).name()))); + getContents()->setStyleSheet("#TestMIGui { background-color: rgb(64, 64, 64); }"); m_helpURL = "plugins/samplemimo/testmi/readme.md"; ui->spectrumSource->addItem("0"); ui->spectrumSource->addItem("1"); diff --git a/plugins/samplemimo/testmosync/testmosyncgui.cpp b/plugins/samplemimo/testmosync/testmosyncgui.cpp index 68d1be442..abd91236b 100644 --- a/plugins/samplemimo/testmosync/testmosyncgui.cpp +++ b/plugins/samplemimo/testmosync/testmosyncgui.cpp @@ -53,8 +53,7 @@ TestMOSyncGui::TestMOSyncGui(DeviceUISet *deviceUISet, QWidget* parent) : setAttribute(Qt::WA_DeleteOnClose, true); ui->setupUi(getContents()); setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); - getContents()->setStyleSheet(QString(tr("#TestMOSyncGui { border: 1px solid %1 }") - .arg(palette().highlight().color().darker(115).name()))); + getContents()->setStyleSheet("#TestMOSyncGui { background-color: rgb(64, 64, 64); }"); m_helpURL = "plugins/samplemimo/testmosync/readme.md"; m_sampleMIMO = (TestMOSync*) m_deviceUISet->m_deviceAPI->getSampleMIMO(); diff --git a/plugins/samplemimo/xtrxmimo/xtrxmimogui.cpp b/plugins/samplemimo/xtrxmimo/xtrxmimogui.cpp index 1aaab21f3..d31c970ea 100644 --- a/plugins/samplemimo/xtrxmimo/xtrxmimogui.cpp +++ b/plugins/samplemimo/xtrxmimo/xtrxmimogui.cpp @@ -68,8 +68,7 @@ XTRXMIMOGUI::XTRXMIMOGUI(DeviceUISet *deviceUISet, QWidget* parent) : setAttribute(Qt::WA_DeleteOnClose, true); ui->setupUi(getContents()); setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); - getContents()->setStyleSheet(QString(tr("#XTRXMIMOGUI { border: 1px solid %1 }") - .arg(palette().highlight().color().darker(115).name()))); + getContents()->setStyleSheet("#XTRXMIMOGUI { background-color: rgb(64, 64, 64); }"); m_helpURL = "plugins/samplemimo/xtrxmimo/readme.md"; m_xtrxMIMO = (XTRXMIMO*) m_deviceUISet->m_deviceAPI->getSampleMIMO(); diff --git a/plugins/samplesink/audiooutput/audiooutputgui.cpp b/plugins/samplesink/audiooutput/audiooutputgui.cpp index 8e680df1e..cd8c5caf8 100644 --- a/plugins/samplesink/audiooutput/audiooutputgui.cpp +++ b/plugins/samplesink/audiooutput/audiooutputgui.cpp @@ -45,8 +45,7 @@ AudioOutputGui::AudioOutputGui(DeviceUISet *deviceUISet, QWidget* parent) : ui->setupUi(getContents()); setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); - getContents()->setStyleSheet(QString(tr("#AudioOutputGui { border: 1px solid %1 }") - .arg(palette().highlight().color().darker(115).name()))); + getContents()->setStyleSheet("#AudioOutputGui { background-color: rgb(64, 64, 64); }"); m_helpURL = "plugins/samplesink/audiooutput/readme.md"; connect(&m_updateTimer, SIGNAL(timeout()), this, SLOT(updateHardware())); diff --git a/plugins/samplesink/bladerf1output/bladerf1outputgui.cpp b/plugins/samplesink/bladerf1output/bladerf1outputgui.cpp index 8d4218202..bc88fa575 100644 --- a/plugins/samplesink/bladerf1output/bladerf1outputgui.cpp +++ b/plugins/samplesink/bladerf1output/bladerf1outputgui.cpp @@ -48,8 +48,7 @@ Bladerf1OutputGui::Bladerf1OutputGui(DeviceUISet *deviceUISet, QWidget* parent) ui->setupUi(getContents()); setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); - getContents()->setStyleSheet(QString(tr("#Bladerf1OutputGui { border: 1px solid %1 }") - .arg(palette().highlight().color().darker(115).name()))); + getContents()->setStyleSheet("#Bladerf1OutputGui { background-color: rgb(64, 64, 64); }"); m_helpURL = "/plugins/samplesink/bladerf1output/readme.md"; ui->centerFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold)); ui->centerFrequency->setValueRange(7, BLADERF_FREQUENCY_MIN_XB200/1000, BLADERF_FREQUENCY_MAX/1000); diff --git a/plugins/samplesink/bladerf2output/bladerf2outputgui.cpp b/plugins/samplesink/bladerf2output/bladerf2outputgui.cpp index 52585235e..19377ef2b 100644 --- a/plugins/samplesink/bladerf2output/bladerf2outputgui.cpp +++ b/plugins/samplesink/bladerf2output/bladerf2outputgui.cpp @@ -51,8 +51,7 @@ BladeRF2OutputGui::BladeRF2OutputGui(DeviceUISet *deviceUISet, QWidget* parent) ui->setupUi(getContents()); setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); - getContents()->setStyleSheet(QString(tr("#BladeRF2OutputGui { border: 1px solid %1 }") - .arg(palette().highlight().color().darker(115).name()))); + getContents()->setStyleSheet("#BladeRF2OutputGui { background-color: rgb(64, 64, 64); }"); m_helpURL = "plugins/samplesink/bladerf2output/readme.md"; ui->centerFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold)); m_sampleSink->getFrequencyRange(f_min, f_max, step, scale); diff --git a/plugins/samplesink/fileoutput/fileoutputgui.cpp b/plugins/samplesink/fileoutput/fileoutputgui.cpp index 9a52e7a06..37c443db6 100644 --- a/plugins/samplesink/fileoutput/fileoutputgui.cpp +++ b/plugins/samplesink/fileoutput/fileoutputgui.cpp @@ -56,8 +56,7 @@ FileOutputGui::FileOutputGui(DeviceUISet *deviceUISet, QWidget* parent) : setAttribute(Qt::WA_DeleteOnClose, true); ui->setupUi(getContents()); setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); - getContents()->setStyleSheet(QString(tr("#FileOutputGui { border: 1px solid %1 }") - .arg(palette().highlight().color().darker(115).name()))); + getContents()->setStyleSheet("#FileOutputGui { background-color: rgb(64, 64, 64); }"); m_helpURL = "plugins/samplesink/fileoutput/readme.md"; ui->centerFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold)); diff --git a/plugins/samplesink/hackrfoutput/hackrfoutputgui.cpp b/plugins/samplesink/hackrfoutput/hackrfoutputgui.cpp index 6700e804a..1bd613033 100644 --- a/plugins/samplesink/hackrfoutput/hackrfoutputgui.cpp +++ b/plugins/samplesink/hackrfoutput/hackrfoutputgui.cpp @@ -50,8 +50,7 @@ HackRFOutputGui::HackRFOutputGui(DeviceUISet *deviceUISet, QWidget* parent) : ui->setupUi(getContents()); setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); - getContents()->setStyleSheet(QString(tr("#HackRFOutputGui { border: 1px solid %1 }") - .arg(palette().highlight().color().darker(115).name()))); + getContents()->setStyleSheet("#HackRFOutputGui { background-color: rgb(64, 64, 64); }"); m_helpURL = "plugins/samplesink/hackrfoutput/readme.md"; ui->centerFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold)); ui->centerFrequency->setValueRange(7, 0U, 7250000U); diff --git a/plugins/samplesink/limesdroutput/limesdroutputgui.cpp b/plugins/samplesink/limesdroutput/limesdroutputgui.cpp index 9acb4ed60..77c709329 100644 --- a/plugins/samplesink/limesdroutput/limesdroutputgui.cpp +++ b/plugins/samplesink/limesdroutput/limesdroutputgui.cpp @@ -47,8 +47,7 @@ LimeSDROutputGUI::LimeSDROutputGUI(DeviceUISet *deviceUISet, QWidget* parent) : ui->setupUi(getContents()); setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); - getContents()->setStyleSheet(QString(tr("#LimeSDROutputGUI { border: 1px solid %1 }") - .arg(palette().highlight().color().darker(115).name()))); + getContents()->setStyleSheet("#LimeSDROutputGUI { background-color: rgb(64, 64, 64); }"); m_helpURL = "plugins/samplesink/limesdroutput/readme.md"; float minF, maxF; diff --git a/plugins/samplesink/localoutput/localoutputgui.cpp b/plugins/samplesink/localoutput/localoutputgui.cpp index a1c376e97..7eb1035d8 100644 --- a/plugins/samplesink/localoutput/localoutputgui.cpp +++ b/plugins/samplesink/localoutput/localoutputgui.cpp @@ -64,8 +64,7 @@ LocalOutputGui::LocalOutputGui(DeviceUISet *deviceUISet, QWidget* parent) : ui->setupUi(getContents()); setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); - getContents()->setStyleSheet(QString(tr("#LocalOutputGui { border: 1px solid %1 }") - .arg(palette().highlight().color().darker(115).name()))); + getContents()->setStyleSheet("#LocalOutputGui { background-color: rgb(64, 64, 64); }"); m_helpURL = "plugins/samplesink/localoutput/readme.md"; connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &))); diff --git a/plugins/samplesink/plutosdroutput/plutosdroutputgui.cpp b/plugins/samplesink/plutosdroutput/plutosdroutputgui.cpp index 5140da0ff..fff366918 100644 --- a/plugins/samplesink/plutosdroutput/plutosdroutputgui.cpp +++ b/plugins/samplesink/plutosdroutput/plutosdroutputgui.cpp @@ -50,8 +50,7 @@ PlutoSDROutputGUI::PlutoSDROutputGUI(DeviceUISet *deviceUISet, QWidget* parent) ui->setupUi(getContents()); setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); - getContents()->setStyleSheet(QString(tr("#PlutoSDROutputGUI { border: 1px solid %1 }") - .arg(palette().highlight().color().darker(115).name()))); + getContents()->setStyleSheet("#PlutoSDROutputGUI { background-color: rgb(64, 64, 64); }"); m_helpURL = "plugins/samplesink/plutosdroutput/readme.md"; ui->centerFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold)); updateFrequencyLimits(); diff --git a/plugins/samplesink/remoteoutput/remoteoutputgui.cpp b/plugins/samplesink/remoteoutput/remoteoutputgui.cpp index 3fb588ca4..13c3b809d 100644 --- a/plugins/samplesink/remoteoutput/remoteoutputgui.cpp +++ b/plugins/samplesink/remoteoutput/remoteoutputgui.cpp @@ -69,8 +69,7 @@ RemoteOutputSinkGui::RemoteOutputSinkGui(DeviceUISet *deviceUISet, QWidget* pare ui->setupUi(getContents()); setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); - getContents()->setStyleSheet(QString(tr("#RemoteOutputGui { border: 1px solid %1 }") - .arg(palette().highlight().color().darker(115).name()))); + getContents()->setStyleSheet("#RemoteOutputGui { background-color: rgb(64, 64, 64); }"); m_helpURL = "plugins/samplesink/remoteoutput/readme.md"; connect(&(m_deviceUISet->m_deviceAPI->getMasterTimer()), SIGNAL(timeout()), this, SLOT(tick())); diff --git a/plugins/samplesink/soapysdroutput/soapysdroutputgui.cpp b/plugins/samplesink/soapysdroutput/soapysdroutputgui.cpp index 427c2af74..c664ac5bc 100644 --- a/plugins/samplesink/soapysdroutput/soapysdroutputgui.cpp +++ b/plugins/samplesink/soapysdroutput/soapysdroutputgui.cpp @@ -61,8 +61,7 @@ SoapySDROutputGui::SoapySDROutputGui(DeviceUISet *deviceUISet, QWidget* parent) m_sampleSink = (SoapySDROutput*) m_deviceUISet->m_deviceAPI->getSampleSink(); ui->setupUi(getContents()); setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding); - getContents()->setStyleSheet(QString(tr("#SoapySDROutputGui { border: 1px solid %1 }") - .arg(palette().highlight().color().darker(115).name()))); + getContents()->setStyleSheet("#SoapySDROutputGui { background-color: rgb(64, 64, 64); }"); m_helpURL = "plugins/samplesink/soapysdroutput/readme.md"; ui->centerFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold)); diff --git a/plugins/samplesink/testsink/testsinkgui.cpp b/plugins/samplesink/testsink/testsinkgui.cpp index c9e99b719..471f6561d 100644 --- a/plugins/samplesink/testsink/testsinkgui.cpp +++ b/plugins/samplesink/testsink/testsinkgui.cpp @@ -53,8 +53,7 @@ TestSinkGui::TestSinkGui(DeviceUISet *deviceUISet, QWidget* parent) : ui->setupUi(getContents()); setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); setMinimumSize(m_MinimumWidth, m_MinimumHeight); - getContents()->setStyleSheet(QString(tr("#TestSinkGui { border: 1px solid %1 }") - .arg(palette().highlight().color().darker(115).name()))); + getContents()->setStyleSheet("#TestSinkGui { background-color: rgb(64, 64, 64); }"); m_helpURL = "plugins/samplesink/testsink/readme.md"; m_sampleSink = (TestSinkOutput*) m_deviceUISet->m_deviceAPI->getSampleSink(); diff --git a/plugins/samplesink/usrpoutput/usrpoutputgui.cpp b/plugins/samplesink/usrpoutput/usrpoutputgui.cpp index aaa57aa14..a67c3eaaa 100644 --- a/plugins/samplesink/usrpoutput/usrpoutputgui.cpp +++ b/plugins/samplesink/usrpoutput/usrpoutputgui.cpp @@ -48,8 +48,7 @@ USRPOutputGUI::USRPOutputGUI(DeviceUISet *deviceUISet, QWidget* parent) : ui->setupUi(getContents()); setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); - getContents()->setStyleSheet(QString(tr("#USRPOutputGUI { border: 1px solid %1 }") - .arg(palette().highlight().color().darker(115).name()))); + getContents()->setStyleSheet("#USRPOutputGUI { background-color: rgb(64, 64, 64); }"); m_helpURL = "plugins/samplesink/usrpoutput/readme.md"; float minF, maxF; diff --git a/plugins/samplesink/xtrxoutput/xtrxoutputgui.cpp b/plugins/samplesink/xtrxoutput/xtrxoutputgui.cpp index 2a034d5ad..f5b8a79c2 100644 --- a/plugins/samplesink/xtrxoutput/xtrxoutputgui.cpp +++ b/plugins/samplesink/xtrxoutput/xtrxoutputgui.cpp @@ -49,8 +49,7 @@ XTRXOutputGUI::XTRXOutputGUI(DeviceUISet *deviceUISet, QWidget* parent) : ui->setupUi(getContents()); setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); - getContents()->setStyleSheet(QString(tr("#XTRXOutputGUI { border: 1px solid %1 }") - .arg(palette().highlight().color().darker(115).name()))); + getContents()->setStyleSheet("#XTRXOutputGUI { background-color: rgb(64, 64, 64); }"); m_helpURL = "plugins/samplesink/xtrxoutput/readme.md"; float minF, maxF, stepF; diff --git a/plugins/samplesource/airspy/airspygui.cpp b/plugins/samplesource/airspy/airspygui.cpp index 09b22bb81..291563abd 100644 --- a/plugins/samplesource/airspy/airspygui.cpp +++ b/plugins/samplesource/airspy/airspygui.cpp @@ -49,8 +49,7 @@ AirspyGui::AirspyGui(DeviceUISet *deviceUISet, QWidget* parent) : ui->setupUi(getContents()); setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); - getContents()->setStyleSheet(QString(tr("#AirspyGui { border: 1px solid %1 }") - .arg(palette().highlight().color().darker(115).name()))); + getContents()->setStyleSheet("#AirspyGui { background-color: rgb(64, 64, 64); }"); ui->centerFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold)); updateFrequencyLimits(); diff --git a/plugins/samplesource/airspyhf/airspyhfgui.cpp b/plugins/samplesource/airspyhf/airspyhfgui.cpp index 3426f0f10..6a0803b75 100644 --- a/plugins/samplesource/airspyhf/airspyhfgui.cpp +++ b/plugins/samplesource/airspyhf/airspyhfgui.cpp @@ -48,8 +48,7 @@ AirspyHFGui::AirspyHFGui(DeviceUISet *deviceUISet, QWidget* parent) : ui->setupUi(getContents()); setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); - getContents()->setStyleSheet(QString(tr("#AirspyHFGui { border: 1px solid %1 }") - .arg(palette().highlight().color().darker(115).name()))); + getContents()->setStyleSheet("#AirspyHFGui { background-color: rgb(64, 64, 64); }"); m_helpURL = "plugins/samplesource/airspyhf/readme.md"; ui->centerFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold)); updateFrequencyLimits(); diff --git a/plugins/samplesource/audioinput/audioinputgui.cpp b/plugins/samplesource/audioinput/audioinputgui.cpp index c0ae6373e..273a5050e 100644 --- a/plugins/samplesource/audioinput/audioinputgui.cpp +++ b/plugins/samplesource/audioinput/audioinputgui.cpp @@ -45,8 +45,7 @@ AudioInputGui::AudioInputGui(DeviceUISet *deviceUISet, QWidget* parent) : ui->setupUi(getContents()); setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); - getContents()->setStyleSheet(QString(tr("#AudioInputGui { border: 1px solid %1 }") - .arg(palette().highlight().color().darker(115).name()))); + getContents()->setStyleSheet("#AudioInputGui { background-color: rgb(64, 64, 64); }"); m_helpURL = "plugins/samplesource/audioinput/readme.md"; connect(&m_updateTimer, SIGNAL(timeout()), this, SLOT(updateHardware())); diff --git a/plugins/samplesource/bladerf1input/bladerf1inputgui.cpp b/plugins/samplesource/bladerf1input/bladerf1inputgui.cpp index 9c4051878..17c948c0d 100644 --- a/plugins/samplesource/bladerf1input/bladerf1inputgui.cpp +++ b/plugins/samplesource/bladerf1input/bladerf1inputgui.cpp @@ -50,8 +50,7 @@ Bladerf1InputGui::Bladerf1InputGui(DeviceUISet *deviceUISet, QWidget* parent) : ui->setupUi(getContents()); setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); - getContents()->setStyleSheet(QString(tr("#Bladerf1InputGui { border: 1px solid %1 }") - .arg(palette().highlight().color().darker(115).name()))); + getContents()->setStyleSheet("#Bladerf1InputGui { background-color: rgb(64, 64, 64); }"); m_helpURL = "plugins/samplesource/bladerf1input/readme.md"; ui->centerFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold)); ui->centerFrequency->setValueRange(7, BLADERF_FREQUENCY_MIN_XB200/1000, BLADERF_FREQUENCY_MAX/1000); diff --git a/plugins/samplesource/bladerf2input/bladerf2inputgui.cpp b/plugins/samplesource/bladerf2input/bladerf2inputgui.cpp index d96c48b2a..fea4b1505 100644 --- a/plugins/samplesource/bladerf2input/bladerf2inputgui.cpp +++ b/plugins/samplesource/bladerf2input/bladerf2inputgui.cpp @@ -53,8 +53,7 @@ BladeRF2InputGui::BladeRF2InputGui(DeviceUISet *deviceUISet, QWidget* parent) : ui->setupUi(getContents()); setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); - getContents()->setStyleSheet(QString(tr("#Bladerf2InputGui { border: 1px solid %1 }") - .arg(palette().highlight().color().darker(115).name()))); + getContents()->setStyleSheet("#Bladerf2InputGui { background-color: rgb(64, 64, 64); }"); m_helpURL = "plugins/samplesource/bladerf2input/readme.md"; m_sampleSource->getFrequencyRange(f_min, f_max, step, scale); diff --git a/plugins/samplesource/fcdpro/fcdprogui.cpp b/plugins/samplesource/fcdpro/fcdprogui.cpp index 638f570da..692165e14 100644 --- a/plugins/samplesource/fcdpro/fcdprogui.cpp +++ b/plugins/samplesource/fcdpro/fcdprogui.cpp @@ -46,8 +46,7 @@ FCDProGui::FCDProGui(DeviceUISet *deviceUISet, QWidget* parent) : ui->setupUi(getContents()); setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); - getContents()->setStyleSheet(QString(tr("#FCDProGui { border: 1px solid %1 }") - .arg(palette().highlight().color().darker(115).name()))); + getContents()->setStyleSheet("#FCDProGui { background-color: rgb(64, 64, 64); }"); m_helpURL = "plugins/samplesource/fcdpro/readme.md"; ui->centerFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold)); updateFrequencyLimits(); diff --git a/plugins/samplesource/fcdproplus/fcdproplusgui.cpp b/plugins/samplesource/fcdproplus/fcdproplusgui.cpp index 3630fd7f4..76d3f6737 100644 --- a/plugins/samplesource/fcdproplus/fcdproplusgui.cpp +++ b/plugins/samplesource/fcdproplus/fcdproplusgui.cpp @@ -47,8 +47,7 @@ FCDProPlusGui::FCDProPlusGui(DeviceUISet *deviceUISet, QWidget* parent) : ui->setupUi(getContents()); setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); - getContents()->setStyleSheet(QString(tr("#FCDProPlusGui { border: 1px solid %1 }") - .arg(palette().highlight().color().darker(115).name()))); + getContents()->setStyleSheet("#FCDProPlusGui { background-color: rgb(64, 64, 64); }"); m_helpURL = "plugins/samplesource/fcdproplus/readme.md"; ui->centerFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold)); diff --git a/plugins/samplesource/fileinput/fileinputgui.cpp b/plugins/samplesource/fileinput/fileinputgui.cpp index 13130ca3b..e59153aeb 100644 --- a/plugins/samplesource/fileinput/fileinputgui.cpp +++ b/plugins/samplesource/fileinput/fileinputgui.cpp @@ -58,8 +58,7 @@ FileInputGUI::FileInputGUI(DeviceUISet *deviceUISet, QWidget* parent) : setAttribute(Qt::WA_DeleteOnClose, true); ui->setupUi(getContents()); setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); - getContents()->setStyleSheet(QString(tr("#FileInputGUI { border: 1px solid %1 }") - .arg(palette().highlight().color().darker(115).name()))); + getContents()->setStyleSheet("#FileInputGUI { background-color: rgb(64, 64, 64); }"); m_helpURL = "plugins/samplesource/fileinput/readme.md"; ui->crcLabel->setStyleSheet("QLabel { background:rgb(79,79,79); }"); diff --git a/plugins/samplesource/hackrfinput/hackrfinputgui.cpp b/plugins/samplesource/hackrfinput/hackrfinputgui.cpp index 6f6adcf02..a692cbce5 100644 --- a/plugins/samplesource/hackrfinput/hackrfinputgui.cpp +++ b/plugins/samplesource/hackrfinput/hackrfinputgui.cpp @@ -51,8 +51,7 @@ HackRFInputGui::HackRFInputGui(DeviceUISet *deviceUISet, QWidget* parent) : ui->setupUi(getContents()); setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); - getContents()->setStyleSheet(QString(tr("#HackRFInputGui { border: 1px solid %1 }") - .arg(palette().highlight().color().darker(115).name()))); + getContents()->setStyleSheet("#HackRFInputGui { background-color: rgb(64, 64, 64); }"); m_helpURL = "plugins/samplesource/hackrfinput/readme.md"; ui->centerFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold)); ui->centerFrequency->setValueRange(7, 0U, 7250000U); diff --git a/plugins/samplesource/kiwisdr/kiwisdrgui.cpp b/plugins/samplesource/kiwisdr/kiwisdrgui.cpp index c6444f519..2ca20b58f 100644 --- a/plugins/samplesource/kiwisdr/kiwisdrgui.cpp +++ b/plugins/samplesource/kiwisdr/kiwisdrgui.cpp @@ -69,8 +69,7 @@ KiwiSDRGui::KiwiSDRGui(DeviceUISet *deviceUISet, QWidget* parent) : ui->setupUi(getContents()); setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); - getContents()->setStyleSheet(QString(tr("#KiwiSDRGui { border: 1px solid %1 }") - .arg(palette().highlight().color().darker(115).name()))); + getContents()->setStyleSheet("#KiwiSDRGui { background-color: rgb(64, 64, 64); }"); m_helpURL = "plugins/samplesource/kiwisdr/readme.md"; ui->centerFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold)); ui->centerFrequency->setValueRange(7, 0, 9999999); diff --git a/plugins/samplesource/limesdrinput/limesdrinputgui.cpp b/plugins/samplesource/limesdrinput/limesdrinputgui.cpp index b515535c5..0a8c5ea4e 100644 --- a/plugins/samplesource/limesdrinput/limesdrinputgui.cpp +++ b/plugins/samplesource/limesdrinput/limesdrinputgui.cpp @@ -51,8 +51,7 @@ LimeSDRInputGUI::LimeSDRInputGUI(DeviceUISet *deviceUISet, QWidget* parent) : ui->setupUi(getContents()); setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); - getContents()->setStyleSheet(QString(tr("#LimeSDRInputGUI { border: 1px solid %1 }") - .arg(palette().highlight().color().darker(115).name()))); + getContents()->setStyleSheet("#LimeSDRInputGUI { background-color: rgb(64, 64, 64); }"); m_helpURL = "plugins/samplesource/limesdrinput/readme.md"; float minF, maxF; diff --git a/plugins/samplesource/localinput/localinputgui.cpp b/plugins/samplesource/localinput/localinputgui.cpp index cab4d3e22..c7d087c6d 100644 --- a/plugins/samplesource/localinput/localinputgui.cpp +++ b/plugins/samplesource/localinput/localinputgui.cpp @@ -77,8 +77,7 @@ LocalInputGui::LocalInputGui(DeviceUISet *deviceUISet, QWidget* parent) : m_startingTimeStampms = 0; ui->setupUi(getContents()); setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); - getContents()->setStyleSheet(QString(tr("#LocalInputGui { border: 1px solid %1 }") - .arg(palette().highlight().color().darker(115).name()))); + getContents()->setStyleSheet("#LocalInputGui { background-color: rgb(64, 64, 64); }"); m_helpURL = "plugins/samplesource/localinput/readme.md"; connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &))); diff --git a/plugins/samplesource/perseus/perseusgui.cpp b/plugins/samplesource/perseus/perseusgui.cpp index 9176fc0a1..917d12a76 100644 --- a/plugins/samplesource/perseus/perseusgui.cpp +++ b/plugins/samplesource/perseus/perseusgui.cpp @@ -46,8 +46,7 @@ PerseusGui::PerseusGui(DeviceUISet *deviceUISet, QWidget* parent) : ui->setupUi(getContents()); setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); - getContents()->setStyleSheet(QString(tr("#PerseusGui { border: 1px solid %1 }") - .arg(palette().highlight().color().darker(115).name()))); + getContents()->setStyleSheet("#PerseusGui { background-color: rgb(64, 64, 64); }"); m_helpURL = "plugins/samplesource/perseus/readme.md"; ui->centerFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold)); updateFrequencyLimits(); diff --git a/plugins/samplesource/plutosdrinput/plutosdrinputgui.cpp b/plugins/samplesource/plutosdrinput/plutosdrinputgui.cpp index 9c7b757ce..01cc68dda 100644 --- a/plugins/samplesource/plutosdrinput/plutosdrinputgui.cpp +++ b/plugins/samplesource/plutosdrinput/plutosdrinputgui.cpp @@ -51,8 +51,7 @@ PlutoSDRInputGui::PlutoSDRInputGui(DeviceUISet *deviceUISet, QWidget* parent) : ui->setupUi(getContents()); setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); - getContents()->setStyleSheet(QString(tr("#PlutoSDRInputGUI { border: 1px solid %1 }") - .arg(palette().highlight().color().darker(115).name()))); + getContents()->setStyleSheet("#PlutoSDRInputGUI { background-color: rgb(64, 64, 64); }"); m_helpURL = "plugins/samplesource/plutosdrinput/readme.md"; ui->centerFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold)); updateFrequencyLimits(); diff --git a/plugins/samplesource/remoteinput/remoteinputgui.cpp b/plugins/samplesource/remoteinput/remoteinputgui.cpp index 40e0d6267..99022c146 100644 --- a/plugins/samplesource/remoteinput/remoteinputgui.cpp +++ b/plugins/samplesource/remoteinput/remoteinputgui.cpp @@ -74,8 +74,7 @@ RemoteInputGui::RemoteInputGui(DeviceUISet *deviceUISet, QWidget* parent) : m_startingTimeStampms = 0; ui->setupUi(getContents()); setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); - getContents()->setStyleSheet(QString(tr("#RemoteInputGui { border: 1px solid %1 }") - .arg(palette().highlight().color().darker(115).name()))); + getContents()->setStyleSheet("#RemoteInputGui { background-color: rgb(64, 64, 64); }"); m_helpURL = "plugins/samplesource/remoteinput/readme.md"; ui->remoteDeviceFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold)); diff --git a/plugins/samplesource/rtlsdr/rtlsdrgui.cpp b/plugins/samplesource/rtlsdr/rtlsdrgui.cpp index cd8e7b588..37b273e2b 100644 --- a/plugins/samplesource/rtlsdr/rtlsdrgui.cpp +++ b/plugins/samplesource/rtlsdr/rtlsdrgui.cpp @@ -48,8 +48,7 @@ RTLSDRGui::RTLSDRGui(DeviceUISet *deviceUISet, QWidget* parent) : ui->setupUi(getContents()); setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); - getContents()->setStyleSheet(QString(tr("#RTLSDRGui { border: 1px solid %1 }") - .arg(palette().highlight().color().darker(115).name()))); + getContents()->setStyleSheet("#RTLSDRGui { background-color: rgb(64, 64, 64); }"); m_helpURL = "plugins/samplesource/rtlsdr/readme.md"; ui->centerFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold)); updateFrequencyLimits(); diff --git a/plugins/samplesource/sdrplay/sdrplaygui.cpp b/plugins/samplesource/sdrplay/sdrplaygui.cpp index 9a82aec84..dd8ce5172 100644 --- a/plugins/samplesource/sdrplay/sdrplaygui.cpp +++ b/plugins/samplesource/sdrplay/sdrplaygui.cpp @@ -45,8 +45,7 @@ SDRPlayGui::SDRPlayGui(DeviceUISet *deviceUISet, QWidget* parent) : ui->setupUi(getContents()); setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); - getContents()->setStyleSheet(QString(tr("#SDRPlayGui { border: 1px solid %1 }") - .arg(palette().highlight().color().darker(115).name()))); + getContents()->setStyleSheet("#SDRPlayGui { background-color: rgb(64, 64, 64); }"); m_helpURL = "plugins/samplesource/sdrplay/readme.md"; ui->centerFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold)); ui->centerFrequency->setValueRange(7, 10U, 12000U); diff --git a/plugins/samplesource/sdrplayv3/sdrplayv3gui.cpp b/plugins/samplesource/sdrplayv3/sdrplayv3gui.cpp index 691c8ce58..a66991a7b 100644 --- a/plugins/samplesource/sdrplayv3/sdrplayv3gui.cpp +++ b/plugins/samplesource/sdrplayv3/sdrplayv3gui.cpp @@ -45,8 +45,7 @@ SDRPlayV3Gui::SDRPlayV3Gui(DeviceUISet *deviceUISet, QWidget* parent) : ui->setupUi(getContents()); setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); - getContents()->setStyleSheet(QString(tr("#SDRPlayV3Gui { border: 1px solid %1 }") - .arg(palette().highlight().color().darker(115).name()))); + getContents()->setStyleSheet("#SDRPlayV3Gui { background-color: rgb(64, 64, 64); }"); m_helpURL = "plugins/samplesource/sdrplayv3/readme.md"; ui->centerFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold)); updateFrequencyLimits(); diff --git a/plugins/samplesource/sigmffileinput/sigmffileinputgui.cpp b/plugins/samplesource/sigmffileinput/sigmffileinputgui.cpp index 69ad7658c..07e165b06 100644 --- a/plugins/samplesource/sigmffileinput/sigmffileinputgui.cpp +++ b/plugins/samplesource/sigmffileinput/sigmffileinputgui.cpp @@ -64,8 +64,7 @@ SigMFFileInputGUI::SigMFFileInputGUI(DeviceUISet *deviceUISet, QWidget* parent) setAttribute(Qt::WA_DeleteOnClose, true); ui->setupUi(getContents()); setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding); - getContents()->setStyleSheet(QString(tr("#SigMFFileInputGUI { border: 1px solid %1 }") - .arg(palette().highlight().color().darker(115).name()))); + getContents()->setStyleSheet("#SigMFFileInputGUI { background-color: rgb(64, 64, 64); }"); m_helpURL = "plugins/samplesource/sigmffileinput/readme.md"; ui->fileNameText->setText(m_metaFileName); diff --git a/plugins/samplesource/soapysdrinput/soapysdrinputgui.cpp b/plugins/samplesource/soapysdrinput/soapysdrinputgui.cpp index 6e3337ed7..ecade848c 100644 --- a/plugins/samplesource/soapysdrinput/soapysdrinputgui.cpp +++ b/plugins/samplesource/soapysdrinput/soapysdrinputgui.cpp @@ -63,8 +63,7 @@ SoapySDRInputGui::SoapySDRInputGui(DeviceUISet *deviceUISet, QWidget* parent) : m_sampleSource = (SoapySDRInput*) m_deviceUISet->m_deviceAPI->getSampleSource(); ui->setupUi(getContents()); setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding); - getContents()->setStyleSheet(QString(tr("#SoapySDRInputGui { border: 1px solid %1 }") - .arg(palette().highlight().color().darker(115).name()))); + getContents()->setStyleSheet("#SoapySDRInputGui { background-color: rgb(64, 64, 64); }"); m_helpURL = "plugins/samplesource/soapysdrinput/readme.md"; ui->centerFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold)); diff --git a/plugins/samplesource/testsource/testsourcegui.cpp b/plugins/samplesource/testsource/testsourcegui.cpp index a97f72de4..dda83fc7d 100644 --- a/plugins/samplesource/testsource/testsourcegui.cpp +++ b/plugins/samplesource/testsource/testsourcegui.cpp @@ -56,8 +56,7 @@ TestSourceGui::TestSourceGui(DeviceUISet *deviceUISet, QWidget* parent) : ui->setupUi(getContents()); setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); - getContents()->setStyleSheet(QString(tr("#TestSourceGui { border: 1px solid %1 }") - .arg(palette().highlight().color().darker(115).name()))); + getContents()->setStyleSheet("#TestSourceGui { background-color: rgb(64, 64, 64); }"); m_helpURL = "plugins/samplesource/testsource/readme.md"; ui->centerFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold)); ui->centerFrequency->setValueRange(7, 0, 9999999); diff --git a/plugins/samplesource/usrpinput/usrpinputgui.cpp b/plugins/samplesource/usrpinput/usrpinputgui.cpp index 77ee8fb0e..5b7830843 100644 --- a/plugins/samplesource/usrpinput/usrpinputgui.cpp +++ b/plugins/samplesource/usrpinput/usrpinputgui.cpp @@ -52,8 +52,7 @@ USRPInputGUI::USRPInputGUI(DeviceUISet *deviceUISet, QWidget* parent) : ui->setupUi(getContents()); setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); - getContents()->setStyleSheet(QString(tr("#USRPInputGUI { border: 1px solid %1 }") - .arg(palette().highlight().color().darker(115).name()))); + getContents()->setStyleSheet("#USRPInputGUI { background-color: rgb(64, 64, 64); }"); m_helpURL = "plugins/samplesource/usrpinput/readme.md"; float minF, maxF; diff --git a/plugins/samplesource/xtrxinput/xtrxinputgui.cpp b/plugins/samplesource/xtrxinput/xtrxinputgui.cpp index 63f4096dd..2a137e6db 100644 --- a/plugins/samplesource/xtrxinput/xtrxinputgui.cpp +++ b/plugins/samplesource/xtrxinput/xtrxinputgui.cpp @@ -52,8 +52,7 @@ XTRXInputGUI::XTRXInputGUI(DeviceUISet *deviceUISet, QWidget* parent) : ui->setupUi(getContents()); setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); - getContents()->setStyleSheet(QString(tr("#XTRXInputGUI { border: 1px solid %1 }") - .arg(palette().highlight().color().darker(115).name()))); + getContents()->setStyleSheet("#XTRXInputGUI { background-color: rgb(64, 64, 64); }"); m_helpURL = "plugins/samplesource/xtrxinput/readme.md"; float minF, maxF, stepF; diff --git a/sdrgui/device/devicegui.cpp b/sdrgui/device/devicegui.cpp index b0a24260f..2b5d2cc5f 100644 --- a/sdrgui/device/devicegui.cpp +++ b/sdrgui/device/devicegui.cpp @@ -42,6 +42,10 @@ DeviceGUI::DeviceGUI(QWidget *parent) : { qDebug("DeviceGUI::DeviceGUI: %p", parent); setWindowFlags(windowFlags() | Qt::FramelessWindowHint); + setObjectName("DeviceGUI"); + setStyleSheet(QString(tr("#DeviceGUI { border: 1px solid %1; background-color: %2; }") + .arg(palette().highlight().color().darker(115).name())) + .arg(palette().dark().color().darker(115).name())); m_indexLabel = new QLabel(); m_indexLabel->setFixedSize(32, 16); @@ -129,8 +133,8 @@ DeviceGUI::DeviceGUI(QWidget *parent) : m_showAllChannelsButton->setToolTip("Show all channels"); m_layouts = new QVBoxLayout(); - m_layouts->setContentsMargins(m_resizer.m_gripSize, 4, m_resizer.m_gripSize, 4); - m_layouts->setSpacing(2); + m_layouts->setContentsMargins(m_resizer.m_gripSize, m_resizer.m_gripSize, m_resizer.m_gripSize, m_resizer.m_gripSize); + m_layouts->setSpacing(0); m_topLayout = new QHBoxLayout(); m_topLayout->setContentsMargins(0, 0, 0, 0); @@ -148,6 +152,7 @@ DeviceGUI::DeviceGUI(QWidget *parent) : m_topLayout->addWidget(m_closeButton); m_centerLayout = new QHBoxLayout(); + m_centerLayout->setContentsMargins(0, 0, 0, 0); m_contents = new QWidget(); // Do not delete! Done in child's destructor with "delete ui" m_centerLayout->addWidget(m_contents); @@ -158,6 +163,7 @@ DeviceGUI::DeviceGUI(QWidget *parent) : m_bottomLayout->addWidget(m_statusLabel); m_sizeGripBottomRight = new QSizeGrip(this); m_sizeGripBottomRight->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + m_sizeGripBottomRight->setFixedHeight(20); // m_bottomLayout->addStretch(1); m_bottomLayout->addWidget(m_sizeGripBottomRight, 0, Qt::AlignBottom | Qt::AlignRight); diff --git a/sdrgui/device/devicegui.h b/sdrgui/device/devicegui.h index 69831084b..1417ca8a8 100644 --- a/sdrgui/device/devicegui.h +++ b/sdrgui/device/devicegui.h @@ -83,6 +83,7 @@ protected: void mouseReleaseEvent(QMouseEvent* event) override; void mouseMoveEvent(QMouseEvent* event) override; void resetContextMenuType() { m_contextMenuType = ContextMenuNone; } + int getAdditionalHeight() const { return 25 + 22; } DeviceType m_deviceType; int m_deviceSetIndex; From e285be84ffb2de01d1e195c698613895b058ca55 Mon Sep 17 00:00:00 2001 From: f4exb Date: Sun, 24 Apr 2022 02:48:10 +0200 Subject: [PATCH 059/115] Massive UI revamping (v7): Main spectrum: set the border on the outer side of the window. Part of #1213 --- sdrgui/mainspectrum/mainspectrumgui.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sdrgui/mainspectrum/mainspectrumgui.cpp b/sdrgui/mainspectrum/mainspectrumgui.cpp index d87da6e9e..f0424c16c 100644 --- a/sdrgui/mainspectrum/mainspectrumgui.cpp +++ b/sdrgui/mainspectrum/mainspectrumgui.cpp @@ -42,6 +42,10 @@ MainSpectrumGUI::MainSpectrumGUI(GLSpectrum *spectrum, GLSpectrumGUI *spectrumGU qDebug("MainSpectrumGUI::MainSpectrumGUI: %p", parent); setWindowFlags(windowFlags() | Qt::FramelessWindowHint); m_helpURL = "sdrgui/mainspectrum/readme.md"; + setObjectName("MainSpectrumGUI"); + setStyleSheet(QString(tr("#MainSpectrumGUI { border: 1px solid %1; background-color: %2; }") + .arg(palette().highlight().color().darker(115).name())) + .arg(palette().window().color().name())); setMinimumSize(m_MinimumWidth, m_MinimumHeight); @@ -118,6 +122,7 @@ MainSpectrumGUI::MainSpectrumGUI(GLSpectrum *spectrum, GLSpectrumGUI *spectrumGU m_bottomLayout->addWidget(m_statusLabel); m_sizeGripBottomRight = new QSizeGrip(this); m_sizeGripBottomRight->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + m_sizeGripBottomRight->setFixedHeight(10); //m_bottomLayout->addStretch(1); m_bottomLayout->addWidget(m_sizeGripBottomRight, 0, Qt::AlignBottom | Qt::AlignRight); From 360b8a9753736a87864580a77f98d55e67175bf6 Mon Sep 17 00:00:00 2001 From: f4exb Date: Sun, 24 Apr 2022 12:28:56 +0200 Subject: [PATCH 060/115] Massive UI revamping (v7): Fixed sizing of channel windows. Part of #1209 --- .../beamsteeringcwmodgui.cpp | 19 ++++-- .../beamsteeringcwmod/beamsteeringcwmodgui.h | 3 + .../interferometer/interferometergui.cpp | 23 +++++-- .../channelrx/chanalyzer/chanalyzergui.cpp | 24 +++++-- plugins/channelrx/demodadsb/adsbdemodgui.cpp | 24 +++++-- plugins/channelrx/demodais/aisdemodgui.cpp | 24 +++++-- plugins/channelrx/demodapt/aptdemodgui.cpp | 24 +++++-- plugins/channelrx/demodatv/atvdemodgui.cpp | 19 ++++-- plugins/channelrx/demodatv/atvdemodgui.h | 3 + plugins/channelrx/demodbfm/bfmdemodgui.cpp | 24 +++++-- .../demodchirpchat/chirpchatdemodgui.cpp | 23 +++++-- plugins/channelrx/demoddab/dabdemodgui.cpp | 24 +++++-- plugins/channelrx/demoddatv/datvdemodgui.cpp | 21 ++++-- plugins/channelrx/demoddatv/datvdemodgui.h | 3 + .../channelrx/demodfreedv/freedvdemodgui.cpp | 23 +++++-- .../channelrx/demodpacket/packetdemodgui.cpp | 24 +++++-- .../channelrx/demodpager/pagerdemodgui.cpp | 24 +++++-- .../demodradiosonde/radiosondedemodgui.cpp | 24 +++++-- plugins/channelrx/demodvor/vordemodgui.cpp | 23 +++++-- .../channelrx/demodvorsc/vordemodscgui.cpp | 20 ++++-- plugins/channelrx/demodvorsc/vordemodscgui.h | 3 + plugins/channelrx/demodvorsc/vordemodscgui.ui | 24 ++++--- plugins/channelrx/filesink/filesinkgui.cpp | 23 +++++-- plugins/channelrx/filesink/filesinkgui.ui | 45 ++++++++----- .../channelrx/freqtracker/freqtrackergui.ui | 54 ++++++++++------ plugins/channelrx/localsink/localsinkgui.cpp | 19 ++++-- plugins/channelrx/localsink/localsinkgui.h | 3 + plugins/channelrx/localsink/localsinkgui.ui | 4 +- .../channelrx/noisefigure/noisefiguregui.cpp | 24 +++++-- .../radioastronomy/radioastronomygui.cpp | 23 +++++-- .../channelrx/radioclock/radioclockgui.cpp | 24 +++++-- .../channelrx/remotesink/remotesinkgui.cpp | 19 ++++-- plugins/channelrx/remotesink/remotesinkgui.h | 3 + plugins/channelrx/remotesink/remotesinkgui.ui | 8 +-- .../sigmffilesink/sigmffilesinkgui.cpp | 23 +++++-- .../sigmffilesink/sigmffilesinkgui.ui | 45 ++++++++----- plugins/channelrx/udpsink/udpsinkgui.cpp | 25 ++++++-- plugins/channelrx/udpsink/udpsinkgui.ui | 62 +++++++++++++++--- .../channeltx/filesource/filesourcegui.cpp | 25 +++++--- plugins/channeltx/filesource/filesourcegui.h | 3 + plugins/channeltx/filesource/filesourcegui.ui | 21 ++++-- .../channeltx/localsource/localsourcegui.cpp | 19 ++++-- .../channeltx/localsource/localsourcegui.h | 3 + .../channeltx/localsource/localsourcegui.ui | 4 +- .../mod802.15.4/ieee_802_15_4_modgui.cpp | 24 +++++-- .../mod802.15.4/ieee_802_15_4_modgui.ui | 42 ++++++------ plugins/channeltx/modais/aismodgui.cpp | 24 +++++-- plugins/channeltx/modais/aismodgui.ui | 64 ++++++++++++++----- plugins/channeltx/modam/ammodgui.cpp | 19 ++++-- plugins/channeltx/modam/ammodgui.h | 3 + plugins/channeltx/modam/ammodgui.ui | 18 ++++-- plugins/channeltx/modatv/atvmodgui.cpp | 19 ++++-- plugins/channeltx/modatv/atvmodgui.h | 3 + plugins/channeltx/modatv/atvmodgui.ui | 16 +++-- .../modchirpchat/chirpchatmodgui.cpp | 20 ++++-- .../channeltx/modchirpchat/chirpchatmodgui.h | 3 + .../channeltx/modchirpchat/chirpchatmodgui.ui | 37 ++++++----- plugins/channeltx/moddatv/datvmodgui.cpp | 19 ++++-- plugins/channeltx/moddatv/datvmodgui.h | 3 + plugins/channeltx/moddatv/datvmodgui.ui | 18 ++++-- plugins/channeltx/modfreedv/freedvmodgui.cpp | 23 +++++-- plugins/channeltx/modfreedv/freedvmodgui.ui | 26 ++++++-- plugins/channeltx/modnfm/nfmmodgui.cpp | 19 ++++-- plugins/channeltx/modnfm/nfmmodgui.h | 3 + plugins/channeltx/modnfm/nfmmodgui.ui | 18 ++++-- plugins/channeltx/modpacket/packetmodgui.cpp | 24 +++++-- plugins/channeltx/modpacket/packetmodgui.ui | 36 +++++++---- plugins/channeltx/modssb/ssbmodgui.cpp | 23 +++++-- plugins/channeltx/modssb/ssbmodgui.ui | 34 ++++++---- plugins/channeltx/modwfm/wfmmodgui.cpp | 19 ++++-- plugins/channeltx/modwfm/wfmmodgui.h | 3 + plugins/channeltx/modwfm/wfmmodgui.ui | 24 ++++--- .../remotesource/remotesourcegui.cpp | 19 ++++-- .../channeltx/remotesource/remotesourcegui.h | 3 + .../channeltx/remotesource/remotesourcegui.ui | 21 ++---- plugins/channeltx/udpsource/udpsourcegui.cpp | 25 ++++++-- plugins/channeltx/udpsource/udpsourcegui.ui | 32 ++++------ 77 files changed, 1146 insertions(+), 440 deletions(-) diff --git a/plugins/channelmimo/beamsteeringcwmod/beamsteeringcwmodgui.cpp b/plugins/channelmimo/beamsteeringcwmod/beamsteeringcwmodgui.cpp index 5c35e4b8c..f54ec2100 100644 --- a/plugins/channelmimo/beamsteeringcwmod/beamsteeringcwmodgui.cpp +++ b/plugins/channelmimo/beamsteeringcwmod/beamsteeringcwmodgui.cpp @@ -16,6 +16,7 @@ /////////////////////////////////////////////////////////////////////////////////// #include +#include #include "device/deviceuiset.h" #include "gui/basicchannelsettingsdialog.h" @@ -61,6 +62,14 @@ bool BeamSteeringCWModGUI::deserialize(const QByteArray& data) } } +void BeamSteeringCWModGUI::resizeEvent(QResizeEvent* size) +{ + int maxWidth = getRollupContents()->maximumWidth(); + int minHeight = getRollupContents()->minimumHeight() + getAdditionalHeight(); + resize(width() < maxWidth ? width() : maxWidth, minHeight); + size->accept(); +} + bool BeamSteeringCWModGUI::handleMessage(const Message& message) { if (BeamSteeringCWMod::MsgBasebandNotification::match(message)) @@ -96,11 +105,13 @@ BeamSteeringCWModGUI::BeamSteeringCWModGUI(PluginAPI* pluginAPI, DeviceUISet *de m_centerFrequency(435000000), m_tickCount(0) { - ui->setupUi(getRollupContents()); - getRollupContents()->arrangeRollups(); setAttribute(Qt::WA_DeleteOnClose, true); - - connect(getRollupContents(), SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + m_helpURL = "plugins/channelmimo/beamSteeringcwmod/readme.md"; + RollupContents *rollupContents = getRollupContents(); + ui->setupUi(rollupContents); + setSizePolicy(rollupContents->sizePolicy()); + rollupContents->arrangeRollups(); + connect(rollupContents, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &))); m_bsCWSource = (BeamSteeringCWMod*) mimoChannel; diff --git a/plugins/channelmimo/beamsteeringcwmod/beamsteeringcwmodgui.h b/plugins/channelmimo/beamsteeringcwmod/beamsteeringcwmodgui.h index 984eb4dd4..b515573b1 100644 --- a/plugins/channelmimo/beamsteeringcwmod/beamsteeringcwmodgui.h +++ b/plugins/channelmimo/beamsteeringcwmod/beamsteeringcwmodgui.h @@ -93,6 +93,9 @@ private: void applyInterpolation(); void applyPosition(); +protected: + void resizeEvent(QResizeEvent* size); + private slots: void handleSourceMessages(); void on_channelOutput_currentIndexChanged(int index); diff --git a/plugins/channelmimo/interferometer/interferometergui.cpp b/plugins/channelmimo/interferometer/interferometergui.cpp index 8ab582f62..d97dfb80d 100644 --- a/plugins/channelmimo/interferometer/interferometergui.cpp +++ b/plugins/channelmimo/interferometer/interferometergui.cpp @@ -106,11 +106,13 @@ InterferometerGUI::InterferometerGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUI m_centerFrequency(435000000), m_tickCount(0) { - ui->setupUi(getRollupContents()); - getRollupContents()->arrangeRollups(); setAttribute(Qt::WA_DeleteOnClose, true); - - connect(getRollupContents(), SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + m_helpURL = "plugins/channelmimo/interferometer/readme.md"; + RollupContents *rollupContents = getRollupContents(); + ui->setupUi(rollupContents); + setSizePolicy(rollupContents->sizePolicy()); + rollupContents->arrangeRollups(); + connect(rollupContents, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &))); m_interferometer = (Interferometer*) channelMIMO; @@ -248,7 +250,18 @@ void InterferometerGUI::onWidgetRolled(QWidget* widget, bool rollDown) (void) widget; (void) rollDown; - getRollupContents()->saveState(m_rollupState); + RollupContents *rollupContents = getRollupContents(); + + if (rollupContents->hasExpandableWidgets()) { + setSizePolicy(sizePolicy().horizontalPolicy(), QSizePolicy::Expanding); + } else { + setSizePolicy(sizePolicy().horizontalPolicy(), QSizePolicy::Fixed); + } + + int h = rollupContents->height() + getAdditionalHeight(); + resize(width(), h); + + rollupContents->saveState(m_rollupState); applySettings(); } diff --git a/plugins/channelrx/chanalyzer/chanalyzergui.cpp b/plugins/channelrx/chanalyzer/chanalyzergui.cpp index aa5289e1e..ce061c88a 100644 --- a/plugins/channelrx/chanalyzer/chanalyzergui.cpp +++ b/plugins/channelrx/chanalyzer/chanalyzergui.cpp @@ -458,7 +458,18 @@ void ChannelAnalyzerGUI::onWidgetRolled(QWidget* widget, bool rollDown) (void) widget; (void) rollDown; - getRollupContents()->saveState(m_rollupState); + RollupContents *rollupContents = getRollupContents(); + + if (rollupContents->hasExpandableWidgets()) { + setSizePolicy(sizePolicy().horizontalPolicy(), QSizePolicy::Expanding); + } else { + setSizePolicy(sizePolicy().horizontalPolicy(), QSizePolicy::Fixed); + } + + int h = rollupContents->height() + getAdditionalHeight(); + resize(width(), h); + + rollupContents->saveState(m_rollupState); applySettings(); } @@ -518,12 +529,13 @@ ChannelAnalyzerGUI::ChannelAnalyzerGUI(PluginAPI* pluginAPI, DeviceUISet *device m_doApplySettings(true), m_basebandSampleRate(48000) { - ui->setupUi(getRollupContents()); - getRollupContents()->arrangeRollups(); - m_helpURL = "plugins/channelrx/chanalyzer/readme.md"; setAttribute(Qt::WA_DeleteOnClose, true); - - connect(getRollupContents(), SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + m_helpURL = "plugins/channelrx/chanalyzer/readme.md"; + RollupContents *rollupContents = getRollupContents(); + ui->setupUi(rollupContents); + setSizePolicy(rollupContents->sizePolicy()); + rollupContents->arrangeRollups(); + connect(rollupContents, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &))); m_channelAnalyzer = (ChannelAnalyzer*) rxChannel; diff --git a/plugins/channelrx/demodadsb/adsbdemodgui.cpp b/plugins/channelrx/demodadsb/adsbdemodgui.cpp index 4005e67a6..ee16b190d 100644 --- a/plugins/channelrx/demodadsb/adsbdemodgui.cpp +++ b/plugins/channelrx/demodadsb/adsbdemodgui.cpp @@ -2765,7 +2765,18 @@ void ADSBDemodGUI::onWidgetRolled(QWidget* widget, bool rollDown) (void) widget; (void) rollDown; - getRollupContents()->saveState(m_rollupState); + RollupContents *rollupContents = getRollupContents(); + + if (rollupContents->hasExpandableWidgets()) { + setSizePolicy(sizePolicy().horizontalPolicy(), QSizePolicy::Expanding); + } else { + setSizePolicy(sizePolicy().horizontalPolicy(), QSizePolicy::Fixed); + } + + int h = rollupContents->height() + getAdditionalHeight(); + resize(width(), h); + + rollupContents->saveState(m_rollupState); applySettings(); } @@ -3694,9 +3705,13 @@ ADSBDemodGUI::ADSBDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Baseb m_highlightAircraft(nullptr), m_progressDialog(nullptr) { - ui->setupUi(getRollupContents()); - getRollupContents()->arrangeRollups(); + setAttribute(Qt::WA_DeleteOnClose, true); m_helpURL = "plugins/channelrx/demodadsb/readme.md"; + RollupContents *rollupContents = getRollupContents(); + ui->setupUi(rollupContents); + setSizePolicy(rollupContents->sizePolicy()); + rollupContents->arrangeRollups(); + connect(rollupContents, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); m_osmPort = 0; // Pick a free port m_templateServer = new ADSBOSMTemplateServer("q2RVNAe3eFKCH4XsrE3r", m_osmPort); @@ -3707,9 +3722,6 @@ ADSBDemodGUI::ADSBDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Baseb ui->map->rootContext()->setContextProperty("navAidModel", &m_navAidModel); ui->map->setSource(QUrl(QStringLiteral("qrc:/map/map.qml"))); - setAttribute(Qt::WA_DeleteOnClose, true); - - connect(getRollupContents(), SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &))); connect(&m_dlm, &HttpDownloadManager::downloadComplete, this, &ADSBDemodGUI::downloadFinished); diff --git a/plugins/channelrx/demodais/aisdemodgui.cpp b/plugins/channelrx/demodais/aisdemodgui.cpp index c1095e8f5..72946bac4 100644 --- a/plugins/channelrx/demodais/aisdemodgui.cpp +++ b/plugins/channelrx/demodais/aisdemodgui.cpp @@ -382,7 +382,18 @@ void AISDemodGUI::onWidgetRolled(QWidget* widget, bool rollDown) } } - getRollupContents()->saveState(m_rollupState); + RollupContents *rollupContents = getRollupContents(); + + if (rollupContents->hasExpandableWidgets()) { + setSizePolicy(sizePolicy().horizontalPolicy(), QSizePolicy::Expanding); + } else { + setSizePolicy(sizePolicy().horizontalPolicy(), QSizePolicy::Fixed); + } + + int h = rollupContents->height() + getAdditionalHeight(); + resize(width(), h); + + rollupContents->saveState(m_rollupState); applySettings(); } @@ -444,12 +455,13 @@ AISDemodGUI::AISDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Baseban m_doApplySettings(true), m_tickCount(0) { - ui->setupUi(getRollupContents()); - getRollupContents()->arrangeRollups(); - m_helpURL = "plugins/channelrx/demodais/readme.md"; - setAttribute(Qt::WA_DeleteOnClose, true); - connect(getRollupContents(), SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + m_helpURL = "plugins/channelrx/demodais/readme.md"; + RollupContents *rollupContents = getRollupContents(); + ui->setupUi(rollupContents); + setSizePolicy(rollupContents->sizePolicy()); + rollupContents->arrangeRollups(); + connect(rollupContents, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &))); m_aisDemod = reinterpret_cast(rxChannel); diff --git a/plugins/channelrx/demodapt/aptdemodgui.cpp b/plugins/channelrx/demodapt/aptdemodgui.cpp index f7889956a..789459c4e 100644 --- a/plugins/channelrx/demodapt/aptdemodgui.cpp +++ b/plugins/channelrx/demodapt/aptdemodgui.cpp @@ -542,7 +542,18 @@ void APTDemodGUI::onWidgetRolled(QWidget* widget, bool rollDown) (void) widget; (void) rollDown; - getRollupContents()->saveState(m_rollupState); + RollupContents *rollupContents = getRollupContents(); + + if (rollupContents->hasExpandableWidgets()) { + setSizePolicy(sizePolicy().horizontalPolicy(), QSizePolicy::Expanding); + } else { + setSizePolicy(sizePolicy().horizontalPolicy(), QSizePolicy::Fixed); + } + + int h = rollupContents->height() + getAdditionalHeight(); + resize(width(), h); + + rollupContents->saveState(m_rollupState); applySettings(); } @@ -605,12 +616,13 @@ APTDemodGUI::APTDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Baseban m_scene(nullptr), m_pixmapItem(nullptr) { - ui->setupUi(getRollupContents()); - getRollupContents()->arrangeRollups(); - m_helpURL = "plugins/channelrx/demodapt/readme.md"; - setAttribute(Qt::WA_DeleteOnClose, true); - connect(getRollupContents(), SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + m_helpURL = "plugins/channelrx/demodapt/readme.md"; + RollupContents *rollupContents = getRollupContents(); + ui->setupUi(rollupContents); + setSizePolicy(rollupContents->sizePolicy()); + rollupContents->arrangeRollups(); + connect(rollupContents, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &))); m_aptDemod = reinterpret_cast(rxChannel); diff --git a/plugins/channelrx/demodatv/atvdemodgui.cpp b/plugins/channelrx/demodatv/atvdemodgui.cpp index bc0ca5eab..41b819517 100644 --- a/plugins/channelrx/demodatv/atvdemodgui.cpp +++ b/plugins/channelrx/demodatv/atvdemodgui.cpp @@ -18,6 +18,7 @@ #include #include +#include #include "atvdemodgui.h" @@ -76,6 +77,14 @@ bool ATVDemodGUI::deserialize(const QByteArray& data) } } +void ATVDemodGUI::resizeEvent(QResizeEvent* size) +{ + int maxWidth = getRollupContents()->maximumWidth(); + int minHeight = getRollupContents()->minimumHeight() + getAdditionalHeight(); + resize(width() < maxWidth ? width() : maxWidth, minHeight); + size->accept(); +} + void ATVDemodGUI::displaySettings() { m_channelMarker.blockSignals(true); @@ -268,11 +277,13 @@ ATVDemodGUI::ATVDemodGUI(PluginAPI* objPluginAPI, DeviceUISet *deviceUISet, Base m_intTickCount(0), m_basebandSampleRate(48000) { - ui->setupUi(getRollupContents()); - getRollupContents()->arrangeRollups(); - m_helpURL = "plugins/channelrx/demodatv/readme.md"; setAttribute(Qt::WA_DeleteOnClose, true); - connect(getRollupContents(), SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + m_helpURL = "plugins/channelrx/demodatv/readme.md"; + RollupContents *rollupContents = getRollupContents(); + ui->setupUi(rollupContents); + setSizePolicy(rollupContents->sizePolicy()); + rollupContents->arrangeRollups(); + connect(rollupContents, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &))); m_atvDemod = (ATVDemod*) rxChannel; diff --git a/plugins/channelrx/demodatv/atvdemodgui.h b/plugins/channelrx/demodatv/atvdemodgui.h index 663d565a9..d950a0b1e 100644 --- a/plugins/channelrx/demodatv/atvdemodgui.h +++ b/plugins/channelrx/demodatv/atvdemodgui.h @@ -66,6 +66,9 @@ public slots: void channelMarkerChangedByCursor(); void channelMarkerHighlightedByCursor(); +protected: + void resizeEvent(QResizeEvent* size); + private: Ui::ATVDemodGUI* ui; PluginAPI* m_pluginAPI; diff --git a/plugins/channelrx/demodbfm/bfmdemodgui.cpp b/plugins/channelrx/demodbfm/bfmdemodgui.cpp index 9917db345..c486ef29e 100644 --- a/plugins/channelrx/demodbfm/bfmdemodgui.cpp +++ b/plugins/channelrx/demodbfm/bfmdemodgui.cpp @@ -316,7 +316,18 @@ void BFMDemodGUI::onWidgetRolled(QWidget* widget, bool rollDown) (void) widget; (void) rollDown; - getRollupContents()->saveState(m_rollupState); + RollupContents *rollupContents = getRollupContents(); + + if (rollupContents->hasExpandableWidgets()) { + setSizePolicy(sizePolicy().horizontalPolicy(), QSizePolicy::Expanding); + } else { + setSizePolicy(sizePolicy().horizontalPolicy(), QSizePolicy::Fixed); + } + + int h = rollupContents->height() + getAdditionalHeight(); + resize(width(), h); + + rollupContents->saveState(m_rollupState); applySettings(); } @@ -379,9 +390,14 @@ BFMDemodGUI::BFMDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Baseban m_radiotext_AB_flag(false), m_rate(625000) { - ui->setupUi(getRollupContents()); - getRollupContents()->arrangeRollups(); + setAttribute(Qt::WA_DeleteOnClose, true); m_helpURL = "plugins/channelrx/demodbfm/readme.md"; + RollupContents *rollupContents = getRollupContents(); + ui->setupUi(rollupContents); + setSizePolicy(rollupContents->sizePolicy()); + rollupContents->arrangeRollups(); + connect(rollupContents, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + ui->deltaFrequencyLabel->setText(QString("%1f").arg(QChar(0x94, 0x03))); ui->deltaFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold)); ui->deltaFrequency->setValueRange(false, 8, -99999999, 99999999); @@ -390,8 +406,6 @@ BFMDemodGUI::BFMDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Baseban CRightClickEnabler *audioMuteRightClickEnabler = new CRightClickEnabler(ui->audioStereo); connect(audioMuteRightClickEnabler, SIGNAL(rightClick(const QPoint &)), this, SLOT(audioSelect())); - setAttribute(Qt::WA_DeleteOnClose, true); - connect(getRollupContents(), SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &))); connect(getInputMessageQueue(), SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages())); diff --git a/plugins/channelrx/demodchirpchat/chirpchatdemodgui.cpp b/plugins/channelrx/demodchirpchat/chirpchatdemodgui.cpp index 1226ebbae..a2ca6ffd6 100644 --- a/plugins/channelrx/demodchirpchat/chirpchatdemodgui.cpp +++ b/plugins/channelrx/demodchirpchat/chirpchatdemodgui.cpp @@ -329,7 +329,18 @@ void ChirpChatDemodGUI::onWidgetRolled(QWidget* widget, bool rollDown) (void) widget; (void) rollDown; - getRollupContents()->saveState(m_rollupState); + RollupContents *rollupContents = getRollupContents(); + + if (rollupContents->hasExpandableWidgets()) { + setSizePolicy(sizePolicy().horizontalPolicy(), QSizePolicy::Expanding); + } else { + setSizePolicy(sizePolicy().horizontalPolicy(), QSizePolicy::Fixed); + } + + int h = rollupContents->height() + getAdditionalHeight(); + resize(width(), h); + + rollupContents->saveState(m_rollupState); applySettings(); } @@ -391,11 +402,13 @@ ChirpChatDemodGUI::ChirpChatDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUI m_doApplySettings(true), m_tickCount(0) { - ui->setupUi(getRollupContents()); - getRollupContents()->arrangeRollups(); - m_helpURL = "plugins/channelrx/demodchirpchat/readme.md"; setAttribute(Qt::WA_DeleteOnClose, true); - connect(getRollupContents(), SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + m_helpURL = "plugins/channelrx/demodchirpchat/readme.md"; + RollupContents *rollupContents = getRollupContents(); + ui->setupUi(rollupContents); + setSizePolicy(rollupContents->sizePolicy()); + rollupContents->arrangeRollups(); + connect(rollupContents, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &))); m_chirpChatDemod = (ChirpChatDemod*) rxChannel; diff --git a/plugins/channelrx/demoddab/dabdemodgui.cpp b/plugins/channelrx/demoddab/dabdemodgui.cpp index dd8e36f21..b64e9e8a5 100644 --- a/plugins/channelrx/demoddab/dabdemodgui.cpp +++ b/plugins/channelrx/demoddab/dabdemodgui.cpp @@ -392,7 +392,18 @@ void DABDemodGUI::onWidgetRolled(QWidget* widget, bool rollDown) (void) widget; (void) rollDown; - getRollupContents()->saveState(m_rollupState); + RollupContents *rollupContents = getRollupContents(); + + if (rollupContents->hasExpandableWidgets()) { + setSizePolicy(sizePolicy().horizontalPolicy(), QSizePolicy::Expanding); + } else { + setSizePolicy(sizePolicy().horizontalPolicy(), QSizePolicy::Fixed); + } + + int h = rollupContents->height() + getAdditionalHeight(); + resize(width(), h); + + rollupContents->saveState(m_rollupState); applySettings(); } @@ -454,12 +465,13 @@ DABDemodGUI::DABDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Baseban m_tickCount(0), m_channelFreq(0.0) { - ui->setupUi(getRollupContents()); - getRollupContents()->arrangeRollups(); - m_helpURL = "plugins/channelrx/demoddab/readme.md"; - setAttribute(Qt::WA_DeleteOnClose, true); - connect(getRollupContents(), SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + m_helpURL = "plugins/channelrx/demoddab/readme.md"; + RollupContents *rollupContents = getRollupContents(); + ui->setupUi(rollupContents); + setSizePolicy(rollupContents->sizePolicy()); + rollupContents->arrangeRollups(); + connect(rollupContents, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &))); m_dabDemod = reinterpret_cast(rxChannel); diff --git a/plugins/channelrx/demoddatv/datvdemodgui.cpp b/plugins/channelrx/demoddatv/datvdemodgui.cpp index eeb480588..6e5b70674 100644 --- a/plugins/channelrx/demoddatv/datvdemodgui.cpp +++ b/plugins/channelrx/demoddatv/datvdemodgui.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include "device/deviceuiset.h" #include "dsp/dspengine.h" @@ -80,6 +81,14 @@ bool DATVDemodGUI::deserialize(const QByteArray& arrData) } } +void DATVDemodGUI::resizeEvent(QResizeEvent* size) +{ + int maxWidth = getRollupContents()->maximumWidth(); + int minHeight = getRollupContents()->minimumHeight() + getAdditionalHeight(); + resize(width() < maxWidth ? width() : maxWidth, minHeight); + size->accept(); +} + bool DATVDemodGUI::handleMessage(const Message& message) { if (DATVDemodReport::MsgReportModcodCstlnChange::match(message)) @@ -213,13 +222,17 @@ DATVDemodGUI::DATVDemodGUI(PluginAPI* objPluginAPI, DeviceUISet *deviceUISet, Ba m_modcodCodeRateIndex(-1), m_cstlnSetByModcod(false) { - ui->setupUi(getRollupContents()); - getRollupContents()->arrangeRollups(); + setAttribute(Qt::WA_DeleteOnClose, true); m_helpURL = "plugins/channelrx/demoddatv/readme.md"; + RollupContents *rollupContents = getRollupContents(); + ui->setupUi(rollupContents); + setSizePolicy(rollupContents->sizePolicy()); + rollupContents->arrangeRollups(); + connect(rollupContents, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + ui->screenTV->setColor(true); ui->screenTV->resizeTVScreen(256,256); - setAttribute(Qt::WA_DeleteOnClose, true); - connect(getRollupContents(), SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &))); connect(getInputMessageQueue(), SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages())); diff --git a/plugins/channelrx/demoddatv/datvdemodgui.h b/plugins/channelrx/demoddatv/datvdemodgui.h index 34b987c10..c39c5a143 100644 --- a/plugins/channelrx/demoddatv/datvdemodgui.h +++ b/plugins/channelrx/demoddatv/datvdemodgui.h @@ -65,6 +65,9 @@ public: static const char* const m_strChannelID; +protected: + void resizeEvent(QResizeEvent* size); + private slots: void channelMarkerChangedByCursor(); void channelMarkerHighlightedByCursor(); diff --git a/plugins/channelrx/demodfreedv/freedvdemodgui.cpp b/plugins/channelrx/demodfreedv/freedvdemodgui.cpp index 6ea0372a8..2ed6714be 100644 --- a/plugins/channelrx/demodfreedv/freedvdemodgui.cpp +++ b/plugins/channelrx/demodfreedv/freedvdemodgui.cpp @@ -248,7 +248,18 @@ void FreeDVDemodGUI::onWidgetRolled(QWidget* widget, bool rollDown) (void) widget; (void) rollDown; - getRollupContents()->saveState(m_rollupState); + RollupContents *rollupContents = getRollupContents(); + + if (rollupContents->hasExpandableWidgets()) { + setSizePolicy(sizePolicy().horizontalPolicy(), QSizePolicy::Expanding); + } else { + setSizePolicy(sizePolicy().horizontalPolicy(), QSizePolicy::Fixed); + } + + int h = rollupContents->height() + getAdditionalHeight(); + resize(width(), h); + + rollupContents->saveState(m_rollupState); applySettings(); } @@ -268,11 +279,13 @@ FreeDVDemodGUI::FreeDVDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, B m_squelchOpen(false), m_audioSampleRate(-1) { - ui->setupUi(getRollupContents()); - getRollupContents()->arrangeRollups(); - m_helpURL = "plugins/channelrx/demodfreedv/readme.md"; setAttribute(Qt::WA_DeleteOnClose, true); - connect(getRollupContents(), SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + m_helpURL = "plugins/channelrx/demodfreedv/readme.md"; + RollupContents *rollupContents = getRollupContents(); + ui->setupUi(rollupContents); + setSizePolicy(rollupContents->sizePolicy()); + rollupContents->arrangeRollups(); + connect(rollupContents, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &))); m_freeDVDemod = (FreeDVDemod*) rxChannel; diff --git a/plugins/channelrx/demodpacket/packetdemodgui.cpp b/plugins/channelrx/demodpacket/packetdemodgui.cpp index 28bbf01db..e132d0547 100644 --- a/plugins/channelrx/demodpacket/packetdemodgui.cpp +++ b/plugins/channelrx/demodpacket/packetdemodgui.cpp @@ -374,7 +374,18 @@ void PacketDemodGUI::onWidgetRolled(QWidget* widget, bool rollDown) (void) widget; (void) rollDown; - getRollupContents()->saveState(m_rollupState); + RollupContents *rollupContents = getRollupContents(); + + if (rollupContents->hasExpandableWidgets()) { + setSizePolicy(sizePolicy().horizontalPolicy(), QSizePolicy::Expanding); + } else { + setSizePolicy(sizePolicy().horizontalPolicy(), QSizePolicy::Fixed); + } + + int h = rollupContents->height() + getAdditionalHeight(); + resize(width(), h); + + rollupContents->saveState(m_rollupState); applySettings(); } @@ -435,12 +446,13 @@ PacketDemodGUI::PacketDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, B m_doApplySettings(true), m_tickCount(0) { - ui->setupUi(getRollupContents()); - getRollupContents()->arrangeRollups(); - m_helpURL = "plugins/channelrx/demodpacket/readme.md"; - setAttribute(Qt::WA_DeleteOnClose, true); - connect(getRollupContents(), SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + m_helpURL = "plugins/channelrx/demodpacket/readme.md"; + RollupContents *rollupContents = getRollupContents(); + ui->setupUi(rollupContents); + setSizePolicy(rollupContents->sizePolicy()); + rollupContents->arrangeRollups(); + connect(rollupContents, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &))); m_packetDemod = reinterpret_cast(rxChannel); diff --git a/plugins/channelrx/demodpager/pagerdemodgui.cpp b/plugins/channelrx/demodpager/pagerdemodgui.cpp index bbab5548b..8ae79a3a3 100644 --- a/plugins/channelrx/demodpager/pagerdemodgui.cpp +++ b/plugins/channelrx/demodpager/pagerdemodgui.cpp @@ -431,7 +431,18 @@ void PagerDemodGUI::onWidgetRolled(QWidget* widget, bool rollDown) } } - getRollupContents()->saveState(m_rollupState); + RollupContents *rollupContents = getRollupContents(); + + if (rollupContents->hasExpandableWidgets()) { + setSizePolicy(sizePolicy().horizontalPolicy(), QSizePolicy::Expanding); + } else { + setSizePolicy(sizePolicy().horizontalPolicy(), QSizePolicy::Fixed); + } + + int h = rollupContents->height() + getAdditionalHeight(); + resize(width(), h); + + rollupContents->saveState(m_rollupState); applySettings(); } @@ -493,12 +504,13 @@ PagerDemodGUI::PagerDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Bas m_doApplySettings(true), m_tickCount(0) { - ui->setupUi(getRollupContents()); - getRollupContents()->arrangeRollups(); - m_helpURL = "plugins/channelrx/demodpager/readme.md"; - setAttribute(Qt::WA_DeleteOnClose, true); - connect(getRollupContents(), SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + m_helpURL = "plugins/channelrx/demodpager/readme.md"; + RollupContents *rollupContents = getRollupContents(); + ui->setupUi(rollupContents); + setSizePolicy(rollupContents->sizePolicy()); + rollupContents->arrangeRollups(); + connect(rollupContents, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &))); m_pagerDemod = reinterpret_cast(rxChannel); diff --git a/plugins/channelrx/demodradiosonde/radiosondedemodgui.cpp b/plugins/channelrx/demodradiosonde/radiosondedemodgui.cpp index d45f552a4..1c34e1542 100644 --- a/plugins/channelrx/demodradiosonde/radiosondedemodgui.cpp +++ b/plugins/channelrx/demodradiosonde/radiosondedemodgui.cpp @@ -488,7 +488,18 @@ void RadiosondeDemodGUI::onWidgetRolled(QWidget* widget, bool rollDown) } } - getRollupContents()->saveState(m_rollupState); + RollupContents *rollupContents = getRollupContents(); + + if (rollupContents->hasExpandableWidgets()) { + setSizePolicy(sizePolicy().horizontalPolicy(), QSizePolicy::Expanding); + } else { + setSizePolicy(sizePolicy().horizontalPolicy(), QSizePolicy::Fixed); + } + + int h = rollupContents->height() + getAdditionalHeight(); + resize(width(), h); + + rollupContents->saveState(m_rollupState); applySettings(); } @@ -550,12 +561,13 @@ RadiosondeDemodGUI::RadiosondeDemodGUI(PluginAPI* pluginAPI, DeviceUISet *device m_doApplySettings(true), m_tickCount(0) { - ui->setupUi(getRollupContents()); - getRollupContents()->arrangeRollups(); - m_helpURL = "plugins/channelrx/demodradiosonde/readme.md"; - setAttribute(Qt::WA_DeleteOnClose, true); - connect(getRollupContents(), SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + m_helpURL = "plugins/channelrx/demodradiosonde/readme.md"; + RollupContents *rollupContents = getRollupContents(); + ui->setupUi(rollupContents); + setSizePolicy(rollupContents->sizePolicy()); + rollupContents->arrangeRollups(); + connect(rollupContents, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &))); m_radiosondeDemod = reinterpret_cast(rxChannel); diff --git a/plugins/channelrx/demodvor/vordemodgui.cpp b/plugins/channelrx/demodvor/vordemodgui.cpp index 18df17879..14410fb43 100644 --- a/plugins/channelrx/demodvor/vordemodgui.cpp +++ b/plugins/channelrx/demodvor/vordemodgui.cpp @@ -1107,7 +1107,18 @@ void VORDemodGUI::onWidgetRolled(QWidget* widget, bool rollDown) (void) widget; (void) rollDown; - getRollupContents()->saveState(m_rollupState); + RollupContents *rollupContents = getRollupContents(); + + if (rollupContents->hasExpandableWidgets()) { + setSizePolicy(sizePolicy().horizontalPolicy(), QSizePolicy::Expanding); + } else { + setSizePolicy(sizePolicy().horizontalPolicy(), QSizePolicy::Fixed); + } + + int h = rollupContents->height() + getAdditionalHeight(); + resize(width(), h); + + rollupContents->saveState(m_rollupState); applySettings(); } @@ -1172,9 +1183,13 @@ VORDemodGUI::VORDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Baseban m_vorModel(this), m_vors(nullptr) { - ui->setupUi(getRollupContents()); - getRollupContents()->arrangeRollups(); + setAttribute(Qt::WA_DeleteOnClose, true); m_helpURL = "plugins/channelrx/demodvor/readme.md"; + RollupContents *rollupContents = getRollupContents(); + ui->setupUi(rollupContents); + setSizePolicy(rollupContents->sizePolicy()); + rollupContents->arrangeRollups(); + connect(rollupContents, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); ui->map->rootContext()->setContextProperty("vorModel", &m_vorModel); ui->map->setSource(QUrl(QStringLiteral("qrc:/demodvor/map/map.qml"))); @@ -1182,8 +1197,6 @@ VORDemodGUI::VORDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Baseban m_muteIcon.addPixmap(QPixmap("://sound_off.png"), QIcon::Normal, QIcon::On); m_muteIcon.addPixmap(QPixmap("://sound_on.png"), QIcon::Normal, QIcon::Off); - setAttribute(Qt::WA_DeleteOnClose, true); - connect(getRollupContents(), SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &))); connect(&m_dlm, &HttpDownloadManager::downloadComplete, this, &VORDemodGUI::downloadFinished); diff --git a/plugins/channelrx/demodvorsc/vordemodscgui.cpp b/plugins/channelrx/demodvorsc/vordemodscgui.cpp index 2cfe7740e..8cdd2be12 100644 --- a/plugins/channelrx/demodvorsc/vordemodscgui.cpp +++ b/plugins/channelrx/demodvorsc/vordemodscgui.cpp @@ -19,6 +19,7 @@ #include #include +#include #include "device/deviceuiset.h" #include "dsp/dspengine.h" @@ -76,6 +77,14 @@ bool VORDemodSCGUI::deserialize(const QByteArray& data) } } +void VORDemodSCGUI::resizeEvent(QResizeEvent* size) +{ + int maxWidth = getRollupContents()->maximumWidth(); + int minHeight = getRollupContents()->minimumHeight() + getAdditionalHeight(); + resize(width() < maxWidth ? width() : maxWidth, minHeight); + size->accept(); +} + bool VORDemodSCGUI::handleMessage(const Message& message) { if (VORDemodSC::MsgConfigureVORDemod::match(message)) @@ -290,12 +299,13 @@ VORDemodSCGUI::VORDemodSCGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Bas m_squelchOpen(false), m_tickCount(0) { - ui->setupUi(getRollupContents()); - getRollupContents()->arrangeRollups(); - m_helpURL = "plugins/channelrx/demodvorsc/readme.md"; - setAttribute(Qt::WA_DeleteOnClose, true); - connect(getRollupContents(), SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + m_helpURL = "plugins/channelrx/demodvorsc/readme.md"; + RollupContents *rollupContents = getRollupContents(); + ui->setupUi(rollupContents); + setSizePolicy(rollupContents->sizePolicy()); + rollupContents->arrangeRollups(); + connect(rollupContents, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &))); m_vorDemod = reinterpret_cast(rxChannel); diff --git a/plugins/channelrx/demodvorsc/vordemodscgui.h b/plugins/channelrx/demodvorsc/vordemodscgui.h index 058d36c20..53e23bac4 100644 --- a/plugins/channelrx/demodvorsc/vordemodscgui.h +++ b/plugins/channelrx/demodvorsc/vordemodscgui.h @@ -64,6 +64,9 @@ public slots: void channelMarkerChangedByCursor(); void channelMarkerHighlightedByCursor(); +protected: + void resizeEvent(QResizeEvent* size); + private: Ui::VORDemodSCGUI* ui; PluginAPI* m_pluginAPI; diff --git a/plugins/channelrx/demodvorsc/vordemodscgui.ui b/plugins/channelrx/demodvorsc/vordemodscgui.ui index e406cd4c2..2e14ac898 100644 --- a/plugins/channelrx/demodvorsc/vordemodscgui.ui +++ b/plugins/channelrx/demodvorsc/vordemodscgui.ui @@ -7,11 +7,11 @@ 0 0 402 - 189 + 138
    - + 0 0 @@ -22,6 +22,12 @@ 110 + + + 560 + 16777215 + + Liberation Sans @@ -619,25 +625,25 @@
    gui/rollupcontents.h
    1 - - LevelMeterSignalDB - QWidget -
    gui/levelmeter.h
    - 1 -
    ValueDialZ QWidget
    gui/valuedialz.h
    1
    + + LevelMeterSignalDB + QWidget +
    gui/levelmeter.h
    + 1 +
    audioMute - + diff --git a/plugins/channelrx/filesink/filesinkgui.cpp b/plugins/channelrx/filesink/filesinkgui.cpp index e2ab550d1..98ddc362e 100644 --- a/plugins/channelrx/filesink/filesinkgui.cpp +++ b/plugins/channelrx/filesink/filesinkgui.cpp @@ -189,11 +189,13 @@ FileSinkGUI::FileSinkGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Baseban m_fixedPosition(false), m_tickCount(0) { - ui->setupUi(getRollupContents()); - getRollupContents()->arrangeRollups(); - m_helpURL = "plugins/channelrx/filesink/readme.md"; setAttribute(Qt::WA_DeleteOnClose, true); - connect(getRollupContents(), SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + m_helpURL = "plugins/channelrx/filesink/readme.md"; + RollupContents *rollupContents = getRollupContents(); + ui->setupUi(rollupContents); + setSizePolicy(rollupContents->sizePolicy()); + rollupContents->arrangeRollups(); + connect(rollupContents, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &))); m_fileSink = (FileSink*) channelrx; @@ -352,7 +354,18 @@ void FileSinkGUI::onWidgetRolled(QWidget* widget, bool rollDown) (void) widget; (void) rollDown; - getRollupContents()->saveState(m_rollupState); + RollupContents *rollupContents = getRollupContents(); + + if (rollupContents->hasExpandableWidgets()) { + setSizePolicy(sizePolicy().horizontalPolicy(), QSizePolicy::Expanding); + } else { + setSizePolicy(sizePolicy().horizontalPolicy(), QSizePolicy::Fixed); + } + + int h = rollupContents->height() + getAdditionalHeight(); + resize(width(), h); + + rollupContents->saveState(m_rollupState); applySettings(); } diff --git a/plugins/channelrx/filesink/filesinkgui.ui b/plugins/channelrx/filesink/filesinkgui.ui index b109a54f9..55ba0b170 100644 --- a/plugins/channelrx/filesink/filesinkgui.ui +++ b/plugins/channelrx/filesink/filesinkgui.ui @@ -10,6 +10,12 @@ 458
    + + + 0 + 0 + + 552 @@ -508,7 +514,7 @@ - + 0 @@ -517,8 +523,11 @@ 351 - - Channel Spectrum + + + 0 + 0 + @@ -526,9 +535,15 @@ + + + 0 + 0 + + - 0 + 300 300 @@ -553,17 +568,6 @@
    gui/rollupcontents.h
    1 - - ButtonSwitch - QToolButton -
    gui/buttonswitch.h
    -
    - - ValueDialZ - QWidget -
    gui/valuedialz.h
    - 1 -
    GLSpectrum QWidget @@ -576,6 +580,17 @@
    gui/glspectrumgui.h
    1
    + + ValueDialZ + QWidget +
    gui/valuedialz.h
    + 1 +
    + + ButtonSwitch + QToolButton +
    gui/buttonswitch.h
    +
    diff --git a/plugins/channelrx/freqtracker/freqtrackergui.ui b/plugins/channelrx/freqtracker/freqtrackergui.ui index 72ba59785..cc5d7a76f 100644 --- a/plugins/channelrx/freqtracker/freqtrackergui.ui +++ b/plugins/channelrx/freqtracker/freqtrackergui.ui @@ -11,7 +11,7 @@
    - + 0 0 @@ -754,6 +754,12 @@ 161 + + + 0 + 0 + + Channel Spectrum @@ -775,6 +781,18 @@
    + + + 0 + 0 + + + + + 300 + 300 + + Liberation Mono @@ -796,23 +814,6 @@
    gui/rollupcontents.h
    1 - - ButtonSwitch - QToolButton -
    gui/buttonswitch.h
    -
    - - LevelMeterSignalDB - QWidget -
    gui/levelmeter.h
    - 1 -
    - - ValueDialZ - QWidget -
    gui/valuedialz.h
    - 1 -
    GLSpectrum QWidget @@ -825,6 +826,23 @@
    gui/glspectrumgui.h
    1
    + + ValueDialZ + QWidget +
    gui/valuedialz.h
    + 1 +
    + + LevelMeterSignalDB + QWidget +
    gui/levelmeter.h
    + 1 +
    + + ButtonSwitch + QToolButton +
    gui/buttonswitch.h
    +
    diff --git a/plugins/channelrx/localsink/localsinkgui.cpp b/plugins/channelrx/localsink/localsinkgui.cpp index 399cae251..24230a796 100644 --- a/plugins/channelrx/localsink/localsinkgui.cpp +++ b/plugins/channelrx/localsink/localsinkgui.cpp @@ -16,6 +16,7 @@ /////////////////////////////////////////////////////////////////////////////////// #include +#include #include "device/deviceuiset.h" #include "gui/basicchannelsettingsdialog.h" @@ -68,6 +69,14 @@ bool LocalSinkGUI::deserialize(const QByteArray& data) } } +void LocalSinkGUI::resizeEvent(QResizeEvent* size) +{ + int maxWidth = getRollupContents()->maximumWidth(); + int minHeight = getRollupContents()->minimumHeight() + getAdditionalHeight(); + resize(width() < maxWidth ? width() : maxWidth, minHeight); + size->accept(); +} + bool LocalSinkGUI::handleMessage(const Message& message) { if (DSPSignalNotification::match(message)) @@ -105,11 +114,13 @@ LocalSinkGUI::LocalSinkGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Baseb m_basebandSampleRate(0), m_tickCount(0) { - ui->setupUi(getRollupContents()); - getRollupContents()->arrangeRollups(); - m_helpURL = "plugins/channelrx/localsink/readme.md"; setAttribute(Qt::WA_DeleteOnClose, true); - connect(getRollupContents(), SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + m_helpURL = "plugins/channelrx/localsink/readme.md"; + RollupContents *rollupContents = getRollupContents(); + ui->setupUi(rollupContents); + setSizePolicy(rollupContents->sizePolicy()); + rollupContents->arrangeRollups(); + connect(rollupContents, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &))); m_localSink = (LocalSink*) channelrx; diff --git a/plugins/channelrx/localsink/localsinkgui.h b/plugins/channelrx/localsink/localsinkgui.h index 6d678d563..201615245 100644 --- a/plugins/channelrx/localsink/localsinkgui.h +++ b/plugins/channelrx/localsink/localsinkgui.h @@ -60,6 +60,9 @@ public: virtual int getStreamIndex() const { return m_settings.m_streamIndex; } virtual void setStreamIndex(int streamIndex) { m_settings.m_streamIndex = streamIndex; } +protected: + void resizeEvent(QResizeEvent* size); + private: Ui::LocalSinkGUI* ui; PluginAPI* m_pluginAPI; diff --git a/plugins/channelrx/localsink/localsinkgui.ui b/plugins/channelrx/localsink/localsinkgui.ui index dc64076d2..8ebd37b1b 100644 --- a/plugins/channelrx/localsink/localsinkgui.ui +++ b/plugins/channelrx/localsink/localsinkgui.ui @@ -11,7 +11,7 @@
    - + 0 0 @@ -24,7 +24,7 @@ - 320 + 560 16777215 diff --git a/plugins/channelrx/noisefigure/noisefiguregui.cpp b/plugins/channelrx/noisefigure/noisefiguregui.cpp index 765d13515..8c0b1f427 100644 --- a/plugins/channelrx/noisefigure/noisefiguregui.cpp +++ b/plugins/channelrx/noisefigure/noisefiguregui.cpp @@ -537,7 +537,18 @@ void NoiseFigureGUI::onWidgetRolled(QWidget* widget, bool rollDown) (void) widget; (void) rollDown; - getRollupContents()->saveState(m_rollupState); + RollupContents *rollupContents = getRollupContents(); + + if (rollupContents->hasExpandableWidgets()) { + setSizePolicy(sizePolicy().horizontalPolicy(), QSizePolicy::Expanding); + } else { + setSizePolicy(sizePolicy().horizontalPolicy(), QSizePolicy::Fixed); + } + + int h = rollupContents->height() + getAdditionalHeight(); + resize(width(), h); + + rollupContents->saveState(m_rollupState); applySettings(); } @@ -601,12 +612,13 @@ NoiseFigureGUI::NoiseFigureGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, B m_runningTest(false), m_chart(nullptr) { - ui->setupUi(getRollupContents()); - getRollupContents()->arrangeRollups(); - m_helpURL = "plugins/channelrx/noisefigure/readme.md"; - setAttribute(Qt::WA_DeleteOnClose, true); - connect(getRollupContents(), SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + m_helpURL = "plugins/channelrx/noisefigure/readme.md"; + RollupContents *rollupContents = getRollupContents(); + ui->setupUi(rollupContents); + setSizePolicy(rollupContents->sizePolicy()); + rollupContents->arrangeRollups(); + connect(rollupContents, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &))); m_noiseFigure = reinterpret_cast(rxChannel); diff --git a/plugins/channelrx/radioastronomy/radioastronomygui.cpp b/plugins/channelrx/radioastronomy/radioastronomygui.cpp index 5fe80f66a..d37a99def 100644 --- a/plugins/channelrx/radioastronomy/radioastronomygui.cpp +++ b/plugins/channelrx/radioastronomy/radioastronomygui.cpp @@ -1949,7 +1949,18 @@ void RadioAstronomyGUI::onWidgetRolled(QWidget* widget, bool rollDown) (void) widget; (void) rollDown; - getRollupContents()->saveState(m_rollupState); + RollupContents *rollupContents = getRollupContents(); + + if (rollupContents->hasExpandableWidgets()) { + setSizePolicy(sizePolicy().horizontalPolicy(), QSizePolicy::Expanding); + } else { + setSizePolicy(sizePolicy().horizontalPolicy(), QSizePolicy::Fixed); + } + + int h = rollupContents->height() + getAdditionalHeight(); + resize(width(), h); + + rollupContents->saveState(m_rollupState); applySettings(); } @@ -2063,11 +2074,13 @@ RadioAstronomyGUI::RadioAstronomyGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUI m_downloadingLAB(false) { qDebug("RadioAstronomyGUI::RadioAstronomyGUI"); - ui->setupUi(getRollupContents()); - getRollupContents()->arrangeRollups(); - setAttribute(Qt::WA_DeleteOnClose, true); - connect(getRollupContents(), SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + m_helpURL = "plugins/channelrx/radioastronomy/readme.md"; + RollupContents *rollupContents = getRollupContents(); + ui->setupUi(rollupContents); + setSizePolicy(rollupContents->sizePolicy()); + rollupContents->arrangeRollups(); + connect(rollupContents, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &))); m_radioAstronomy = reinterpret_cast(rxChannel); diff --git a/plugins/channelrx/radioclock/radioclockgui.cpp b/plugins/channelrx/radioclock/radioclockgui.cpp index 826736eab..72976c9b0 100644 --- a/plugins/channelrx/radioclock/radioclockgui.cpp +++ b/plugins/channelrx/radioclock/radioclockgui.cpp @@ -222,7 +222,18 @@ void RadioClockGUI::onWidgetRolled(QWidget* widget, bool rollDown) } } - getRollupContents()->saveState(m_rollupState); + RollupContents *rollupContents = getRollupContents(); + + if (rollupContents->hasExpandableWidgets()) { + setSizePolicy(sizePolicy().horizontalPolicy(), QSizePolicy::Expanding); + } else { + setSizePolicy(sizePolicy().horizontalPolicy(), QSizePolicy::Fixed); + } + + int h = rollupContents->height() + getAdditionalHeight(); + resize(width(), h); + + rollupContents->saveState(m_rollupState); applySettings(); } @@ -284,12 +295,13 @@ RadioClockGUI::RadioClockGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Bas m_doApplySettings(true), m_tickCount(0) { - ui->setupUi(getRollupContents()); - getRollupContents()->arrangeRollups(); - m_helpURL = "plugins/channelrx/radioclock/readme.md"; - setAttribute(Qt::WA_DeleteOnClose, true); - connect(getRollupContents(), SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + m_helpURL = "plugins/channelrx/radioclock/readme.md"; + RollupContents *rollupContents = getRollupContents(); + ui->setupUi(rollupContents); + setSizePolicy(rollupContents->sizePolicy()); + rollupContents->arrangeRollups(); + connect(rollupContents, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); m_radioClock = reinterpret_cast(rxChannel); m_radioClock->setMessageQueueToGUI(getInputMessageQueue()); diff --git a/plugins/channelrx/remotesink/remotesinkgui.cpp b/plugins/channelrx/remotesink/remotesinkgui.cpp index eaeae5cb2..fb2a1df70 100644 --- a/plugins/channelrx/remotesink/remotesinkgui.cpp +++ b/plugins/channelrx/remotesink/remotesinkgui.cpp @@ -16,6 +16,7 @@ /////////////////////////////////////////////////////////////////////////////////// #include +#include #include "device/deviceuiset.h" #include "gui/basicchannelsettingsdialog.h" @@ -63,6 +64,14 @@ bool RemoteSinkGUI::deserialize(const QByteArray& data) } } +void RemoteSinkGUI::resizeEvent(QResizeEvent* size) +{ + int maxWidth = getRollupContents()->maximumWidth(); + int minHeight = getRollupContents()->minimumHeight() + getAdditionalHeight(); + resize(width() < maxWidth ? width() : maxWidth, minHeight); + size->accept(); +} + bool RemoteSinkGUI::handleMessage(const Message& message) { if (RemoteSink::MsgConfigureRemoteSink::match(message)) @@ -101,11 +110,13 @@ RemoteSinkGUI::RemoteSinkGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Bas m_deviceCenterFrequency(0), m_tickCount(0) { - ui->setupUi(getRollupContents()); - getRollupContents()->arrangeRollups(); - m_helpURL = "plugins/channelrx/remotesink/readme.md"; setAttribute(Qt::WA_DeleteOnClose, true); - connect(getRollupContents(), SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + m_helpURL = "plugins/channelrx/remotesink/readme.md"; + RollupContents *rollupContents = getRollupContents(); + ui->setupUi(rollupContents); + setSizePolicy(rollupContents->sizePolicy()); + rollupContents->arrangeRollups(); + connect(rollupContents, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &))); m_remoteSink = (RemoteSink*) channelrx; diff --git a/plugins/channelrx/remotesink/remotesinkgui.h b/plugins/channelrx/remotesink/remotesinkgui.h index 441ae61ec..39287de17 100644 --- a/plugins/channelrx/remotesink/remotesinkgui.h +++ b/plugins/channelrx/remotesink/remotesinkgui.h @@ -60,6 +60,9 @@ public: virtual int getStreamIndex() const { return m_settings.m_streamIndex; } virtual void setStreamIndex(int streamIndex) { m_settings.m_streamIndex = streamIndex; } +protected: + void resizeEvent(QResizeEvent* size); + private: Ui::RemoteSinkGUI* ui; PluginAPI* m_pluginAPI; diff --git a/plugins/channelrx/remotesink/remotesinkgui.ui b/plugins/channelrx/remotesink/remotesinkgui.ui index 9f9d3526f..8cdbf64a0 100644 --- a/plugins/channelrx/remotesink/remotesinkgui.ui +++ b/plugins/channelrx/remotesink/remotesinkgui.ui @@ -11,7 +11,7 @@
    - + 0 0 @@ -24,7 +24,7 @@ - 320 + 360 16777215 @@ -40,8 +40,8 @@ - 10 - 10 + 0 + 0 301 141 diff --git a/plugins/channelrx/sigmffilesink/sigmffilesinkgui.cpp b/plugins/channelrx/sigmffilesink/sigmffilesinkgui.cpp index 48c10287e..eca7730ba 100644 --- a/plugins/channelrx/sigmffilesink/sigmffilesinkgui.cpp +++ b/plugins/channelrx/sigmffilesink/sigmffilesinkgui.cpp @@ -175,11 +175,13 @@ SigMFFileSinkGUI::SigMFFileSinkGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISe m_fixedPosition(false), m_tickCount(0) { - ui->setupUi(getRollupContents()); - getRollupContents()->arrangeRollups(); - m_helpURL = "plugins/channelrx/sigmffilesink/readme.md"; setAttribute(Qt::WA_DeleteOnClose, true); - connect(getRollupContents(), SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + m_helpURL = "plugins/channelrx/sigmffilesink/readme.md"; + RollupContents *rollupContents = getRollupContents(); + ui->setupUi(rollupContents); + setSizePolicy(rollupContents->sizePolicy()); + rollupContents->arrangeRollups(); + connect(rollupContents, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &))); m_sigMFFileSink = (SigMFFileSink*) channelrx; @@ -344,7 +346,18 @@ void SigMFFileSinkGUI::onWidgetRolled(QWidget* widget, bool rollDown) (void) widget; (void) rollDown; - getRollupContents()->saveState(m_rollupState); + RollupContents *rollupContents = getRollupContents(); + + if (rollupContents->hasExpandableWidgets()) { + setSizePolicy(sizePolicy().horizontalPolicy(), QSizePolicy::Expanding); + } else { + setSizePolicy(sizePolicy().horizontalPolicy(), QSizePolicy::Fixed); + } + + int h = rollupContents->height() + getAdditionalHeight(); + resize(width(), h); + + rollupContents->saveState(m_rollupState); applySettings(); } diff --git a/plugins/channelrx/sigmffilesink/sigmffilesinkgui.ui b/plugins/channelrx/sigmffilesink/sigmffilesinkgui.ui index b2e55f758..a12c65b03 100644 --- a/plugins/channelrx/sigmffilesink/sigmffilesinkgui.ui +++ b/plugins/channelrx/sigmffilesink/sigmffilesinkgui.ui @@ -10,6 +10,12 @@ 458 + + + 0 + 0 + + 552 @@ -505,7 +511,7 @@ - + 0 @@ -514,8 +520,11 @@ 351 - - Channel Spectrum + + + 0 + 0 + @@ -523,9 +532,15 @@ + + + 0 + 0 + + - 0 + 300 300 @@ -550,17 +565,6 @@
    gui/rollupcontents.h
    1 - - ButtonSwitch - QToolButton -
    gui/buttonswitch.h
    -
    - - ValueDialZ - QWidget -
    gui/valuedialz.h
    - 1 -
    GLSpectrum QWidget @@ -573,6 +577,17 @@
    gui/glspectrumgui.h
    1
    + + ValueDialZ + QWidget +
    gui/valuedialz.h
    + 1 +
    + + ButtonSwitch + QToolButton +
    gui/buttonswitch.h
    +
    diff --git a/plugins/channelrx/udpsink/udpsinkgui.cpp b/plugins/channelrx/udpsink/udpsinkgui.cpp index 8f2269cf4..44bf1b881 100644 --- a/plugins/channelrx/udpsink/udpsinkgui.cpp +++ b/plugins/channelrx/udpsink/udpsinkgui.cpp @@ -160,12 +160,14 @@ UDPSinkGUI::UDPSinkGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandS m_doApplySettings(true), m_rfBandwidthChanged(false) { - ui->setupUi(getRollupContents()); - getRollupContents()->arrangeRollups(); - m_helpURL = "plugins/channelrx/udpsink/readme.md"; - connect(getRollupContents(), SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); - connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &))); setAttribute(Qt::WA_DeleteOnClose, true); + m_helpURL = "plugins/channelrx/udpsink/readme.md"; + RollupContents *rollupContents = getRollupContents(); + ui->setupUi(rollupContents); + setSizePolicy(rollupContents->sizePolicy()); + rollupContents->arrangeRollups(); + connect(rollupContents, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &))); m_udpSink = (UDPSink*) rxChannel; m_spectrumVis = m_udpSink->getSpectrumVis(); @@ -598,7 +600,18 @@ void UDPSinkGUI::onWidgetRolled(QWidget* widget, bool rollDown) m_udpSink->enableSpectrum(rollDown); } - getRollupContents()->saveState(m_rollupState); + RollupContents *rollupContents = getRollupContents(); + + if (rollupContents->hasExpandableWidgets()) { + setSizePolicy(sizePolicy().horizontalPolicy(), QSizePolicy::Expanding); + } else { + setSizePolicy(sizePolicy().horizontalPolicy(), QSizePolicy::Fixed); + } + + int h = rollupContents->height() + getAdditionalHeight(); + resize(width(), h); + + rollupContents->saveState(m_rollupState); applySettings(); } diff --git a/plugins/channelrx/udpsink/udpsinkgui.ui b/plugins/channelrx/udpsink/udpsinkgui.ui index ea438e23c..037195b5c 100644 --- a/plugins/channelrx/udpsink/udpsinkgui.ui +++ b/plugins/channelrx/udpsink/udpsinkgui.ui @@ -10,18 +10,18 @@ 355
    + + + 0 + 0 + + - 342 + 382 0 - - - 400 - 16777215 - - Liberation Sans @@ -183,6 +183,15 @@ + + + + 26 + 26 + 26 + + + @@ -203,6 +212,15 @@ + + + + 26 + 26 + 26 + + + @@ -223,11 +241,21 @@ + + + + 26 + 26 + 26 + + + + Liberation Sans 8 @@ -821,10 +849,16 @@ 15 160 - 231 + 361 156 + + + 0 + 0 + + Channel Spectrum @@ -846,6 +880,18 @@ + + + 0 + 0 + + + + + 300 + 300 + + Liberation Mono diff --git a/plugins/channeltx/filesource/filesourcegui.cpp b/plugins/channeltx/filesource/filesourcegui.cpp index 6bff969f8..eb2b26d06 100644 --- a/plugins/channeltx/filesource/filesourcegui.cpp +++ b/plugins/channeltx/filesource/filesourcegui.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include "device/deviceapi.h" #include "device/deviceuiset.h" @@ -69,6 +70,14 @@ bool FileSourceGUI::deserialize(const QByteArray& data) } } +void FileSourceGUI::resizeEvent(QResizeEvent* size) +{ + int maxWidth = getRollupContents()->maximumWidth(); + int minHeight = getRollupContents()->minimumHeight() + getAdditionalHeight(); + resize(width() < maxWidth ? width() : maxWidth, minHeight); + size->accept(); +} + bool FileSourceGUI::handleMessage(const Message& message) { if (DSPSignalNotification::match(message)) @@ -181,17 +190,17 @@ FileSourceGUI::FileSourceGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Bas m_doApplySettings(true), m_tickCount(0) { - (void) channelTx; - - ui->setupUi(getRollupContents()); - getRollupContents()->arrangeRollups(); - m_helpURL = "plugins/channeltx/filesource/readme.md"; - ui->channelPowerMeter->setColorTheme(LevelMeterSignalDB::ColorGreenAndBlue); - setAttribute(Qt::WA_DeleteOnClose, true); - connect(getRollupContents(), SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + m_helpURL = "plugins/channeltx/filesource/readme.md"; + RollupContents *rollupContents = getRollupContents(); + ui->setupUi(rollupContents); + setSizePolicy(rollupContents->sizePolicy()); + rollupContents->arrangeRollups(); + connect(rollupContents, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &))); + ui->channelPowerMeter->setColorTheme(LevelMeterSignalDB::ColorGreenAndBlue); + m_fileSource = (FileSource*) channelTx; m_fileSource->setMessageQueueToGUI(getInputMessageQueue()); diff --git a/plugins/channeltx/filesource/filesourcegui.h b/plugins/channeltx/filesource/filesourcegui.h index 92c86ec7f..640000b8c 100644 --- a/plugins/channeltx/filesource/filesourcegui.h +++ b/plugins/channeltx/filesource/filesourcegui.h @@ -60,6 +60,9 @@ public: public slots: void channelMarkerChangedByCursor(); +protected: + void resizeEvent(QResizeEvent* size); + private: Ui::FileSourceGUI* ui; PluginAPI* m_pluginAPI; diff --git a/plugins/channeltx/filesource/filesourcegui.ui b/plugins/channeltx/filesource/filesourcegui.ui index 8ae7b0701..43a157b87 100644 --- a/plugins/channeltx/filesource/filesourcegui.ui +++ b/plugins/channeltx/filesource/filesourcegui.ui @@ -11,7 +11,7 @@ - + 0 0 @@ -22,6 +22,12 @@ 140 + + + 560 + 16777215 + + Liberation Sans @@ -416,6 +422,7 @@ + Liberation Sans 9 @@ -447,6 +454,7 @@ + Liberation Sans 9 @@ -472,6 +480,7 @@ + Liberation Sans 9 @@ -701,17 +710,17 @@
    gui/rollupcontents.h
    1 - - ButtonSwitch - QToolButton -
    gui/buttonswitch.h
    -
    LevelMeterSignalDB QWidget
    gui/levelmeter.h
    1
    + + ButtonSwitch + QToolButton +
    gui/buttonswitch.h
    +
    diff --git a/plugins/channeltx/localsource/localsourcegui.cpp b/plugins/channeltx/localsource/localsourcegui.cpp index e18769251..4c028620c 100644 --- a/plugins/channeltx/localsource/localsourcegui.cpp +++ b/plugins/channeltx/localsource/localsourcegui.cpp @@ -16,6 +16,7 @@ /////////////////////////////////////////////////////////////////////////////////// #include +#include #include "device/deviceuiset.h" #include "gui/basicchannelsettingsdialog.h" @@ -63,6 +64,14 @@ bool LocalSourceGUI::deserialize(const QByteArray& data) } } +void LocalSourceGUI::resizeEvent(QResizeEvent* size) +{ + int maxWidth = getRollupContents()->maximumWidth(); + int minHeight = getRollupContents()->minimumHeight() + getAdditionalHeight(); + resize(width() < maxWidth ? width() : maxWidth, minHeight); + size->accept(); +} + bool LocalSourceGUI::handleMessage(const Message& message) { if (DSPSignalNotification::match(message)) @@ -99,11 +108,13 @@ LocalSourceGUI::LocalSourceGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, B m_deviceCenterFrequency(0), m_tickCount(0) { - ui->setupUi(getRollupContents()); - getRollupContents()->arrangeRollups(); - m_helpURL = "plugins/channeltx/localsource/readme.md"; setAttribute(Qt::WA_DeleteOnClose, true); - connect(getRollupContents(), SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + m_helpURL = "plugins/channeltx/localsource/readme.md"; + RollupContents *rollupContents = getRollupContents(); + ui->setupUi(rollupContents); + setSizePolicy(rollupContents->sizePolicy()); + rollupContents->arrangeRollups(); + connect(rollupContents, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &))); m_localSource = (LocalSource*) channeltx; diff --git a/plugins/channeltx/localsource/localsourcegui.h b/plugins/channeltx/localsource/localsourcegui.h index c7a58498e..890874924 100644 --- a/plugins/channeltx/localsource/localsourcegui.h +++ b/plugins/channeltx/localsource/localsourcegui.h @@ -60,6 +60,9 @@ public: virtual int getStreamIndex() const { return m_settings.m_streamIndex; } virtual void setStreamIndex(int streamIndex) { m_settings.m_streamIndex = streamIndex; } +protected: + void resizeEvent(QResizeEvent* size); + private: Ui::LocalSourceGUI* ui; PluginAPI* m_pluginAPI; diff --git a/plugins/channeltx/localsource/localsourcegui.ui b/plugins/channeltx/localsource/localsourcegui.ui index a4e0aa877..e54ccc920 100644 --- a/plugins/channeltx/localsource/localsourcegui.ui +++ b/plugins/channeltx/localsource/localsourcegui.ui @@ -11,7 +11,7 @@ - + 0 0 @@ -24,7 +24,7 @@ - 320 + 560 16777215 diff --git a/plugins/channeltx/mod802.15.4/ieee_802_15_4_modgui.cpp b/plugins/channeltx/mod802.15.4/ieee_802_15_4_modgui.cpp index fa95810e0..ae3dddd83 100644 --- a/plugins/channeltx/mod802.15.4/ieee_802_15_4_modgui.cpp +++ b/plugins/channeltx/mod802.15.4/ieee_802_15_4_modgui.cpp @@ -320,7 +320,18 @@ void IEEE_802_15_4_ModGUI::onWidgetRolled(QWidget* widget, bool rollDown) (void) widget; (void) rollDown; - getRollupContents()->saveState(m_rollupState); + RollupContents *rollupContents = getRollupContents(); + + if (rollupContents->hasExpandableWidgets()) { + setSizePolicy(sizePolicy().horizontalPolicy(), QSizePolicy::Expanding); + } else { + setSizePolicy(sizePolicy().horizontalPolicy(), QSizePolicy::Fixed); + } + + int h = rollupContents->height() + getAdditionalHeight(); + resize(width(), h); + + rollupContents->saveState(m_rollupState); applySettings(); } @@ -381,12 +392,13 @@ IEEE_802_15_4_ModGUI::IEEE_802_15_4_ModGUI(PluginAPI* pluginAPI, DeviceUISet *de m_doApplySettings(true), m_basebandSampleRate(12000000) { - ui->setupUi(getRollupContents()); - getRollupContents()->arrangeRollups(); - m_helpURL = "plugins/channeltx/mod802.15.4/readme.md"; setAttribute(Qt::WA_DeleteOnClose, true); - - connect(getRollupContents(), SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + m_helpURL = "plugins/channeltx/mod802.15.4/readme.md"; + RollupContents *rollupContents = getRollupContents(); + ui->setupUi(rollupContents); + setSizePolicy(rollupContents->sizePolicy()); + rollupContents->arrangeRollups(); + connect(rollupContents, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &))); m_IEEE_802_15_4_Mod = (IEEE_802_15_4_Mod*) channelTx; diff --git a/plugins/channeltx/mod802.15.4/ieee_802_15_4_modgui.ui b/plugins/channeltx/mod802.15.4/ieee_802_15_4_modgui.ui index 472cb688b..e53cb30e5 100644 --- a/plugins/channeltx/mod802.15.4/ieee_802_15_4_modgui.ui +++ b/plugins/channeltx/mod802.15.4/ieee_802_15_4_modgui.ui @@ -11,7 +11,7 @@ - + 0 0 @@ -701,6 +701,12 @@ 284 + + + 0 + 0 + + 598 @@ -761,17 +767,6 @@
    gui/rollupcontents.h
    1 - - ButtonSwitch - QToolButton -
    gui/buttonswitch.h
    -
    - - ValueDialZ - QWidget -
    gui/valuedialz.h
    - 1 -
    GLSpectrum QWidget @@ -784,12 +779,6 @@
    gui/glspectrumgui.h
    1
    - - LevelMeterVU - QWidget -
    gui/levelmeter.h
    - 1 -
    GLScope QWidget @@ -802,6 +791,23 @@
    gui/glscopegui.h
    1
    + + ValueDialZ + QWidget +
    gui/valuedialz.h
    + 1 +
    + + ButtonSwitch + QToolButton +
    gui/buttonswitch.h
    +
    + + LevelMeterVU + QWidget +
    gui/levelmeter.h
    + 1 +
    deltaFrequency diff --git a/plugins/channeltx/modais/aismodgui.cpp b/plugins/channeltx/modais/aismodgui.cpp index 4a6f74a47..6514ee8ba 100644 --- a/plugins/channeltx/modais/aismodgui.cpp +++ b/plugins/channeltx/modais/aismodgui.cpp @@ -343,7 +343,18 @@ void AISModGUI::onWidgetRolled(QWidget* widget, bool rollDown) (void) widget; (void) rollDown; - getRollupContents()->saveState(m_rollupState); + RollupContents *rollupContents = getRollupContents(); + + if (rollupContents->hasExpandableWidgets()) { + setSizePolicy(sizePolicy().horizontalPolicy(), QSizePolicy::Expanding); + } else { + setSizePolicy(sizePolicy().horizontalPolicy(), QSizePolicy::Fixed); + } + + int h = rollupContents->height() + getAdditionalHeight(); + resize(width(), h); + + rollupContents->saveState(m_rollupState); applySettings(); } @@ -404,12 +415,13 @@ AISModGUI::AISModGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSam m_basebandSampleRate(1), m_doApplySettings(true) { - ui->setupUi(getRollupContents()); - getRollupContents()->arrangeRollups(); - m_helpURL = "plugins/channeltx/modais/readme.md"; setAttribute(Qt::WA_DeleteOnClose, true); - - connect(getRollupContents(), SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + m_helpURL = "plugins/channeltx/modais/readme.md"; + RollupContents *rollupContents = getRollupContents(); + ui->setupUi(rollupContents); + setSizePolicy(rollupContents->sizePolicy()); + rollupContents->arrangeRollups(); + connect(rollupContents, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &))); m_aisMod = (AISMod*) channelTx; diff --git a/plugins/channeltx/modais/aismodgui.ui b/plugins/channeltx/modais/aismodgui.ui index 342e69e97..cf270f5b8 100644 --- a/plugins/channeltx/modais/aismodgui.ui +++ b/plugins/channeltx/modais/aismodgui.ui @@ -11,7 +11,7 @@
    - + 0 0 @@ -1010,15 +1010,21 @@ 0 290 431 - 141 + 200 - + 0 0 + + + 0 + 200 + + Transmitted Messages @@ -1056,6 +1062,12 @@ 331 + + + 0 + 0 + + Baseband Spectrum @@ -1077,6 +1089,12 @@ + + + 0 + 0 + + 200 @@ -1105,6 +1123,12 @@ 311 + + + 0 + 0 + + IQ Waveforms @@ -1126,6 +1150,12 @@ + + + 0 + 0 + + 200 @@ -1154,9 +1184,15 @@ 1 - ValueDialZ + GLSpectrum QWidget -
    gui/valuedialz.h
    +
    gui/glspectrum.h
    + 1 +
    + + GLSpectrumGUI + QWidget +
    gui/glspectrumgui.h
    1
    @@ -1171,23 +1207,17 @@
    gui/glscopegui.h
    1
    + + ValueDialZ + QWidget +
    gui/valuedialz.h
    + 1 +
    ButtonSwitch QToolButton
    gui/buttonswitch.h
    - - GLSpectrum - QWidget -
    gui/glspectrum.h
    - 1 -
    - - GLSpectrumGUI - QWidget -
    gui/glspectrumgui.h
    - 1 -
    LevelMeterVU QWidget diff --git a/plugins/channeltx/modam/ammodgui.cpp b/plugins/channeltx/modam/ammodgui.cpp index e7b71bfb4..b2a1889b8 100644 --- a/plugins/channeltx/modam/ammodgui.cpp +++ b/plugins/channeltx/modam/ammodgui.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include "device/deviceuiset.h" @@ -73,6 +74,14 @@ bool AMModGUI::deserialize(const QByteArray& data) } } +void AMModGUI::resizeEvent(QResizeEvent* size) +{ + int maxWidth = getRollupContents()->maximumWidth(); + int minHeight = getRollupContents()->minimumHeight() + getAdditionalHeight(); + resize(width() < maxWidth ? width() : maxWidth, minHeight); + size->accept(); +} + bool AMModGUI::handleMessage(const Message& message) { if (AMMod::MsgReportFileSourceStreamData::match(message)) @@ -351,11 +360,13 @@ AMModGUI::AMModGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSampl m_tickCount(0), m_enableNavTime(false) { - ui->setupUi(getRollupContents()); - getRollupContents()->arrangeRollups(); - m_helpURL = "plugins/channeltx/modam/readme.md"; setAttribute(Qt::WA_DeleteOnClose, true); - connect(getRollupContents(), SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + m_helpURL = "plugins/channeltx/modam/readme.md"; + RollupContents *rollupContents = getRollupContents(); + ui->setupUi(rollupContents); + setSizePolicy(rollupContents->sizePolicy()); + rollupContents->arrangeRollups(); + connect(rollupContents, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &))); m_amMod = (AMMod*) channelTx; diff --git a/plugins/channeltx/modam/ammodgui.h b/plugins/channeltx/modam/ammodgui.h index 07985e85c..76239be4e 100644 --- a/plugins/channeltx/modam/ammodgui.h +++ b/plugins/channeltx/modam/ammodgui.h @@ -63,6 +63,9 @@ public: public slots: void channelMarkerChangedByCursor(); +protected: + void resizeEvent(QResizeEvent* size); + private: Ui::AMModGUI* ui; PluginAPI* m_pluginAPI; diff --git a/plugins/channeltx/modam/ammodgui.ui b/plugins/channeltx/modam/ammodgui.ui index b1f136cf0..e34f48ebd 100644 --- a/plugins/channeltx/modam/ammodgui.ui +++ b/plugins/channeltx/modam/ammodgui.ui @@ -11,7 +11,7 @@
    - + 0 0 @@ -22,6 +22,12 @@ 0 + + + 560 + 16777215 + + Liberation Sans @@ -736,17 +742,17 @@
    gui/rollupcontents.h
    1 - - ButtonSwitch - QToolButton -
    gui/buttonswitch.h
    -
    ValueDialZ QWidget
    gui/valuedialz.h
    1
    + + ButtonSwitch + QToolButton +
    gui/buttonswitch.h
    +
    LevelMeterVU QWidget diff --git a/plugins/channeltx/modatv/atvmodgui.cpp b/plugins/channeltx/modatv/atvmodgui.cpp index 511765c84..3be698ead 100644 --- a/plugins/channeltx/modatv/atvmodgui.cpp +++ b/plugins/channeltx/modatv/atvmodgui.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include @@ -66,11 +67,13 @@ ATVModGUI::ATVModGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSam m_camBusyFPSMessageBox(0), m_rfSliderDivisor(100000) { - ui->setupUi(getRollupContents()); - getRollupContents()->arrangeRollups(); - m_helpURL = "plugins/channeltx/modatv/readme.md"; setAttribute(Qt::WA_DeleteOnClose, true); - connect(getRollupContents(), SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + m_helpURL = "plugins/channeltx/modatv/readme.md"; + RollupContents *rollupContents = getRollupContents(); + ui->setupUi(rollupContents); + setSizePolicy(rollupContents->sizePolicy()); + rollupContents->arrangeRollups(); + connect(rollupContents, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &))); m_atvMod = (ATVMod*) channelTx; @@ -151,6 +154,14 @@ bool ATVModGUI::deserialize(const QByteArray& data) } } +void ATVModGUI::resizeEvent(QResizeEvent* size) +{ + int maxWidth = getRollupContents()->maximumWidth(); + int minHeight = getRollupContents()->minimumHeight() + getAdditionalHeight(); + resize(width() < maxWidth ? width() : maxWidth, minHeight); + size->accept(); +} + bool ATVModGUI::handleMessage(const Message& message) { if (ATVModReport::MsgReportVideoFileSourceStreamData::match(message)) diff --git a/plugins/channeltx/modatv/atvmodgui.h b/plugins/channeltx/modatv/atvmodgui.h index cfbf04961..d8235ebb0 100644 --- a/plugins/channeltx/modatv/atvmodgui.h +++ b/plugins/channeltx/modatv/atvmodgui.h @@ -62,6 +62,9 @@ public: public slots: void channelMarkerChangedByCursor(); +protected: + void resizeEvent(QResizeEvent* size); + private: Ui::ATVModGUI* ui; PluginAPI* m_pluginAPI; diff --git a/plugins/channeltx/modatv/atvmodgui.ui b/plugins/channeltx/modatv/atvmodgui.ui index 7155fd5cb..d175a0f95 100644 --- a/plugins/channeltx/modatv/atvmodgui.ui +++ b/plugins/channeltx/modatv/atvmodgui.ui @@ -11,7 +11,7 @@
    - + 0 0 @@ -22,6 +22,12 @@ 0 + + + 760 + 16777215 + + Liberation Sans @@ -1310,9 +1316,9 @@ 1 - LevelMeterVU + ValueDialZ QWidget -
    gui/levelmeter.h
    +
    gui/valuedialz.h
    1
    @@ -1321,9 +1327,9 @@
    gui/buttonswitch.h
    - ValueDialZ + LevelMeterVU QWidget -
    gui/valuedialz.h
    +
    gui/levelmeter.h
    1
    diff --git a/plugins/channeltx/modchirpchat/chirpchatmodgui.cpp b/plugins/channeltx/modchirpchat/chirpchatmodgui.cpp index c30310e72..30c055a3c 100644 --- a/plugins/channeltx/modchirpchat/chirpchatmodgui.cpp +++ b/plugins/channeltx/modchirpchat/chirpchatmodgui.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include "device/deviceuiset.h" #include "plugin/pluginapi.h" @@ -74,6 +75,14 @@ bool ChirpChatModGUI::deserialize(const QByteArray& data) } } +void ChirpChatModGUI::resizeEvent(QResizeEvent* size) +{ + int maxWidth = getRollupContents()->maximumWidth(); + int minHeight = getRollupContents()->minimumHeight() + getAdditionalHeight(); + resize(width() < maxWidth ? width() : maxWidth, minHeight); + size->accept(); +} + bool ChirpChatModGUI::handleMessage(const Message& message) { if (ChirpChatMod::MsgConfigureChirpChatMod::match(message)) @@ -425,12 +434,13 @@ ChirpChatModGUI::ChirpChatModGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, m_doApplySettings(true), m_tickCount(0) { - ui->setupUi(getRollupContents()); - getRollupContents()->arrangeRollups(); - m_helpURL = "plugins/channeltx/modchirpchat/readme.md"; setAttribute(Qt::WA_DeleteOnClose, true); - - connect(getRollupContents(), SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + m_helpURL = "plugins/channeltx/modchirpchat/readme.md"; + RollupContents *rollupContents = getRollupContents(); + ui->setupUi(rollupContents); + setSizePolicy(rollupContents->sizePolicy()); + rollupContents->arrangeRollups(); + connect(rollupContents, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &))); m_chirpChatMod = (ChirpChatMod*) channelTx; diff --git a/plugins/channeltx/modchirpchat/chirpchatmodgui.h b/plugins/channeltx/modchirpchat/chirpchatmodgui.h index 98d3affb7..219a7f933 100644 --- a/plugins/channeltx/modchirpchat/chirpchatmodgui.h +++ b/plugins/channeltx/modchirpchat/chirpchatmodgui.h @@ -61,6 +61,9 @@ public: public slots: void channelMarkerChangedByCursor(); +protected: + void resizeEvent(QResizeEvent* size); + private: Ui::ChirpChatModGUI* ui; PluginAPI* m_pluginAPI; diff --git a/plugins/channeltx/modchirpchat/chirpchatmodgui.ui b/plugins/channeltx/modchirpchat/chirpchatmodgui.ui index f6b917abf..feb0f9db1 100644 --- a/plugins/channeltx/modchirpchat/chirpchatmodgui.ui +++ b/plugins/channeltx/modchirpchat/chirpchatmodgui.ui @@ -10,12 +10,24 @@ 573
    + + + 0 + 0 + + 392 180 + + + 560 + 16777215 + + Liberation Sans @@ -26,9 +38,6 @@ ChirpChat Modulator - - RF/mod/coder settings - 0 @@ -37,6 +46,9 @@ 131 + + RF/mod/coder settings + 2 @@ -498,9 +510,6 @@ - - Payload - 0 @@ -509,6 +518,9 @@ 401 + + Payload + 2 @@ -1180,19 +1192,6 @@
    - - - - Qt::Vertical - - - - 20 - 40 - - - -
    diff --git a/plugins/channeltx/moddatv/datvmodgui.cpp b/plugins/channeltx/moddatv/datvmodgui.cpp index 5040dd02a..50ed21d8b 100644 --- a/plugins/channeltx/moddatv/datvmodgui.cpp +++ b/plugins/channeltx/moddatv/datvmodgui.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include @@ -66,11 +67,13 @@ DATVModGUI::DATVModGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandS m_tickCount(0), m_enableNavTime(false) { - ui->setupUi(getRollupContents()); - getRollupContents()->arrangeRollups(); - m_helpURL = "plugins/channeltx/moddatv/readme.md"; setAttribute(Qt::WA_DeleteOnClose, true); - connect(getRollupContents(), SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + m_helpURL = "plugins/channeltx/moddatv/readme.md"; + RollupContents *rollupContents = getRollupContents(); + ui->setupUi(rollupContents); + setSizePolicy(rollupContents->sizePolicy()); + rollupContents->arrangeRollups(); + connect(rollupContents, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &))); m_datvMod = (DATVMod*) channelTx; @@ -150,6 +153,14 @@ bool DATVModGUI::deserialize(const QByteArray& data) } } +void DATVModGUI::resizeEvent(QResizeEvent* size) +{ + int maxWidth = getRollupContents()->maximumWidth(); + int minHeight = getRollupContents()->minimumHeight() + getAdditionalHeight(); + resize(width() < maxWidth ? width() : maxWidth, minHeight); + size->accept(); +} + bool DATVModGUI::handleMessage(const Message& message) { if (DATVModReport::MsgReportTsFileSourceStreamData::match(message)) diff --git a/plugins/channeltx/moddatv/datvmodgui.h b/plugins/channeltx/moddatv/datvmodgui.h index 561f44f44..40096319c 100644 --- a/plugins/channeltx/moddatv/datvmodgui.h +++ b/plugins/channeltx/moddatv/datvmodgui.h @@ -63,6 +63,9 @@ public: public slots: void channelMarkerChangedByCursor(); +protected: + void resizeEvent(QResizeEvent* size); + private: Ui::DATVModGUI* ui; PluginAPI* m_pluginAPI; diff --git a/plugins/channeltx/moddatv/datvmodgui.ui b/plugins/channeltx/moddatv/datvmodgui.ui index 1d69e20b9..fca177925 100644 --- a/plugins/channeltx/moddatv/datvmodgui.ui +++ b/plugins/channeltx/moddatv/datvmodgui.ui @@ -11,7 +11,7 @@ - + 0 0 @@ -22,6 +22,12 @@ 0 + + + 700 + 16777215 + + Liberation Sans @@ -799,17 +805,17 @@
    gui/rollupcontents.h
    1 - - ButtonSwitch - QToolButton -
    gui/buttonswitch.h
    -
    ValueDialZ QWidget
    gui/valuedialz.h
    1
    + + ButtonSwitch + QToolButton +
    gui/buttonswitch.h
    +
    diff --git a/plugins/channeltx/modfreedv/freedvmodgui.cpp b/plugins/channeltx/modfreedv/freedvmodgui.cpp index 6adc7b4fe..19ac57d3e 100644 --- a/plugins/channeltx/modfreedv/freedvmodgui.cpp +++ b/plugins/channeltx/modfreedv/freedvmodgui.cpp @@ -295,7 +295,18 @@ void FreeDVModGUI::onWidgetRolled(QWidget* widget, bool rollDown) (void) widget; (void) rollDown; - getRollupContents()->saveState(m_rollupState); + RollupContents *rollupContents = getRollupContents(); + + if (rollupContents->hasExpandableWidgets()) { + setSizePolicy(sizePolicy().horizontalPolicy(), QSizePolicy::Expanding); + } else { + setSizePolicy(sizePolicy().horizontalPolicy(), QSizePolicy::Fixed); + } + + int h = rollupContents->height() + getAdditionalHeight(); + resize(width(), h); + + rollupContents->saveState(m_rollupState); applySettings(); } @@ -363,11 +374,13 @@ FreeDVModGUI::FreeDVModGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Baseb m_tickCount(0), m_enableNavTime(false) { - ui->setupUi(getRollupContents()); - getRollupContents()->arrangeRollups(); - m_helpURL = "plugins/channeltx/modfreedv/readme.md"; setAttribute(Qt::WA_DeleteOnClose, true); - connect(getRollupContents(), SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + m_helpURL = "plugins/channeltx/modfreedv/readme.md"; + RollupContents *rollupContents = getRollupContents(); + ui->setupUi(rollupContents); + setSizePolicy(rollupContents->sizePolicy()); + rollupContents->arrangeRollups(); + connect(rollupContents, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &))); m_freeDVMod = (FreeDVMod*) channelTx; diff --git a/plugins/channeltx/modfreedv/freedvmodgui.ui b/plugins/channeltx/modfreedv/freedvmodgui.ui index cd10b32e3..0f3a15610 100644 --- a/plugins/channeltx/modfreedv/freedvmodgui.ui +++ b/plugins/channeltx/modfreedv/freedvmodgui.ui @@ -11,7 +11,7 @@
    - + 0 0 @@ -675,6 +675,12 @@ 284 + + + 0 + 0 + + Channel Spectrum @@ -696,6 +702,12 @@ + + + 0 + 0 + + 200 @@ -736,9 +748,9 @@ 1 - LevelMeterVU + ValueDialZ QWidget -
    gui/levelmeter.h
    +
    gui/valuedialz.h
    1
    @@ -747,15 +759,15 @@
    gui/buttonswitch.h
    - CWKeyerGUI + LevelMeterVU QWidget -
    gui/cwkeyergui.h
    +
    gui/levelmeter.h
    1
    - ValueDialZ + CWKeyerGUI QWidget -
    gui/valuedialz.h
    +
    gui/cwkeyergui.h
    1
    diff --git a/plugins/channeltx/modnfm/nfmmodgui.cpp b/plugins/channeltx/modnfm/nfmmodgui.cpp index c5221d99d..cf1937b4b 100644 --- a/plugins/channeltx/modnfm/nfmmodgui.cpp +++ b/plugins/channeltx/modnfm/nfmmodgui.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include "device/deviceuiset.h" #include "plugin/pluginapi.h" @@ -74,6 +75,14 @@ bool NFMModGUI::deserialize(const QByteArray& data) } } +void NFMModGUI::resizeEvent(QResizeEvent* size) +{ + int maxWidth = getRollupContents()->maximumWidth(); + int minHeight = getRollupContents()->minimumHeight() + getAdditionalHeight(); + resize(width() < maxWidth ? width() : maxWidth, minHeight); + size->accept(); +} + bool NFMModGUI::handleMessage(const Message& message) { if (NFMMod::MsgReportFileSourceStreamData::match(message)) @@ -424,10 +433,13 @@ NFMModGUI::NFMModGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSam m_enableNavTime(false), m_dcsCodeValidator(QRegExp("[0-7]{1,3}")) { - ui->setupUi(getRollupContents()); - getRollupContents()->arrangeRollups(); - m_helpURL = "plugins/channeltx/modnfm/readme.md"; setAttribute(Qt::WA_DeleteOnClose, true); + m_helpURL = "plugins/channeltx/modnfm/readme.md"; + RollupContents *rollupContents = getRollupContents(); + ui->setupUi(rollupContents); + setSizePolicy(rollupContents->sizePolicy()); + rollupContents->arrangeRollups(); + connect(rollupContents, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); ui->channelSpacing->blockSignals(true); ui->channelSpacing->clear(); @@ -439,7 +451,6 @@ NFMModGUI::NFMModGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSam ui->channelSpacing->setCurrentIndex(NFMModSettings::getChannelSpacingIndex(25000)); ui->channelSpacing->blockSignals(false); - connect(getRollupContents(), SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &))); m_nfmMod = (NFMMod*) channelTx; diff --git a/plugins/channeltx/modnfm/nfmmodgui.h b/plugins/channeltx/modnfm/nfmmodgui.h index 030ab68ae..3d20b38e4 100644 --- a/plugins/channeltx/modnfm/nfmmodgui.h +++ b/plugins/channeltx/modnfm/nfmmodgui.h @@ -63,6 +63,9 @@ public: public slots: void channelMarkerChangedByCursor(); +protected: + void resizeEvent(QResizeEvent* size); + private: Ui::NFMModGUI* ui; PluginAPI* m_pluginAPI; diff --git a/plugins/channeltx/modnfm/nfmmodgui.ui b/plugins/channeltx/modnfm/nfmmodgui.ui index ec293e1c7..086c4d5da 100644 --- a/plugins/channeltx/modnfm/nfmmodgui.ui +++ b/plugins/channeltx/modnfm/nfmmodgui.ui @@ -11,7 +11,7 @@
    - + 0 0 @@ -22,6 +22,12 @@ 0 + + + 560 + 16777215 + + Liberation Sans @@ -933,17 +939,17 @@
    gui/rollupcontents.h
    1 - - ButtonSwitch - QToolButton -
    gui/buttonswitch.h
    -
    ValueDialZ QWidget
    gui/valuedialz.h
    1
    + + ButtonSwitch + QToolButton +
    gui/buttonswitch.h
    +
    LevelMeterVU QWidget diff --git a/plugins/channeltx/modpacket/packetmodgui.cpp b/plugins/channeltx/modpacket/packetmodgui.cpp index 621314241..74d0b793b 100644 --- a/plugins/channeltx/modpacket/packetmodgui.cpp +++ b/plugins/channeltx/modpacket/packetmodgui.cpp @@ -380,7 +380,18 @@ void PacketModGUI::onWidgetRolled(QWidget* widget, bool rollDown) (void) widget; (void) rollDown; - getRollupContents()->saveState(m_rollupState); + RollupContents *rollupContents = getRollupContents(); + + if (rollupContents->hasExpandableWidgets()) { + setSizePolicy(sizePolicy().horizontalPolicy(), QSizePolicy::Expanding); + } else { + setSizePolicy(sizePolicy().horizontalPolicy(), QSizePolicy::Fixed); + } + + int h = rollupContents->height() + getAdditionalHeight(); + resize(width(), h); + + rollupContents->saveState(m_rollupState); applySettings(); } @@ -441,12 +452,13 @@ PacketModGUI::PacketModGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Baseb m_basebandSampleRate(1), m_doApplySettings(true) { - ui->setupUi(getRollupContents()); - getRollupContents()->arrangeRollups(); - m_helpURL = "plugins/channeltx/modpacket/readme.md"; setAttribute(Qt::WA_DeleteOnClose, true); - - connect(getRollupContents(), SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + m_helpURL = "plugins/channeltx/modpacket/readme.md"; + RollupContents *rollupContents = getRollupContents(); + ui->setupUi(rollupContents); + setSizePolicy(rollupContents->sizePolicy()); + rollupContents->arrangeRollups(); + connect(rollupContents, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &))); m_packetMod = (PacketMod*) channelTx; diff --git a/plugins/channeltx/modpacket/packetmodgui.ui b/plugins/channeltx/modpacket/packetmodgui.ui index b6d77bc32..8df7d2d31 100644 --- a/plugins/channeltx/modpacket/packetmodgui.ui +++ b/plugins/channeltx/modpacket/packetmodgui.ui @@ -11,7 +11,7 @@
    - + 0 0 @@ -828,6 +828,12 @@ APRS examples: 284 + + + 0 + 0 + + Baseband Spectrum @@ -849,6 +855,12 @@ APRS examples: + + + 0 + 0 + + 200 @@ -876,17 +888,6 @@ APRS examples:
    gui/rollupcontents.h
    1 - - ValueDialZ - QWidget -
    gui/valuedialz.h
    - 1 -
    - - ButtonSwitch - QToolButton -
    gui/buttonswitch.h
    -
    GLSpectrum QWidget @@ -899,6 +900,17 @@ APRS examples:
    gui/glspectrumgui.h
    1
    + + ValueDialZ + QWidget +
    gui/valuedialz.h
    + 1 +
    + + ButtonSwitch + QToolButton +
    gui/buttonswitch.h
    +
    LevelMeterVU QWidget diff --git a/plugins/channeltx/modssb/ssbmodgui.cpp b/plugins/channeltx/modssb/ssbmodgui.cpp index c9a8696f3..7a3749b09 100644 --- a/plugins/channeltx/modssb/ssbmodgui.cpp +++ b/plugins/channeltx/modssb/ssbmodgui.cpp @@ -359,7 +359,18 @@ void SSBModGUI::onWidgetRolled(QWidget* widget, bool rollDown) (void) widget; (void) rollDown; - getRollupContents()->saveState(m_rollupState); + RollupContents *rollupContents = getRollupContents(); + + if (rollupContents->hasExpandableWidgets()) { + setSizePolicy(sizePolicy().horizontalPolicy(), QSizePolicy::Expanding); + } else { + setSizePolicy(sizePolicy().horizontalPolicy(), QSizePolicy::Fixed); + } + + int h = rollupContents->height() + getAdditionalHeight(); + resize(width(), h); + + rollupContents->saveState(m_rollupState); applySettings(); } @@ -428,11 +439,13 @@ SSBModGUI::SSBModGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSam m_tickCount(0), m_enableNavTime(false) { - ui->setupUi(getRollupContents()); - getRollupContents()->arrangeRollups(); - m_helpURL = "plugins/channeltx/modssb/readme.md"; setAttribute(Qt::WA_DeleteOnClose, true); - connect(getRollupContents(), SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + m_helpURL = "plugins/channeltx/modssb/readme.md"; + RollupContents *rollupContents = getRollupContents(); + ui->setupUi(rollupContents); + setSizePolicy(rollupContents->sizePolicy()); + rollupContents->arrangeRollups(); + connect(rollupContents, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &))); m_ssbMod = (SSBMod*) channelTx; diff --git a/plugins/channeltx/modssb/ssbmodgui.ui b/plugins/channeltx/modssb/ssbmodgui.ui index 06b6eb17f..69d90c843 100644 --- a/plugins/channeltx/modssb/ssbmodgui.ui +++ b/plugins/channeltx/modssb/ssbmodgui.ui @@ -1235,6 +1235,12 @@
    + + + 0 + 0 + + 200 @@ -1262,17 +1268,6 @@
    gui/rollupcontents.h
    1 - - ButtonSwitch - QToolButton -
    gui/buttonswitch.h
    -
    - - ValueDialZ - QWidget -
    gui/valuedialz.h
    - 1 -
    GLSpectrum QWidget @@ -1286,9 +1281,15 @@ 1 - TickedSlider - QSlider -
    gui/tickedslider.h
    + ValueDialZ + QWidget +
    gui/valuedialz.h
    + 1 +
    + + ButtonSwitch + QToolButton +
    gui/buttonswitch.h
    LevelMeterVU @@ -1302,6 +1303,11 @@
    gui/cwkeyergui.h
    1
    + + TickedSlider + QSlider +
    gui/tickedslider.h
    +
    diff --git a/plugins/channeltx/modwfm/wfmmodgui.cpp b/plugins/channeltx/modwfm/wfmmodgui.cpp index d9d744c22..4af2c0a31 100644 --- a/plugins/channeltx/modwfm/wfmmodgui.cpp +++ b/plugins/channeltx/modwfm/wfmmodgui.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include "device/deviceuiset.h" #include "plugin/pluginapi.h" @@ -73,6 +74,14 @@ bool WFMModGUI::deserialize(const QByteArray& data) } } +void WFMModGUI::resizeEvent(QResizeEvent* size) +{ + int maxWidth = getRollupContents()->maximumWidth(); + int minHeight = getRollupContents()->minimumHeight() + getAdditionalHeight(); + resize(width() < maxWidth ? width() : maxWidth, minHeight); + size->accept(); +} + bool WFMModGUI::handleMessage(const Message& message) { if (WFMMod::MsgReportFileSourceStreamData::match(message)) @@ -357,10 +366,13 @@ WFMModGUI::WFMModGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSam m_tickCount(0), m_enableNavTime(false) { - ui->setupUi(getRollupContents()); - getRollupContents()->arrangeRollups(); - m_helpURL = "plugins/channeltx/modwfm/readme.md"; setAttribute(Qt::WA_DeleteOnClose, true); + m_helpURL = "plugins/channeltx/modwfm/readme.md"; + RollupContents *rollupContents = getRollupContents(); + ui->setupUi(rollupContents); + setSizePolicy(rollupContents->sizePolicy()); + rollupContents->arrangeRollups(); + connect(rollupContents, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); blockApplySettings(true); @@ -372,7 +384,6 @@ WFMModGUI::WFMModGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSam blockApplySettings(false); - connect(getRollupContents(), SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &))); m_wfmMod = (WFMMod*) channelTx; diff --git a/plugins/channeltx/modwfm/wfmmodgui.h b/plugins/channeltx/modwfm/wfmmodgui.h index 19e72eae9..35d0c1ad1 100644 --- a/plugins/channeltx/modwfm/wfmmodgui.h +++ b/plugins/channeltx/modwfm/wfmmodgui.h @@ -61,6 +61,9 @@ public: public slots: void channelMarkerChangedByCursor(); +protected: + void resizeEvent(QResizeEvent* size); + private: Ui::WFMModGUI* ui; PluginAPI* m_pluginAPI; diff --git a/plugins/channeltx/modwfm/wfmmodgui.ui b/plugins/channeltx/modwfm/wfmmodgui.ui index 9183b2492..4a8d5bb2b 100644 --- a/plugins/channeltx/modwfm/wfmmodgui.ui +++ b/plugins/channeltx/modwfm/wfmmodgui.ui @@ -6,22 +6,28 @@ 0 0 - 298 + 360 300
    - + 0 0 - 0 + 360 0 + + + 560 + 16777215 + + Liberation Sans @@ -771,9 +777,9 @@ 1 - LevelMeterVU + ValueDialZ QWidget -
    gui/levelmeter.h
    +
    gui/valuedialz.h
    1
    @@ -782,15 +788,15 @@
    gui/buttonswitch.h
    - CWKeyerGUI + LevelMeterVU QWidget -
    gui/cwkeyergui.h
    +
    gui/levelmeter.h
    1
    - ValueDialZ + CWKeyerGUI QWidget -
    gui/valuedialz.h
    +
    gui/cwkeyergui.h
    1
    diff --git a/plugins/channeltx/remotesource/remotesourcegui.cpp b/plugins/channeltx/remotesource/remotesourcegui.cpp index 5ca564926..5ae264408 100644 --- a/plugins/channeltx/remotesource/remotesourcegui.cpp +++ b/plugins/channeltx/remotesource/remotesourcegui.cpp @@ -16,6 +16,7 @@ /////////////////////////////////////////////////////////////////////////////////// #include +#include #include "device/deviceapi.h" #include "device/deviceuiset.h" @@ -65,6 +66,14 @@ bool RemoteSourceGUI::deserialize(const QByteArray& data) } } +void RemoteSourceGUI::resizeEvent(QResizeEvent* size) +{ + int maxWidth = getRollupContents()->maximumWidth(); + int minHeight = getRollupContents()->minimumHeight() + getAdditionalHeight(); + resize(width() < maxWidth ? width() : maxWidth, minHeight); + size->accept(); +} + bool RemoteSourceGUI::handleMessage(const Message& message) { if (DSPSignalNotification::match(message)) @@ -172,11 +181,13 @@ RemoteSourceGUI::RemoteSourceGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, m_resetCounts(true), m_tickCount(0) { - ui->setupUi(getRollupContents()); - getRollupContents()->arrangeRollups(); - m_helpURL = "plugins/channeltx/remotesource/readme.md"; setAttribute(Qt::WA_DeleteOnClose, true); - connect(getRollupContents(), SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + m_helpURL = "plugins/channeltx/remotesource/readme.md"; + RollupContents *rollupContents = getRollupContents(); + ui->setupUi(rollupContents); + setSizePolicy(rollupContents->sizePolicy()); + rollupContents->arrangeRollups(); + connect(rollupContents, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &))); m_remoteSrc = (RemoteSource*) channelTx; diff --git a/plugins/channeltx/remotesource/remotesourcegui.h b/plugins/channeltx/remotesource/remotesourcegui.h index 0035875b1..09c410ad3 100644 --- a/plugins/channeltx/remotesource/remotesourcegui.h +++ b/plugins/channeltx/remotesource/remotesourcegui.h @@ -62,6 +62,9 @@ public: public slots: void channelMarkerChangedByCursor(); +protected: + void resizeEvent(QResizeEvent* size); + private: Ui::RemoteSourceGUI* ui; PluginAPI* m_pluginAPI; diff --git a/plugins/channeltx/remotesource/remotesourcegui.ui b/plugins/channeltx/remotesource/remotesourcegui.ui index 7409712b6..a246d4290 100644 --- a/plugins/channeltx/remotesource/remotesourcegui.ui +++ b/plugins/channeltx/remotesource/remotesourcegui.ui @@ -11,7 +11,7 @@
    - + 0 0 @@ -24,7 +24,7 @@ - 320 + 560 16777215 @@ -40,8 +40,8 @@ - 10 - 10 + 0 + 0 301 191 @@ -571,19 +571,6 @@
    - - - - Qt::Vertical - - - - 20 - 40 - - - -
    diff --git a/plugins/channeltx/udpsource/udpsourcegui.cpp b/plugins/channeltx/udpsource/udpsourcegui.cpp index 2e48c1598..0dd9c2533 100644 --- a/plugins/channeltx/udpsource/udpsourcegui.cpp +++ b/plugins/channeltx/udpsource/udpsourcegui.cpp @@ -120,12 +120,14 @@ UDPSourceGUI::UDPSourceGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Baseb m_rfBandwidthChanged(false), m_doApplySettings(true) { - ui->setupUi(getRollupContents()); - getRollupContents()->arrangeRollups(); - m_helpURL = "plugins/channeltx/udpsource/readme.md"; - connect(getRollupContents(), SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); - connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &))); setAttribute(Qt::WA_DeleteOnClose, true); + m_helpURL = "plugins/channeltx/udpsource/readme.md"; + RollupContents *rollupContents = getRollupContents(); + ui->setupUi(rollupContents); + setSizePolicy(rollupContents->sizePolicy()); + rollupContents->arrangeRollups(); + connect(rollupContents, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &))); m_udpSource = (UDPSource*) channelTx; m_spectrumVis = m_udpSource->getSpectrumVis(); @@ -480,7 +482,18 @@ void UDPSourceGUI::onWidgetRolled(QWidget* widget, bool rollDown) m_udpSource->setSpectrum(rollDown); } - getRollupContents()->saveState(m_rollupState); + RollupContents *rollupContents = getRollupContents(); + + if (rollupContents->hasExpandableWidgets()) { + setSizePolicy(sizePolicy().horizontalPolicy(), QSizePolicy::Expanding); + } else { + setSizePolicy(sizePolicy().horizontalPolicy(), QSizePolicy::Fixed); + } + + int h = rollupContents->height() + getAdditionalHeight(); + resize(width(), h); + + rollupContents->saveState(m_rollupState); applySettings(); } diff --git a/plugins/channeltx/udpsource/udpsourcegui.ui b/plugins/channeltx/udpsource/udpsourcegui.ui index dcc1dc6d8..90cccc5ef 100644 --- a/plugins/channeltx/udpsource/udpsourcegui.ui +++ b/plugins/channeltx/udpsource/udpsourcegui.ui @@ -6,7 +6,7 @@ 0 0 - 414 + 420 400 @@ -18,16 +18,10 @@ - 395 + 420 0 - - - 414 - 16777215 - - Liberation Sans @@ -1091,17 +1085,6 @@
    gui/rollupcontents.h
    1 - - ButtonSwitch - QToolButton -
    gui/buttonswitch.h
    -
    - - ValueDialZ - QWidget -
    gui/valuedialz.h
    - 1 -
    GLSpectrum QWidget @@ -1114,6 +1097,17 @@
    gui/glspectrumgui.h
    1
    + + ValueDialZ + QWidget +
    gui/valuedialz.h
    + 1 +
    + + ButtonSwitch + QToolButton +
    gui/buttonswitch.h
    +
    LevelMeterVU QWidget From f77e38eb3fef83d936876ae585daa97f4a07ef0e Mon Sep 17 00:00:00 2001 From: f4exb Date: Sun, 24 Apr 2022 19:34:48 +0200 Subject: [PATCH 061/115] Massive UI revamping (v7): Fixed sizing of feature windows. Part of #1209 --- plugins/feature/afc/afcgui.cpp | 15 ++++++----- plugins/feature/afc/afcgui.ui | 16 ++++++------ plugins/feature/ais/aisgui.cpp | 24 +++++++++++++---- .../feature/antennatools/antennatoolsgui.cpp | 26 ++++++++++++++----- plugins/feature/aprs/aprsgui.cpp | 17 +++++++----- plugins/feature/aprs/aprsgui.ui | 22 ++++++++++------ .../demodanalyzer/demodanalyzergui.cpp | 23 ++++++++++++---- .../gs232controller/gs232controllergui.cpp | 16 +++++++----- .../gs232controller/gs232controllergui.ui | 14 +++++----- .../jogdialcontrollergui.cpp | 15 ++++++----- .../jogdialcontroller/jogdialcontrollergui.ui | 20 +++++++++----- plugins/feature/map/mapgui.cpp | 23 ++++++++++++---- plugins/feature/pertester/pertestergui.cpp | 16 +++++++----- plugins/feature/pertester/pertestergui.ui | 24 ++++++++++------- plugins/feature/radiosonde/radiosondegui.cpp | 24 +++++++++++++---- plugins/feature/radiosonde/radiosondegui.ui | 6 ----- .../feature/rigctlserver/rigctlservergui.cpp | 16 +++++++----- .../feature/rigctlserver/rigctlservergui.ui | 10 +++---- .../satellitetracker/satellitetrackergui.cpp | 24 +++++++++++++---- .../satellitetracker/satellitetrackergui.ui | 2 +- plugins/feature/simpleptt/simplepttgui.cpp | 16 +++++++----- plugins/feature/simpleptt/simplepttgui.ui | 8 +++--- .../feature/startracker/startrackergui.cpp | 24 +++++++++++++---- .../feature/vorlocalizer/vorlocalizergui.cpp | 23 ++++++++++++---- .../feature/vorlocalizer/vorlocalizergui.ui | 12 ++++++--- 25 files changed, 294 insertions(+), 142 deletions(-) diff --git a/plugins/feature/afc/afcgui.cpp b/plugins/feature/afc/afcgui.cpp index 791b35c02..413ebc6c4 100644 --- a/plugins/feature/afc/afcgui.cpp +++ b/plugins/feature/afc/afcgui.cpp @@ -68,7 +68,9 @@ bool AFCGUI::deserialize(const QByteArray& data) void AFCGUI::resizeEvent(QResizeEvent* size) { - adjustSize(); + int maxWidth = getRollupContents()->maximumWidth(); + int minHeight = getRollupContents()->minimumHeight() + getAdditionalHeight(); + resize(width() < maxWidth ? width() : maxWidth, minHeight); size->accept(); } @@ -137,11 +139,13 @@ AFCGUI::AFCGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISet, Feature *featur m_doApplySettings(true), m_lastFeatureState(0) { - ui->setupUi(getRollupContents()); - setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); - getRollupContents()->arrangeRollups(); - m_helpURL = "plugins/feature/afc/readme.md"; setAttribute(Qt::WA_DeleteOnClose, true); + m_helpURL = "plugins/feature/afc/readme.md"; + RollupContents *rollupContents = getRollupContents(); + ui->setupUi(rollupContents); + setSizePolicy(rollupContents->sizePolicy()); + rollupContents->arrangeRollups(); + connect(rollupContents, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); ui->targetFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold)); ui->targetFrequency->setValueRange(10, 0, 9999999999L); @@ -149,7 +153,6 @@ AFCGUI::AFCGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISet, Feature *featur ui->toleranceFrequency->setColorMapper(ColorMapper(ColorMapper::GrayYellow)); ui->toleranceFrequency->setValueRange(5, 0, 99999L); - connect(getRollupContents(), SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); m_afc = reinterpret_cast(feature); m_afc->setMessageQueueToGUI(&m_inputMessageQueue); diff --git a/plugins/feature/afc/afcgui.ui b/plugins/feature/afc/afcgui.ui index de65c6172..b72795a21 100644 --- a/plugins/feature/afc/afcgui.ui +++ b/plugins/feature/afc/afcgui.ui @@ -11,7 +11,7 @@
    - + 0 0 @@ -24,7 +24,7 @@ - 320 + 560 100 @@ -412,6 +412,12 @@
    + + RollupContents + QWidget +
    gui/rollupcontents.h
    + 1 +
    ButtonSwitch QToolButton @@ -423,12 +429,6 @@
    gui/valuedial.h
    1
    - - RollupContents - QWidget -
    gui/rollupcontents.h
    - 1 -
    diff --git a/plugins/feature/ais/aisgui.cpp b/plugins/feature/ais/aisgui.cpp index 4ea066702..b021ccacb 100644 --- a/plugins/feature/ais/aisgui.cpp +++ b/plugins/feature/ais/aisgui.cpp @@ -166,7 +166,18 @@ void AISGUI::onWidgetRolled(QWidget* widget, bool rollDown) (void) widget; (void) rollDown; - getRollupContents()->saveState(m_rollupState); + RollupContents *rollupContents = getRollupContents(); + + if (rollupContents->hasExpandableWidgets()) { + setSizePolicy(sizePolicy().horizontalPolicy(), QSizePolicy::Expanding); + } else { + setSizePolicy(sizePolicy().horizontalPolicy(), QSizePolicy::Fixed); + } + + int h = rollupContents->height() + getAdditionalHeight(); + resize(width(), h); + + rollupContents->saveState(m_rollupState); applySettings(); } @@ -178,11 +189,14 @@ AISGUI::AISGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISet, Feature *featur m_doApplySettings(true), m_lastFeatureState(0) { - ui->setupUi(getRollupContents()); - getRollupContents()->arrangeRollups(); - m_helpURL = "plugins/feature/ais/readme.md"; setAttribute(Qt::WA_DeleteOnClose, true); - connect(getRollupContents(), SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + m_helpURL = "plugins/feature/ais/readme.md"; + RollupContents *rollupContents = getRollupContents(); + ui->setupUi(rollupContents); + setSizePolicy(rollupContents->sizePolicy()); + rollupContents->arrangeRollups(); + connect(rollupContents, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + m_ais = reinterpret_cast(feature); m_ais->setMessageQueueToGUI(&m_inputMessageQueue); diff --git a/plugins/feature/antennatools/antennatoolsgui.cpp b/plugins/feature/antennatools/antennatoolsgui.cpp index 4fcd57780..ff316198c 100644 --- a/plugins/feature/antennatools/antennatoolsgui.cpp +++ b/plugins/feature/antennatools/antennatoolsgui.cpp @@ -110,7 +110,18 @@ void AntennaToolsGUI::onWidgetRolled(QWidget* widget, bool rollDown) (void) widget; (void) rollDown; - getRollupContents()->saveState(m_rollupState); + RollupContents *rollupContents = getRollupContents(); + + if (rollupContents->hasExpandableWidgets()) { + setSizePolicy(sizePolicy().horizontalPolicy(), QSizePolicy::Expanding); + } else { + setSizePolicy(sizePolicy().horizontalPolicy(), QSizePolicy::Fixed); + } + + int h = rollupContents->height() + getAdditionalHeight(); + resize(width(), h); + + rollupContents->saveState(m_rollupState); applySettings(); } @@ -122,13 +133,14 @@ AntennaToolsGUI::AntennaToolsGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISe m_doApplySettings(true), m_deviceSets(0) { - ui->setupUi(getRollupContents()); - setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); - getRollupContents()->arrangeRollups(); - m_helpURL = "plugins/feature/antennatools/readme.md"; - setAttribute(Qt::WA_DeleteOnClose, true); - connect(getRollupContents(), SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + m_helpURL = "plugins/feature/antennatools/readme.md"; + RollupContents *rollupContents = getRollupContents(); + ui->setupUi(rollupContents); + setSizePolicy(rollupContents->sizePolicy()); + rollupContents->arrangeRollups(); + connect(rollupContents, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + m_antennatools = reinterpret_cast(feature); m_antennatools->setMessageQueueToGUI(&m_inputMessageQueue); diff --git a/plugins/feature/aprs/aprsgui.cpp b/plugins/feature/aprs/aprsgui.cpp index 80ed4108c..416de7d4b 100644 --- a/plugins/feature/aprs/aprsgui.cpp +++ b/plugins/feature/aprs/aprsgui.cpp @@ -430,12 +430,14 @@ APRSGUI::APRSGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISet, Feature *feat m_doApplySettings(true), m_lastFeatureState(0) { - ui->setupUi(getRollupContents()); - getRollupContents()->arrangeRollups(); - m_helpURL = "plugins/feature/aprs/readme.md"; - setAttribute(Qt::WA_DeleteOnClose, true); - connect(getRollupContents(), SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + m_helpURL = "plugins/feature/aprs/readme.md"; + RollupContents *rollupContents = getRollupContents(); + ui->setupUi(rollupContents); + setSizePolicy(rollupContents->sizePolicy()); + rollupContents->arrangeRollups(); + connect(rollupContents, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + m_aprs = reinterpret_cast(feature); m_aprs->setMessageQueueToGUI(&m_inputMessageQueue); @@ -666,7 +668,10 @@ void APRSGUI::resizeEvent(QResizeEvent* size) plotWeather(); plotTelemetry(); plotMotion(); - FeatureGUI::resizeEvent(size); + int maxWidth = getRollupContents()->maximumWidth(); + int minHeight = getRollupContents()->minimumHeight() + getAdditionalHeight(); + resize(width() < maxWidth ? width() : maxWidth, minHeight); + size->accept(); } void APRSGUI::onMenuDialogCalled(const QPoint &p) diff --git a/plugins/feature/aprs/aprsgui.ui b/plugins/feature/aprs/aprsgui.ui index 8194bb504..1a6fa0712 100644 --- a/plugins/feature/aprs/aprsgui.ui +++ b/plugins/feature/aprs/aprsgui.ui @@ -7,11 +7,11 @@ 0 0 469 - 761 + 578 - + 0 0 @@ -22,6 +22,12 @@ 0 + + + 700 + 16777215 + + Liberation Sans @@ -148,7 +154,7 @@ - + 0 0 @@ -1447,17 +1453,17 @@ - - ButtonSwitch - QToolButton -
    gui/buttonswitch.h
    -
    RollupContents QWidget
    gui/rollupcontents.h
    1
    + + ButtonSwitch + QToolButton +
    gui/buttonswitch.h
    +
    QChartView QGraphicsView diff --git a/plugins/feature/demodanalyzer/demodanalyzergui.cpp b/plugins/feature/demodanalyzer/demodanalyzergui.cpp index 9872a0bfb..006176c59 100644 --- a/plugins/feature/demodanalyzer/demodanalyzergui.cpp +++ b/plugins/feature/demodanalyzer/demodanalyzergui.cpp @@ -125,7 +125,18 @@ void DemodAnalyzerGUI::onWidgetRolled(QWidget* widget, bool rollDown) (void) widget; (void) rollDown; - getRollupContents()->saveState(m_rollupState); + RollupContents *rollupContents = getRollupContents(); + + if (rollupContents->hasExpandableWidgets()) { + setSizePolicy(sizePolicy().horizontalPolicy(), QSizePolicy::Expanding); + } else { + setSizePolicy(sizePolicy().horizontalPolicy(), QSizePolicy::Fixed); + } + + int h = rollupContents->height() + getAdditionalHeight(); + resize(width(), h); + + rollupContents->saveState(m_rollupState); applySettings(); } @@ -139,11 +150,13 @@ DemodAnalyzerGUI::DemodAnalyzerGUI(PluginAPI* pluginAPI, FeatureUISet *featureUI m_lastFeatureState(0), m_selectedChannel(nullptr) { - ui->setupUi(getRollupContents()); - getRollupContents()->arrangeRollups(); - m_helpURL = "plugins/feature/demodanalyzer/readme.md"; setAttribute(Qt::WA_DeleteOnClose, true); - connect(getRollupContents(), SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + m_helpURL = "plugins/feature/demodanalyzer/readme.md"; + RollupContents *rollupContents = getRollupContents(); + ui->setupUi(rollupContents); + setSizePolicy(rollupContents->sizePolicy()); + rollupContents->arrangeRollups(); + connect(rollupContents, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); m_demodAnalyzer = reinterpret_cast(feature); m_demodAnalyzer->setMessageQueueToGUI(&m_inputMessageQueue); diff --git a/plugins/feature/gs232controller/gs232controllergui.cpp b/plugins/feature/gs232controller/gs232controllergui.cpp index f629f0ff7..695083ee2 100644 --- a/plugins/feature/gs232controller/gs232controllergui.cpp +++ b/plugins/feature/gs232controller/gs232controllergui.cpp @@ -73,7 +73,9 @@ bool GS232ControllerGUI::deserialize(const QByteArray& data) void GS232ControllerGUI::resizeEvent(QResizeEvent* size) { - adjustSize(); + int maxWidth = getRollupContents()->maximumWidth(); + int minHeight = getRollupContents()->minimumHeight() + getAdditionalHeight(); + resize(width() < maxWidth ? width() : maxWidth, minHeight); size->accept(); } @@ -147,12 +149,14 @@ GS232ControllerGUI::GS232ControllerGUI(PluginAPI* pluginAPI, FeatureUISet *featu m_lastFeatureState(0), m_lastOnTarget(false) { - ui->setupUi(getRollupContents()); - setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); - getRollupContents()->arrangeRollups(); - m_helpURL = "plugins/feature/gs232controller/readme.md"; setAttribute(Qt::WA_DeleteOnClose, true); - connect(getRollupContents(), SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + m_helpURL = "plugins/feature/gs232controller/readme.md"; + RollupContents *rollupContents = getRollupContents(); + ui->setupUi(rollupContents); + setSizePolicy(rollupContents->sizePolicy()); + rollupContents->arrangeRollups(); + connect(rollupContents, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + m_gs232Controller = reinterpret_cast(feature); m_gs232Controller->setMessageQueueToGUI(&m_inputMessageQueue); diff --git a/plugins/feature/gs232controller/gs232controllergui.ui b/plugins/feature/gs232controller/gs232controllergui.ui index e159e79ea..b697bc9eb 100644 --- a/plugins/feature/gs232controller/gs232controllergui.ui +++ b/plugins/feature/gs232controller/gs232controllergui.ui @@ -11,7 +11,7 @@
    - + 0 0 @@ -24,7 +24,7 @@ - 360 + 560 16777215 @@ -541,17 +541,17 @@ - - ButtonSwitch - QToolButton -
    gui/buttonswitch.h
    -
    RollupContents QWidget
    gui/rollupcontents.h
    1
    + + ButtonSwitch + QToolButton +
    gui/buttonswitch.h
    +
    startStop diff --git a/plugins/feature/jogdialcontroller/jogdialcontrollergui.cpp b/plugins/feature/jogdialcontroller/jogdialcontrollergui.cpp index dd8b2822a..1ff5381c6 100644 --- a/plugins/feature/jogdialcontroller/jogdialcontrollergui.cpp +++ b/plugins/feature/jogdialcontroller/jogdialcontrollergui.cpp @@ -69,7 +69,9 @@ bool JogdialControllerGUI::deserialize(const QByteArray& data) void JogdialControllerGUI::resizeEvent(QResizeEvent* size) { - adjustSize(); + int maxWidth = getRollupContents()->maximumWidth(); + int minHeight = getRollupContents()->minimumHeight() + getAdditionalHeight(); + resize(width() < maxWidth ? width() : maxWidth, minHeight); size->accept(); } @@ -152,12 +154,13 @@ JogdialControllerGUI::JogdialControllerGUI(PluginAPI* pluginAPI, FeatureUISet *f m_lastFeatureState(0), m_selectedChannel(nullptr) { - ui->setupUi(getRollupContents()); - setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); - getRollupContents()->arrangeRollups(); - m_helpURL = "plugins/feature/jogdialcontroller/readme.md"; setAttribute(Qt::WA_DeleteOnClose, true); - connect(getRollupContents(), SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + m_helpURL = "plugins/feature/jogdialcontroller/readme.md"; + RollupContents *rollupContents = getRollupContents(); + ui->setupUi(rollupContents); + setSizePolicy(rollupContents->sizePolicy()); + rollupContents->arrangeRollups(); + connect(rollupContents, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); m_jogdialController = reinterpret_cast(feature); m_jogdialController->setMessageQueueToGUI(&m_inputMessageQueue); diff --git a/plugins/feature/jogdialcontroller/jogdialcontrollergui.ui b/plugins/feature/jogdialcontroller/jogdialcontrollergui.ui index 26ed65d0f..01211f584 100644 --- a/plugins/feature/jogdialcontroller/jogdialcontrollergui.ui +++ b/plugins/feature/jogdialcontroller/jogdialcontrollergui.ui @@ -6,26 +6,26 @@ 0 0 - 340 - 50 + 370 + 60 - + 0 0 - 340 - 50 + 360 + 60 - 340 - 50 + 370 + 16777215 @@ -46,6 +46,12 @@ 51 + + + 0 + 0 + + 360 diff --git a/plugins/feature/map/mapgui.cpp b/plugins/feature/map/mapgui.cpp index fb199fc80..33aa3073b 100644 --- a/plugins/feature/map/mapgui.cpp +++ b/plugins/feature/map/mapgui.cpp @@ -162,7 +162,18 @@ void MapGUI::onWidgetRolled(QWidget* widget, bool rollDown) (void) widget; (void) rollDown; - getRollupContents()->saveState(m_rollupState); + RollupContents *rollupContents = getRollupContents(); + + if (rollupContents->hasExpandableWidgets()) { + setSizePolicy(sizePolicy().horizontalPolicy(), QSizePolicy::Expanding); + } else { + setSizePolicy(sizePolicy().horizontalPolicy(), QSizePolicy::Fixed); + } + + int h = rollupContents->height() + getAdditionalHeight(); + resize(width(), h); + + rollupContents->saveState(m_rollupState); applySettings(); } @@ -179,9 +190,13 @@ MapGUI::MapGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISet, Feature *featur m_radioTimeDialog(this), m_cesium(nullptr) { - ui->setupUi(getRollupContents()); - getRollupContents()->arrangeRollups(); + setAttribute(Qt::WA_DeleteOnClose, true); m_helpURL = "plugins/feature/map/readme.md"; + RollupContents *rollupContents = getRollupContents(); + ui->setupUi(rollupContents); + setSizePolicy(rollupContents->sizePolicy()); + rollupContents->arrangeRollups(); + connect(rollupContents, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); m_osmPort = 0; m_templateServer = new OSMTemplateServer(thunderforestAPIKey(), maptilerAPIKey(), m_osmPort); @@ -201,8 +216,6 @@ MapGUI::MapGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISet, Feature *featur m_settings.m_modelURL = QString("http://127.0.0.1:%1/3d/").arg(m_webPort); m_webServer->addPathSubstitution("3d", m_settings.m_modelDir); - setAttribute(Qt::WA_DeleteOnClose, true); - connect(getRollupContents(), SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); m_map = reinterpret_cast(feature); m_map->setMessageQueueToGUI(&m_inputMessageQueue); diff --git a/plugins/feature/pertester/pertestergui.cpp b/plugins/feature/pertester/pertestergui.cpp index 45d1bdb4b..5458e46bd 100644 --- a/plugins/feature/pertester/pertestergui.cpp +++ b/plugins/feature/pertester/pertestergui.cpp @@ -71,7 +71,9 @@ bool PERTesterGUI::deserialize(const QByteArray& data) void PERTesterGUI::resizeEvent(QResizeEvent* size) { - adjustSize(); + int maxWidth = getRollupContents()->maximumWidth(); + int minHeight = getRollupContents()->minimumHeight() + getAdditionalHeight(); + resize(width() < maxWidth ? width() : maxWidth, minHeight); size->accept(); } @@ -133,12 +135,14 @@ PERTesterGUI::PERTesterGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISet, Fea m_doApplySettings(true), m_lastFeatureState(0) { - ui->setupUi(getRollupContents()); - setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); - getRollupContents()->arrangeRollups(); - m_helpURL = "plugins/feature/pertester/readme.md"; setAttribute(Qt::WA_DeleteOnClose, true); - connect(getRollupContents(), SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + m_helpURL = "plugins/feature/pertester/readme.md"; + RollupContents *rollupContents = getRollupContents(); + ui->setupUi(rollupContents); + setSizePolicy(rollupContents->sizePolicy()); + rollupContents->arrangeRollups(); + connect(rollupContents, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + m_perTester = reinterpret_cast(feature); m_perTester->setMessageQueueToGUI(&m_inputMessageQueue); diff --git a/plugins/feature/pertester/pertestergui.ui b/plugins/feature/pertester/pertestergui.ui index aa295bd45..f2917bf2b 100644 --- a/plugins/feature/pertester/pertestergui.ui +++ b/plugins/feature/pertester/pertestergui.ui @@ -6,26 +6,26 @@ 0 0 - 335 - 472 + 360 + 477 - + 0 0 - 335 - 472 + 360 + 0 - 335 - 472 + 600 + 16777215 @@ -42,10 +42,16 @@ 2 2 - 331 + 351 341 + + + 0 + 0 + + Settings @@ -464,7 +470,7 @@ Substitutions: 2 352 - 331 + 351 117 diff --git a/plugins/feature/radiosonde/radiosondegui.cpp b/plugins/feature/radiosonde/radiosondegui.cpp index da49edcec..8a60dedd2 100644 --- a/plugins/feature/radiosonde/radiosondegui.cpp +++ b/plugins/feature/radiosonde/radiosondegui.cpp @@ -117,7 +117,18 @@ void RadiosondeGUI::onWidgetRolled(QWidget* widget, bool rollDown) (void) widget; (void) rollDown; - getRollupContents()->saveState(m_rollupState); + RollupContents *rollupContents = getRollupContents(); + + if (rollupContents->hasExpandableWidgets()) { + setSizePolicy(sizePolicy().horizontalPolicy(), QSizePolicy::Expanding); + } else { + setSizePolicy(sizePolicy().horizontalPolicy(), QSizePolicy::Fixed); + } + + int h = rollupContents->height() + getAdditionalHeight(); + resize(width(), h); + + rollupContents->saveState(m_rollupState); applySettings(); } @@ -129,11 +140,14 @@ RadiosondeGUI::RadiosondeGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISet, F m_doApplySettings(true), m_lastFeatureState(0) { - ui->setupUi(getRollupContents()); - getRollupContents()->arrangeRollups(); - m_helpURL = "plugins/feature/radiosonde/readme.md"; setAttribute(Qt::WA_DeleteOnClose, true); - connect(getRollupContents(), SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + m_helpURL = "plugins/feature/radiosonde/readme.md"; + RollupContents *rollupContents = getRollupContents(); + ui->setupUi(rollupContents); + setSizePolicy(rollupContents->sizePolicy()); + rollupContents->arrangeRollups(); + connect(rollupContents, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + m_radiosonde = reinterpret_cast(feature); m_radiosonde->setMessageQueueToGUI(&m_inputMessageQueue); diff --git a/plugins/feature/radiosonde/radiosondegui.ui b/plugins/feature/radiosonde/radiosondegui.ui index 810a16bfa..4dccbfa46 100644 --- a/plugins/feature/radiosonde/radiosondegui.ui +++ b/plugins/feature/radiosonde/radiosondegui.ui @@ -22,12 +22,6 @@ 100 - - - 16777215 - 16777215 - - 9 diff --git a/plugins/feature/rigctlserver/rigctlservergui.cpp b/plugins/feature/rigctlserver/rigctlservergui.cpp index f40c7a23e..a9f5eef5e 100644 --- a/plugins/feature/rigctlserver/rigctlservergui.cpp +++ b/plugins/feature/rigctlserver/rigctlservergui.cpp @@ -71,7 +71,9 @@ bool RigCtlServerGUI::deserialize(const QByteArray& data) void RigCtlServerGUI::resizeEvent(QResizeEvent* size) { - adjustSize(); + int maxWidth = getRollupContents()->maximumWidth(); + int minHeight = getRollupContents()->minimumHeight() + getAdditionalHeight(); + resize(width() < maxWidth ? width() : maxWidth, minHeight); size->accept(); } @@ -133,12 +135,14 @@ RigCtlServerGUI::RigCtlServerGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISe m_doApplySettings(true), m_lastFeatureState(0) { - ui->setupUi(getRollupContents()); - setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); - getRollupContents()->arrangeRollups(); - m_helpURL = "plugins/feature/rigctlserver/readme.md"; setAttribute(Qt::WA_DeleteOnClose, true); - connect(getRollupContents(), SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + m_helpURL = "plugins/feature/rigctlserver/readme.md"; + RollupContents *rollupContents = getRollupContents(); + ui->setupUi(rollupContents); + setSizePolicy(rollupContents->sizePolicy()); + rollupContents->arrangeRollups(); + connect(rollupContents, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + m_rigCtlServer = reinterpret_cast(feature); m_rigCtlServer->setMessageQueueToGUI(&m_inputMessageQueue); diff --git a/plugins/feature/rigctlserver/rigctlservergui.ui b/plugins/feature/rigctlserver/rigctlservergui.ui index 1d896fa27..cef98d408 100644 --- a/plugins/feature/rigctlserver/rigctlservergui.ui +++ b/plugins/feature/rigctlserver/rigctlservergui.ui @@ -6,25 +6,25 @@ 0 0 - 304 + 330 173 - + 0 0 - 304 + 330 173 - 304 + 560 173 @@ -42,7 +42,7 @@ 2 2 - 300 + 328 171 diff --git a/plugins/feature/satellitetracker/satellitetrackergui.cpp b/plugins/feature/satellitetracker/satellitetrackergui.cpp index 6dd97e413..7be65d894 100644 --- a/plugins/feature/satellitetracker/satellitetrackergui.cpp +++ b/plugins/feature/satellitetracker/satellitetrackergui.cpp @@ -230,7 +230,18 @@ void SatelliteTrackerGUI::onWidgetRolled(QWidget* widget, bool rollDown) (void) widget; (void) rollDown; - getRollupContents()->saveState(m_rollupState); + RollupContents *rollupContents = getRollupContents(); + + if (rollupContents->hasExpandableWidgets()) { + setSizePolicy(sizePolicy().horizontalPolicy(), QSizePolicy::Expanding); + } else { + setSizePolicy(sizePolicy().horizontalPolicy(), QSizePolicy::Fixed); + } + + int h = rollupContents->height() + getAdditionalHeight(); + resize(width(), h); + + rollupContents->saveState(m_rollupState); applySettings(); } @@ -248,11 +259,14 @@ SatelliteTrackerGUI::SatelliteTrackerGUI(PluginAPI* pluginAPI, FeatureUISet *fea m_polarChart(nullptr), m_geostationarySatVisible(false) { - ui->setupUi(getRollupContents()); - getRollupContents()->arrangeRollups(); - m_helpURL = "plugins/feature/satellitetracker/readme.md"; setAttribute(Qt::WA_DeleteOnClose, true); - connect(getRollupContents(), SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + m_helpURL = "plugins/feature/satellitetracker/readme.md"; + RollupContents *rollupContents = getRollupContents(); + ui->setupUi(rollupContents); + setSizePolicy(rollupContents->sizePolicy()); + rollupContents->arrangeRollups(); + connect(rollupContents, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + m_satelliteTracker = reinterpret_cast(feature); m_satelliteTracker->setMessageQueueToGUI(&m_inputMessageQueue); diff --git a/plugins/feature/satellitetracker/satellitetrackergui.ui b/plugins/feature/satellitetracker/satellitetrackergui.ui index fb84bab4a..53abf8df6 100644 --- a/plugins/feature/satellitetracker/satellitetrackergui.ui +++ b/plugins/feature/satellitetracker/satellitetrackergui.ui @@ -399,7 +399,7 @@ - + 0 0 diff --git a/plugins/feature/simpleptt/simplepttgui.cpp b/plugins/feature/simpleptt/simplepttgui.cpp index c2e3667d8..981662ece 100644 --- a/plugins/feature/simpleptt/simplepttgui.cpp +++ b/plugins/feature/simpleptt/simplepttgui.cpp @@ -72,7 +72,9 @@ bool SimplePTTGUI::deserialize(const QByteArray& data) void SimplePTTGUI::resizeEvent(QResizeEvent* size) { - adjustSize(); + int maxWidth = getRollupContents()->maximumWidth(); + int minHeight = getRollupContents()->minimumHeight() + getAdditionalHeight(); + resize(width() < maxWidth ? width() : maxWidth, minHeight); size->accept(); } @@ -157,12 +159,14 @@ SimplePTTGUI::SimplePTTGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISet, Fea m_doApplySettings(true), m_lastFeatureState(0) { - ui->setupUi(getRollupContents()); - setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); - getRollupContents()->arrangeRollups(); - m_helpURL = "plugins/feature/simpleptt/readme.md"; setAttribute(Qt::WA_DeleteOnClose, true); - connect(getRollupContents(), SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + m_helpURL = "plugins/feature/simpleptt/readme.md"; + RollupContents *rollupContents = getRollupContents(); + ui->setupUi(rollupContents); + setSizePolicy(rollupContents->sizePolicy()); + rollupContents->arrangeRollups(); + connect(rollupContents, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + m_simplePTT = reinterpret_cast(feature); m_simplePTT->setMessageQueueToGUI(&m_inputMessageQueue); diff --git a/plugins/feature/simpleptt/simplepttgui.ui b/plugins/feature/simpleptt/simplepttgui.ui index 51e2fccd6..e3d49567e 100644 --- a/plugins/feature/simpleptt/simplepttgui.ui +++ b/plugins/feature/simpleptt/simplepttgui.ui @@ -6,25 +6,25 @@ 0 0 - 304 + 320 155 - + 0 0 - 304 + 320 155 - 304 + 360 155 diff --git a/plugins/feature/startracker/startrackergui.cpp b/plugins/feature/startracker/startrackergui.cpp index 8ff5f40fe..2807b1c83 100644 --- a/plugins/feature/startracker/startrackergui.cpp +++ b/plugins/feature/startracker/startrackergui.cpp @@ -224,7 +224,18 @@ void StarTrackerGUI::onWidgetRolled(QWidget* widget, bool rollDown) (void) widget; (void) rollDown; - getRollupContents()->saveState(m_rollupState); + RollupContents *rollupContents = getRollupContents(); + + if (rollupContents->hasExpandableWidgets()) { + setSizePolicy(sizePolicy().horizontalPolicy(), QSizePolicy::Expanding); + } else { + setSizePolicy(sizePolicy().horizontalPolicy(), QSizePolicy::Fixed); + } + + int h = rollupContents->height() + getAdditionalHeight(); + resize(width(), h); + + rollupContents->saveState(m_rollupState); applySettings(); } @@ -254,11 +265,14 @@ StarTrackerGUI::StarTrackerGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISet, m_moonRA(0.0), m_moonDec(0.0) { - ui->setupUi(getRollupContents()); - getRollupContents()->arrangeRollups(); - m_helpURL = "plugins/feature/startracker/readme.md"; setAttribute(Qt::WA_DeleteOnClose, true); - connect(getRollupContents(), SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + m_helpURL = "plugins/feature/startracker/readme.md"; + RollupContents *rollupContents = getRollupContents(); + ui->setupUi(rollupContents); + setSizePolicy(rollupContents->sizePolicy()); + rollupContents->arrangeRollups(); + connect(rollupContents, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + m_starTracker = reinterpret_cast(feature); m_starTracker->setMessageQueueToGUI(&m_inputMessageQueue); diff --git a/plugins/feature/vorlocalizer/vorlocalizergui.cpp b/plugins/feature/vorlocalizer/vorlocalizergui.cpp index dd5c5b0f2..2e4c0b965 100644 --- a/plugins/feature/vorlocalizer/vorlocalizergui.cpp +++ b/plugins/feature/vorlocalizer/vorlocalizergui.cpp @@ -1174,7 +1174,18 @@ void VORLocalizerGUI::onWidgetRolled(QWidget* widget, bool rollDown) (void) widget; (void) rollDown; - getRollupContents()->saveState(m_rollupState); + RollupContents *rollupContents = getRollupContents(); + + if (rollupContents->hasExpandableWidgets()) { + setSizePolicy(sizePolicy().horizontalPolicy(), QSizePolicy::Expanding); + } else { + setSizePolicy(sizePolicy().horizontalPolicy(), QSizePolicy::Fixed); + } + + int h = rollupContents->height() + getAdditionalHeight(); + resize(width(), h); + + rollupContents->saveState(m_rollupState); applySettings(); } @@ -1224,9 +1235,13 @@ VORLocalizerGUI::VORLocalizerGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISe m_lastFeatureState(0), m_rrSecondsCount(0) { - ui->setupUi(getRollupContents()); - getRollupContents()->arrangeRollups(); + setAttribute(Qt::WA_DeleteOnClose, true); m_helpURL = "plugins/feature/vorlocalizer/readme.md"; + RollupContents *rollupContents = getRollupContents(); + ui->setupUi(rollupContents); + setSizePolicy(rollupContents->sizePolicy()); + rollupContents->arrangeRollups(); + connect(rollupContents, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); ui->map->rootContext()->setContextProperty("vorModel", &m_vorModel); ui->map->setSource(QUrl(QStringLiteral("qrc:/demodvor/map/map.qml"))); @@ -1234,8 +1249,6 @@ VORLocalizerGUI::VORLocalizerGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISe m_muteIcon.addPixmap(QPixmap("://sound_off.png"), QIcon::Normal, QIcon::On); m_muteIcon.addPixmap(QPixmap("://sound_on.png"), QIcon::Normal, QIcon::Off); - setAttribute(Qt::WA_DeleteOnClose, true); - connect(getRollupContents(), SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &))); connect(&m_dlm, &HttpDownloadManager::downloadComplete, this, &VORLocalizerGUI::downloadFinished); diff --git a/plugins/feature/vorlocalizer/vorlocalizergui.ui b/plugins/feature/vorlocalizer/vorlocalizergui.ui index 2824b96e4..4efe999be 100644 --- a/plugins/feature/vorlocalizer/vorlocalizergui.ui +++ b/plugins/feature/vorlocalizer/vorlocalizergui.ui @@ -7,7 +7,7 @@ 0 0 462 - 893 + 850 @@ -348,7 +348,7 @@ QToolTip{background-color: white; color: black;} 0 110 461 - 140 + 145 @@ -357,6 +357,12 @@ QToolTip{background-color: white; color: black;} 0 + + + 0 + 145 + + VORs @@ -477,7 +483,7 @@ QToolTip{background-color: white; color: black;} 0 - 260 + 258 461 581 From 5f9d14a56eaf8a846b1cc16a8c8961d4875c5197 Mon Sep 17 00:00:00 2001 From: f4exb Date: Mon, 25 Apr 2022 00:15:46 +0200 Subject: [PATCH 062/115] Massive UI revamping (v7): Fixed sizing of some device windows. Part of #1209 --- plugins/samplemimo/metismiso/metismisogui.ui | 2 +- plugins/samplemimo/testmi/testmigui.cpp | 9 +- .../samplemimo/testmosync/testmosyncgui.cpp | 8 +- .../samplemimo/testmosync/testmosyncgui.ui | 68 +++++++------- .../samplesink/audiooutput/audiooutputgui.ui | 8 +- .../samplesink/fileoutput/fileoutputgui.ui | 8 +- .../hackrfoutput/hackrfoutputgui.ui | 8 +- plugins/samplesink/testsink/testsinkgui.cpp | 7 +- plugins/samplesink/testsink/testsinkgui.ui | 90 ++++++++++--------- .../sigmffileinput/sigmffileinputgui.cpp | 13 +-- .../sigmffileinput/sigmffileinputgui.h | 3 - .../sigmffileinput/sigmffileinputgui.ui | 6 +- sdrgui/channel/channelgui.cpp | 1 + sdrgui/device/devicegui.cpp | 2 +- sdrgui/feature/featuregui.cpp | 1 + 15 files changed, 124 insertions(+), 110 deletions(-) diff --git a/plugins/samplemimo/metismiso/metismisogui.ui b/plugins/samplemimo/metismiso/metismisogui.ui index dd5259f47..15158d88d 100644 --- a/plugins/samplemimo/metismiso/metismisogui.ui +++ b/plugins/samplemimo/metismiso/metismisogui.ui @@ -11,7 +11,7 @@ - + 0 0 diff --git a/plugins/samplemimo/testmi/testmigui.cpp b/plugins/samplemimo/testmi/testmigui.cpp index 1d4ece70a..99752212a 100644 --- a/plugins/samplemimo/testmi/testmigui.cpp +++ b/plugins/samplemimo/testmi/testmigui.cpp @@ -55,6 +55,11 @@ TestMIGui::TestMIGui(DeviceUISet *deviceUISet, QWidget* parent) : { qDebug("TestMIGui::TestMIGui"); setAttribute(Qt::WA_DeleteOnClose, true); + m_helpURL = "plugins/samplemimo/testmi/readme.md"; + ui->setupUi(getContents()); + setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + getContents()->setStyleSheet("#TestMIGui { background-color: rgb(64, 64, 64); }"); + m_sampleMIMO = m_deviceUISet->m_deviceAPI->getSampleMIMO(); m_streamIndex = 0; m_deviceCenterFrequencies.push_back(m_settings.m_streams[0].m_centerFrequency); @@ -62,10 +67,6 @@ TestMIGui::TestMIGui(DeviceUISet *deviceUISet, QWidget* parent) : m_deviceSampleRates.push_back(m_settings.m_streams[0].m_sampleRate / (1<setupUi(getContents()); - setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); - getContents()->setStyleSheet("#TestMIGui { background-color: rgb(64, 64, 64); }"); - m_helpURL = "plugins/samplemimo/testmi/readme.md"; ui->spectrumSource->addItem("0"); ui->spectrumSource->addItem("1"); ui->centerFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold)); diff --git a/plugins/samplemimo/testmosync/testmosyncgui.cpp b/plugins/samplemimo/testmosync/testmosyncgui.cpp index abd91236b..764414e2d 100644 --- a/plugins/samplemimo/testmosync/testmosyncgui.cpp +++ b/plugins/samplemimo/testmosync/testmosyncgui.cpp @@ -51,10 +51,12 @@ TestMOSyncGui::TestMOSyncGui(DeviceUISet *deviceUISet, QWidget* parent) : m_lastEngineState(DeviceAPI::StNotStarted) { setAttribute(Qt::WA_DeleteOnClose, true); - ui->setupUi(getContents()); - setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); - getContents()->setStyleSheet("#TestMOSyncGui { background-color: rgb(64, 64, 64); }"); m_helpURL = "plugins/samplemimo/testmosync/readme.md"; + QWidget *contents = getContents(); + ui->setupUi(contents); + setSizePolicy(contents->sizePolicy()); + + getContents()->setStyleSheet("#TestMOSyncGui { background-color: rgb(64, 64, 64); }"); m_sampleMIMO = (TestMOSync*) m_deviceUISet->m_deviceAPI->getSampleMIMO(); ui->centerFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold)); diff --git a/plugins/samplemimo/testmosync/testmosyncgui.ui b/plugins/samplemimo/testmosync/testmosyncgui.ui index f1313b9b6..ce43ea830 100644 --- a/plugins/samplemimo/testmosync/testmosyncgui.ui +++ b/plugins/samplemimo/testmosync/testmosyncgui.ui @@ -7,11 +7,11 @@ 0 0 360 - 320 + 420 - + 0 0 @@ -19,7 +19,7 @@ 360 - 0 + 420 @@ -335,33 +335,41 @@ - - - - - - 0 - 0 - - - - - 200 - 200 - - - - - Liberation Mono - 8 - - - - - - - - + + + + 0 + 0 + + + + + + + + 0 + 0 + + + + + 200 + 200 + + + + + Liberation Mono + 8 + + + + + + + + + diff --git a/plugins/samplesink/audiooutput/audiooutputgui.ui b/plugins/samplesink/audiooutput/audiooutputgui.ui index bdbe5c47a..07a82a594 100644 --- a/plugins/samplesink/audiooutput/audiooutputgui.ui +++ b/plugins/samplesink/audiooutput/audiooutputgui.ui @@ -7,11 +7,11 @@ 0 0 360 - 106 + 124 - + 0 0 @@ -19,13 +19,13 @@ 360 - 106 + 124 360 - 106 + 143 diff --git a/plugins/samplesink/fileoutput/fileoutputgui.ui b/plugins/samplesink/fileoutput/fileoutputgui.ui index f089af530..b525560bf 100644 --- a/plugins/samplesink/fileoutput/fileoutputgui.ui +++ b/plugins/samplesink/fileoutput/fileoutputgui.ui @@ -7,11 +7,11 @@ 0 0 360 - 106 + 126 - + 0 0 @@ -19,13 +19,13 @@ 360 - 106 + 126 360 - 106 + 144 diff --git a/plugins/samplesink/hackrfoutput/hackrfoutputgui.ui b/plugins/samplesink/hackrfoutput/hackrfoutputgui.ui index bb34250dc..a0a70366e 100644 --- a/plugins/samplesink/hackrfoutput/hackrfoutputgui.ui +++ b/plugins/samplesink/hackrfoutput/hackrfoutputgui.ui @@ -7,7 +7,7 @@ 0 0 360 - 151 + 174 @@ -19,13 +19,13 @@ 360 - 151 + 174 360 - 151 + 207 @@ -39,7 +39,7 @@ - 3 + 2 2 diff --git a/plugins/samplesink/testsink/testsinkgui.cpp b/plugins/samplesink/testsink/testsinkgui.cpp index 471f6561d..ecccbf97c 100644 --- a/plugins/samplesink/testsink/testsinkgui.cpp +++ b/plugins/samplesink/testsink/testsinkgui.cpp @@ -50,11 +50,12 @@ TestSinkGui::TestSinkGui(DeviceUISet *deviceUISet, QWidget* parent) : m_lastEngineState(DeviceAPI::StNotStarted) { setAttribute(Qt::WA_DeleteOnClose, true); - ui->setupUi(getContents()); - setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + m_helpURL = "plugins/samplesink/testsink/readme.md"; + QWidget *contents = getContents(); + ui->setupUi(contents); + setSizePolicy(contents->sizePolicy()); setMinimumSize(m_MinimumWidth, m_MinimumHeight); getContents()->setStyleSheet("#TestSinkGui { background-color: rgb(64, 64, 64); }"); - m_helpURL = "plugins/samplesink/testsink/readme.md"; m_sampleSink = (TestSinkOutput*) m_deviceUISet->m_deviceAPI->getSampleSink(); ui->centerFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold)); diff --git a/plugins/samplesink/testsink/testsinkgui.ui b/plugins/samplesink/testsink/testsinkgui.ui index c525546f6..37ecfb351 100644 --- a/plugins/samplesink/testsink/testsinkgui.ui +++ b/plugins/samplesink/testsink/testsinkgui.ui @@ -11,7 +11,7 @@ - + 0 0 @@ -290,46 +290,54 @@ - - - - - - 0 - 0 - - - - - 354 - 200 - - - - - Liberation Mono - 8 - - - - - - - - - 0 - 0 - - - - - 354 - 30 - - - - - + + + + 0 + 0 + + + + + + + + 0 + 0 + + + + + 354 + 200 + + + + + Liberation Mono + 8 + + + + + + + + + 0 + 0 + + + + + 354 + 30 + + + + + + diff --git a/plugins/samplesource/sigmffileinput/sigmffileinputgui.cpp b/plugins/samplesource/sigmffileinput/sigmffileinputgui.cpp index 07e165b06..343585106 100644 --- a/plugins/samplesource/sigmffileinput/sigmffileinputgui.cpp +++ b/plugins/samplesource/sigmffileinput/sigmffileinputgui.cpp @@ -62,10 +62,11 @@ SigMFFileInputGUI::SigMFFileInputGUI(DeviceUISet *deviceUISet, QWidget* parent) m_lastEngineState(DeviceAPI::StNotStarted) { setAttribute(Qt::WA_DeleteOnClose, true); - ui->setupUi(getContents()); - setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding); - getContents()->setStyleSheet("#SigMFFileInputGUI { background-color: rgb(64, 64, 64); }"); m_helpURL = "plugins/samplesource/sigmffileinput/readme.md"; + QWidget *contents = getContents(); + ui->setupUi(contents); + setSizePolicy(contents->sizePolicy()); + contents->setStyleSheet("#SigMFFileInputGUI { background-color: rgb(64, 64, 64); }"); ui->fileNameText->setText(m_metaFileName); ui->crcLabel->setStyleSheet("QLabel { background:rgb(79,79,79); }"); @@ -130,12 +131,6 @@ bool SigMFFileInputGUI::deserialize(const QByteArray& data) } } -void SigMFFileInputGUI::resizeEvent(QResizeEvent* size) -{ - resize(436, height()); - size->accept(); -} - void SigMFFileInputGUI::handleInputMessages() { Message* message; diff --git a/plugins/samplesource/sigmffileinput/sigmffileinputgui.h b/plugins/samplesource/sigmffileinput/sigmffileinputgui.h index 30f3cf9a3..381ad5ea9 100644 --- a/plugins/samplesource/sigmffileinput/sigmffileinputgui.h +++ b/plugins/samplesource/sigmffileinput/sigmffileinputgui.h @@ -47,9 +47,6 @@ public: bool deserialize(const QByteArray& data); virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } -protected: - void resizeEvent(QResizeEvent* size); - private: Ui::SigMFFileInputGUI* ui; diff --git a/plugins/samplesource/sigmffileinput/sigmffileinputgui.ui b/plugins/samplesource/sigmffileinput/sigmffileinputgui.ui index 8ef55dd12..35307e9bc 100644 --- a/plugins/samplesource/sigmffileinput/sigmffileinputgui.ui +++ b/plugins/samplesource/sigmffileinput/sigmffileinputgui.ui @@ -6,7 +6,7 @@ 0 0 - 436 + 450 436 @@ -18,13 +18,13 @@ - 436 + 450 436 - 436 + 450 16777215 diff --git a/sdrgui/channel/channelgui.cpp b/sdrgui/channel/channelgui.cpp index 106df5191..4772363e9 100644 --- a/sdrgui/channel/channelgui.cpp +++ b/sdrgui/channel/channelgui.cpp @@ -56,6 +56,7 @@ ChannelGUI::ChannelGUI(QWidget *parent) : m_indexLabel->setToolTip("Channel index"); m_settingsButton = new QPushButton(); + m_settingsButton->setFixedSize(20, 20); QIcon settingsIcon(":/gear.png"); m_settingsButton->setIcon(settingsIcon); m_settingsButton->setToolTip("Common settings"); diff --git a/sdrgui/device/devicegui.cpp b/sdrgui/device/devicegui.cpp index 2b5d2cc5f..a782cdff5 100644 --- a/sdrgui/device/devicegui.cpp +++ b/sdrgui/device/devicegui.cpp @@ -163,7 +163,7 @@ DeviceGUI::DeviceGUI(QWidget *parent) : m_bottomLayout->addWidget(m_statusLabel); m_sizeGripBottomRight = new QSizeGrip(this); m_sizeGripBottomRight->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); - m_sizeGripBottomRight->setFixedHeight(20); + // m_bottomLayout->addStretch(1); m_bottomLayout->addWidget(m_sizeGripBottomRight, 0, Qt::AlignBottom | Qt::AlignRight); diff --git a/sdrgui/feature/featuregui.cpp b/sdrgui/feature/featuregui.cpp index 7b732652f..c7d1f6481 100644 --- a/sdrgui/feature/featuregui.cpp +++ b/sdrgui/feature/featuregui.cpp @@ -51,6 +51,7 @@ FeatureGUI::FeatureGUI(QWidget *parent) : m_indexLabel->setToolTip("Feature index"); m_settingsButton = new QPushButton(); + m_settingsButton->setFixedSize(20, 20); QIcon settingsIcon(":/gear.png"); m_settingsButton->setIcon(settingsIcon); m_settingsButton->setToolTip("Common settings"); From 3259cf7f0a9220e132fb54afa59822d31606d0ed Mon Sep 17 00:00:00 2001 From: f4exb Date: Mon, 25 Apr 2022 01:18:07 +0200 Subject: [PATCH 063/115] Massive UI revamping (v7): Fixed random deletion of device sets. Fixes #1211 --- sdrbase/dsp/dspengine.cpp | 33 +++++++++++++++------------------ sdrbase/dsp/dspengine.h | 6 ++++-- 2 files changed, 19 insertions(+), 20 deletions(-) diff --git a/sdrbase/dsp/dspengine.cpp b/sdrbase/dsp/dspengine.cpp index 8db297d79..de1561cd3 100644 --- a/sdrbase/dsp/dspengine.cpp +++ b/sdrbase/dsp/dspengine.cpp @@ -63,7 +63,7 @@ DSPDeviceSourceEngine *DSPEngine::addDeviceSourceEngine() { m_deviceSourceEngines.push_back(new DSPDeviceSourceEngine(m_deviceSourceEnginesUIDSequence)); m_deviceSourceEnginesUIDSequence++; - m_deviceEngineReferences.push_back(DeviceEngineReference{0, m_deviceSourceEngines.size() - 1}); + m_deviceEngineReferences.push_back(DeviceEngineReference{0, m_deviceSourceEngines.back(), nullptr, nullptr}); return m_deviceSourceEngines.back(); } @@ -72,13 +72,12 @@ void DSPEngine::removeLastDeviceSourceEngine() if (m_deviceSourceEngines.size() > 0) { DSPDeviceSourceEngine *lastDeviceEngine = m_deviceSourceEngines.back(); - int lastSourceDeviceEngineIndex = m_deviceSourceEngines.size() - 1; delete lastDeviceEngine; m_deviceSourceEngines.pop_back(); for (int i = 0; i < m_deviceEngineReferences.size(); i++) { - if (m_deviceEngineReferences[i].deviceEngineIndex == lastSourceDeviceEngineIndex) + if (m_deviceEngineReferences[i].m_deviceSourceEngine == lastDeviceEngine) { m_deviceEngineReferences.removeAt(i); break; @@ -91,7 +90,7 @@ DSPDeviceSinkEngine *DSPEngine::addDeviceSinkEngine() { m_deviceSinkEngines.push_back(new DSPDeviceSinkEngine(m_deviceSinkEnginesUIDSequence)); m_deviceSinkEnginesUIDSequence++; - m_deviceEngineReferences.push_back(DeviceEngineReference{1, m_deviceSinkEngines.size() - 1}); + m_deviceEngineReferences.push_back(DeviceEngineReference{1, nullptr, m_deviceSinkEngines.back(), nullptr}); return m_deviceSinkEngines.back(); } @@ -100,13 +99,12 @@ void DSPEngine::removeLastDeviceSinkEngine() if (m_deviceSinkEngines.size() > 0) { DSPDeviceSinkEngine *lastDeviceEngine = m_deviceSinkEngines.back(); - int lastSinkDeviceEngineIndex = m_deviceSinkEngines.size() - 1; delete lastDeviceEngine; m_deviceSinkEngines.pop_back(); for (int i = 0; i < m_deviceEngineReferences.size(); i++) { - if (m_deviceEngineReferences[i].deviceEngineIndex == lastSinkDeviceEngineIndex) + if (m_deviceEngineReferences[i].m_deviceSinkEngine == lastDeviceEngine) { m_deviceEngineReferences.removeAt(i); break; @@ -119,7 +117,7 @@ DSPDeviceMIMOEngine *DSPEngine::addDeviceMIMOEngine() { m_deviceMIMOEngines.push_back(new DSPDeviceMIMOEngine(m_deviceMIMOEnginesUIDSequence)); m_deviceMIMOEnginesUIDSequence++; - m_deviceEngineReferences.push_back(DeviceEngineReference{2, m_deviceMIMOEngines.size() - 1}); + m_deviceEngineReferences.push_back(DeviceEngineReference{2, nullptr, nullptr, m_deviceMIMOEngines.back()}); return m_deviceMIMOEngines.back(); } @@ -128,13 +126,12 @@ void DSPEngine::removeLastDeviceMIMOEngine() if (m_deviceMIMOEngines.size() > 0) { DSPDeviceMIMOEngine *lastDeviceEngine = m_deviceMIMOEngines.back(); - int lastMIMODeviceEngineIndex = m_deviceMIMOEngines.size() - 1; delete lastDeviceEngine; m_deviceMIMOEngines.pop_back(); for (int i = 0; i < m_deviceEngineReferences.size(); i++) { - if (m_deviceEngineReferences[i].deviceEngineIndex == lastMIMODeviceEngineIndex) + if (m_deviceEngineReferences[i].m_deviceMIMOEngine == lastDeviceEngine) { m_deviceEngineReferences.removeAt(i); break; @@ -149,23 +146,23 @@ void DSPEngine::removeDeviceEngineAt(int deviceIndex) return; } - if (m_deviceEngineReferences[deviceIndex].deviceEngineTYpe == 0) // source + if (m_deviceEngineReferences[deviceIndex].m_deviceEngineType == 0) // source { - DSPDeviceSourceEngine *deviceEngine = m_deviceSourceEngines[m_deviceEngineReferences[deviceIndex].deviceEngineIndex]; + DSPDeviceSourceEngine *deviceEngine = m_deviceEngineReferences[deviceIndex].m_deviceSourceEngine; delete deviceEngine; - m_deviceSourceEngines.removeAt(m_deviceEngineReferences[deviceIndex].deviceEngineIndex); + m_deviceSourceEngines.removeAll(deviceEngine); } - else if (m_deviceEngineReferences[deviceIndex].deviceEngineTYpe == 1) // sink + else if (m_deviceEngineReferences[deviceIndex].m_deviceEngineType == 1) // sink { - DSPDeviceSinkEngine *deviceEngine = m_deviceSinkEngines[m_deviceEngineReferences[deviceIndex].deviceEngineIndex]; + DSPDeviceSinkEngine *deviceEngine = m_deviceEngineReferences[deviceIndex].m_deviceSinkEngine; delete deviceEngine; - m_deviceSinkEngines.removeAt(m_deviceEngineReferences[deviceIndex].deviceEngineIndex); + m_deviceSinkEngines.removeAll(deviceEngine); } - else if (m_deviceEngineReferences[deviceIndex].deviceEngineTYpe == 2) // MIMO + else if (m_deviceEngineReferences[deviceIndex].m_deviceEngineType == 2) // MIMO { - DSPDeviceMIMOEngine *deviceEngine = m_deviceMIMOEngines[m_deviceEngineReferences[deviceIndex].deviceEngineIndex]; + DSPDeviceMIMOEngine *deviceEngine = m_deviceEngineReferences[deviceIndex].m_deviceMIMOEngine; delete deviceEngine; - m_deviceMIMOEngines.removeAt(m_deviceEngineReferences[deviceIndex].deviceEngineIndex); + m_deviceMIMOEngines.removeAll(deviceEngine); } m_deviceEngineReferences.removeAt(deviceIndex); diff --git a/sdrbase/dsp/dspengine.h b/sdrbase/dsp/dspengine.h index ab8fd1fe7..1f1bafbd1 100644 --- a/sdrbase/dsp/dspengine.h +++ b/sdrbase/dsp/dspengine.h @@ -90,8 +90,10 @@ public: private: struct DeviceEngineReference { - int deviceEngineTYpe; //!< 0: Rx, 1: Tx, 2: MIMO - int deviceEngineIndex; + int m_deviceEngineType; //!< 0: Rx, 1: Tx, 2: MIMO + DSPDeviceSourceEngine *m_deviceSourceEngine; + DSPDeviceSinkEngine *m_deviceSinkEngine; + DSPDeviceMIMOEngine *m_deviceMIMOEngine; }; QList m_deviceSourceEngines; From 3f1ed5b934879a13dbf823af1f7112e547441de0 Mon Sep 17 00:00:00 2001 From: Jon Beniston Date: Mon, 25 Apr 2022 15:01:59 +0100 Subject: [PATCH 064/115] Restore cursor when moved over child widget --- sdrgui/channel/channelgui.cpp | 2 ++ sdrgui/device/devicegui.cpp | 2 ++ sdrgui/feature/featuregui.cpp | 2 ++ sdrgui/gui/framelesswindowresizer.cpp | 8 ++++++++ sdrgui/gui/framelesswindowresizer.h | 3 +++ sdrgui/gui/glspectrum.cpp | 2 ++ sdrgui/mainspectrum/mainspectrumgui.cpp | 1 + 7 files changed, 20 insertions(+) diff --git a/sdrgui/channel/channelgui.cpp b/sdrgui/channel/channelgui.cpp index 4772363e9..6f2f91cf0 100644 --- a/sdrgui/channel/channelgui.cpp +++ b/sdrgui/channel/channelgui.cpp @@ -184,6 +184,8 @@ ChannelGUI::ChannelGUI(QWidget *parent) : this, &ChannelGUI::onWidgetRolled ); + + m_resizer.enableChildMouseTracking(); } ChannelGUI::~ChannelGUI() diff --git a/sdrgui/device/devicegui.cpp b/sdrgui/device/devicegui.cpp index a782cdff5..eefa2355b 100644 --- a/sdrgui/device/devicegui.cpp +++ b/sdrgui/device/devicegui.cpp @@ -193,6 +193,8 @@ DeviceGUI::DeviceGUI(QWidget *parent) : this, &DeviceGUI::addChannelEmitted ); + + m_resizer.enableChildMouseTracking(); } DeviceGUI::~DeviceGUI() diff --git a/sdrgui/feature/featuregui.cpp b/sdrgui/feature/featuregui.cpp index c7d1f6481..583256a18 100644 --- a/sdrgui/feature/featuregui.cpp +++ b/sdrgui/feature/featuregui.cpp @@ -141,6 +141,8 @@ FeatureGUI::FeatureGUI(QWidget *parent) : this, &FeatureGUI::onWidgetRolled ); + + m_resizer.enableChildMouseTracking(); } FeatureGUI::~FeatureGUI() diff --git a/sdrgui/gui/framelesswindowresizer.cpp b/sdrgui/gui/framelesswindowresizer.cpp index ba5c0d7c5..ff5e4a8b3 100644 --- a/sdrgui/gui/framelesswindowresizer.cpp +++ b/sdrgui/gui/framelesswindowresizer.cpp @@ -34,6 +34,14 @@ FramelessWindowResizer::FramelessWindowResizer(QWidget *widget) : { } +void FramelessWindowResizer::enableChildMouseTracking() +{ + QList widgets = m_widget->findChildren(); + for (auto widget : widgets) { + widget->setMouseTracking(true); + } +} + bool FramelessWindowResizer::mouseOnTopBorder(QPoint pos) const { return (pos.y() >= 0) && (pos.y() < m_gripSize); diff --git a/sdrgui/gui/framelesswindowresizer.h b/sdrgui/gui/framelesswindowresizer.h index 7b2e106eb..c3827c4ba 100644 --- a/sdrgui/gui/framelesswindowresizer.h +++ b/sdrgui/gui/framelesswindowresizer.h @@ -28,6 +28,8 @@ // by clicking and draging on the border // The window needs to forward the mousePressEvent, mouseReleaseEvent, mouseMoveEvent // and leaveEvent events to this class +// Child widgets should have mouse tracking enabled, so cursor can be controlled properly +// This can be achieved by calling enableChildMouseTracking class SDRGUI_API FramelessWindowResizer { private: @@ -49,6 +51,7 @@ private: public: FramelessWindowResizer(QWidget *widget); + void enableChildMouseTracking(); void mousePressEvent(QMouseEvent* event); void mouseReleaseEvent(QMouseEvent* event); void mouseMoveEvent(QMouseEvent* event); diff --git a/sdrgui/gui/glspectrum.cpp b/sdrgui/gui/glspectrum.cpp index f6a6485e4..4004ed4c5 100644 --- a/sdrgui/gui/glspectrum.cpp +++ b/sdrgui/gui/glspectrum.cpp @@ -2565,6 +2565,8 @@ void GLSpectrum::mouseMoveEvent(QMouseEvent* event) return; } + + event->setAccepted(false); } void GLSpectrum::mousePressEvent(QMouseEvent* event) diff --git a/sdrgui/mainspectrum/mainspectrumgui.cpp b/sdrgui/mainspectrum/mainspectrumgui.cpp index f0424c16c..dfc44d99e 100644 --- a/sdrgui/mainspectrum/mainspectrumgui.cpp +++ b/sdrgui/mainspectrum/mainspectrumgui.cpp @@ -140,6 +140,7 @@ MainSpectrumGUI::MainSpectrumGUI(GLSpectrum *spectrum, GLSpectrumGUI *spectrumGU connect(this, SIGNAL(forceShrink()), this, SLOT(shrinkWindow())); connect(m_hideButton, SIGNAL(clicked()), this, SLOT(hide())); + m_resizer.enableChildMouseTracking(); shrinkWindow(); } From dc303cdd7d21775deb53220b3f6daa27cc0c34b1 Mon Sep 17 00:00:00 2001 From: f4exb Date: Mon, 25 Apr 2022 23:48:14 +0200 Subject: [PATCH 065/115] Massive UI revamping (v7): Renumerate workspaces and workspace index in sub windows after empty workspace trimmin. Fixes #1212 --- sdrgui/gui/workspace.cpp | 13 +++++++++++++ sdrgui/gui/workspace.h | 2 ++ sdrgui/mainwindow.cpp | 27 +++++++++++++++++++++++++++ 3 files changed, 42 insertions(+) diff --git a/sdrgui/gui/workspace.cpp b/sdrgui/gui/workspace.cpp index a40b82bee..a9345417e 100644 --- a/sdrgui/gui/workspace.cpp +++ b/sdrgui/gui/workspace.cpp @@ -215,6 +215,19 @@ Workspace::~Workspace() qDebug("Workspace::~Workspace: end"); } +void Workspace::setIndex(int index) +{ + m_index = index; + setWindowTitle(tr("W%1").arg(m_index)); + setObjectName(tr("W%1").arg(m_index)); + m_titleLabel->setText(windowTitle()); +} + +QList Workspace::getSubWindowList() const +{ + return m_mdi->subWindowList(); +} + void Workspace::toggleFloating() { setFloating(!isFloating()); diff --git a/sdrgui/gui/workspace.h b/sdrgui/gui/workspace.h index 309332c15..fff92eaa7 100644 --- a/sdrgui/gui/workspace.h +++ b/sdrgui/gui/workspace.h @@ -40,6 +40,7 @@ public: ~Workspace(); int getIndex() const { return m_index; } + void setIndex(int index); void resetAvailableFeatures() { m_featureAddDialog.resetFeatureNames(); } void addAvailableFeatures(const QStringList& featureNames) { m_featureAddDialog.addFeatureNames(featureNames); } void addToMdiArea(QMdiSubWindow *sub); @@ -47,6 +48,7 @@ public: int getNumberOfSubWindows() const; QByteArray saveMdiGeometry(); void restoreMdiGeometry(const QByteArray& blob); + QList getSubWindowList() const; private: int m_index; diff --git a/sdrgui/mainwindow.cpp b/sdrgui/mainwindow.cpp index 6fcd1cc19..338c08567 100644 --- a/sdrgui/mainwindow.cpp +++ b/sdrgui/mainwindow.cpp @@ -1830,6 +1830,33 @@ void MainWindow::removeEmptyWorkspaces() ++it; } } + + // Renumerate + for (int i = 0; i < m_workspaces.size(); i++) + { + Workspace *workspace = m_workspaces[i]; + workspace->setIndex(i); + QList subWindows = workspace->getSubWindowList(); + + for (auto& subWindow : subWindows) + { + if (qobject_cast(subWindow)) { + qobject_cast(subWindow)->setWorkspaceIndex(i); + } + + if (qobject_cast(subWindow)) { + qobject_cast(subWindow)->setWorkspaceIndex(i); + } + + if (qobject_cast(subWindow)) { + qobject_cast(subWindow)->setWorkspaceIndex(i); + } + + if (qobject_cast(subWindow)) { + qobject_cast(subWindow)->setWorkspaceIndex(i); + } + } + } } void MainWindow::on_action_View_Fullscreen_toggled(bool checked) From c6baed9f730ae80b7664b54411509efe6bfd7227 Mon Sep 17 00:00:00 2001 From: f4exb Date: Tue, 26 Apr 2022 00:42:26 +0200 Subject: [PATCH 066/115] Massive UI revamping (v7): Restored channel highlighted by cursor function. Part of #1213 --- plugins/channelrx/chanalyzer/chanalyzergui.cpp | 2 +- plugins/channelrx/demodadsb/adsbdemodgui.cpp | 2 +- plugins/channelrx/demodais/aisdemodgui.cpp | 2 +- plugins/channelrx/demodam/amdemodgui.cpp | 2 +- plugins/channelrx/demodapt/aptdemodgui.cpp | 2 +- plugins/channelrx/demodatv/atvdemodgui.cpp | 2 +- plugins/channelrx/demodbfm/bfmdemodgui.cpp | 2 +- .../channelrx/demodchirpchat/chirpchatdemodgui.cpp | 2 +- plugins/channelrx/demoddab/dabdemodgui.cpp | 2 +- plugins/channelrx/demoddatv/datvdemodgui.cpp | 2 +- plugins/channelrx/demoddsd/dsddemodgui.cpp | 2 +- plugins/channelrx/demodfreedv/freedvdemodgui.cpp | 2 +- plugins/channelrx/demodnfm/nfmdemodgui.cpp | 2 +- plugins/channelrx/demodpacket/packetdemodgui.cpp | 2 +- plugins/channelrx/demodpager/pagerdemodgui.cpp | 2 +- .../channelrx/demodradiosonde/radiosondedemodgui.cpp | 2 +- plugins/channelrx/demodssb/ssbdemodgui.cpp | 2 +- plugins/channelrx/demodvor/vordemodgui.cpp | 2 +- plugins/channelrx/demodvorsc/vordemodscgui.cpp | 2 +- plugins/channelrx/demodwfm/wfmdemodgui.cpp | 2 +- plugins/channelrx/filesink/filesinkgui.cpp | 2 +- plugins/channelrx/freqtracker/freqtrackergui.cpp | 2 +- plugins/channelrx/noisefigure/noisefiguregui.cpp | 2 +- .../channelrx/radioastronomy/radioastronomygui.cpp | 2 +- plugins/channelrx/radioclock/radioclockgui.cpp | 2 +- plugins/channelrx/sigmffilesink/sigmffilesinkgui.cpp | 2 +- plugins/channelrx/udpsink/udpsinkgui.cpp | 2 +- sdrgui/channel/channelgui.cpp | 11 +++++++++-- sdrgui/channel/channelgui.h | 1 + sdrgui/gui/rollupcontents.cpp | 12 +----------- sdrgui/gui/rollupcontents.h | 2 -- 31 files changed, 38 insertions(+), 42 deletions(-) diff --git a/plugins/channelrx/chanalyzer/chanalyzergui.cpp b/plugins/channelrx/chanalyzer/chanalyzergui.cpp index ce061c88a..54fdddb94 100644 --- a/plugins/channelrx/chanalyzer/chanalyzergui.cpp +++ b/plugins/channelrx/chanalyzer/chanalyzergui.cpp @@ -286,7 +286,7 @@ void ChannelAnalyzerGUI::channelMarkerChangedByCursor() void ChannelAnalyzerGUI::channelMarkerHighlightedByCursor() { - getRollupContents()->setHighlighted(m_channelMarker.getHighlighted()); + setHighlighted(m_channelMarker.getHighlighted()); } void ChannelAnalyzerGUI::tick() diff --git a/plugins/channelrx/demodadsb/adsbdemodgui.cpp b/plugins/channelrx/demodadsb/adsbdemodgui.cpp index ee16b190d..4e6ef181e 100644 --- a/plugins/channelrx/demodadsb/adsbdemodgui.cpp +++ b/plugins/channelrx/demodadsb/adsbdemodgui.cpp @@ -2209,7 +2209,7 @@ void ADSBDemodGUI::channelMarkerChangedByCursor() void ADSBDemodGUI::channelMarkerHighlightedByCursor() { - getRollupContents()->setHighlighted(m_channelMarker.getHighlighted()); + setHighlighted(m_channelMarker.getHighlighted()); } void ADSBDemodGUI::on_deltaFrequency_changed(qint64 value) diff --git a/plugins/channelrx/demodais/aisdemodgui.cpp b/plugins/channelrx/demodais/aisdemodgui.cpp index 72946bac4..0d78263fa 100644 --- a/plugins/channelrx/demodais/aisdemodgui.cpp +++ b/plugins/channelrx/demodais/aisdemodgui.cpp @@ -253,7 +253,7 @@ void AISDemodGUI::channelMarkerChangedByCursor() void AISDemodGUI::channelMarkerHighlightedByCursor() { - getRollupContents()->setHighlighted(m_channelMarker.getHighlighted()); + setHighlighted(m_channelMarker.getHighlighted()); } void AISDemodGUI::on_deltaFrequency_changed(qint64 value) diff --git a/plugins/channelrx/demodam/amdemodgui.cpp b/plugins/channelrx/demodam/amdemodgui.cpp index 3718d1fcf..f50ee0de6 100644 --- a/plugins/channelrx/demodam/amdemodgui.cpp +++ b/plugins/channelrx/demodam/amdemodgui.cpp @@ -131,7 +131,7 @@ void AMDemodGUI::channelMarkerChangedByCursor() void AMDemodGUI::channelMarkerHighlightedByCursor() { - getRollupContents()->setHighlighted(m_channelMarker.getHighlighted()); + setHighlighted(m_channelMarker.getHighlighted()); } void AMDemodGUI::on_deltaFrequency_changed(qint64 value) diff --git a/plugins/channelrx/demodapt/aptdemodgui.cpp b/plugins/channelrx/demodapt/aptdemodgui.cpp index 789459c4e..c6d215d0c 100644 --- a/plugins/channelrx/demodapt/aptdemodgui.cpp +++ b/plugins/channelrx/demodapt/aptdemodgui.cpp @@ -307,7 +307,7 @@ void APTDemodGUI::channelMarkerChangedByCursor() void APTDemodGUI::channelMarkerHighlightedByCursor() { - getRollupContents()->setHighlighted(m_channelMarker.getHighlighted()); + setHighlighted(m_channelMarker.getHighlighted()); } void APTDemodGUI::on_deltaFrequency_changed(qint64 value) diff --git a/plugins/channelrx/demodatv/atvdemodgui.cpp b/plugins/channelrx/demodatv/atvdemodgui.cpp index 41b819517..31bcd52da 100644 --- a/plugins/channelrx/demodatv/atvdemodgui.cpp +++ b/plugins/channelrx/demodatv/atvdemodgui.cpp @@ -194,7 +194,7 @@ void ATVDemodGUI::channelMarkerChangedByCursor() void ATVDemodGUI::channelMarkerHighlightedByCursor() { - getRollupContents()->setHighlighted(m_channelMarker.getHighlighted()); + setHighlighted(m_channelMarker.getHighlighted()); } void ATVDemodGUI::handleSourceMessages() diff --git a/plugins/channelrx/demodbfm/bfmdemodgui.cpp b/plugins/channelrx/demodbfm/bfmdemodgui.cpp index c486ef29e..1eeddc372 100644 --- a/plugins/channelrx/demodbfm/bfmdemodgui.cpp +++ b/plugins/channelrx/demodbfm/bfmdemodgui.cpp @@ -149,7 +149,7 @@ void BFMDemodGUI::channelMarkerChangedByCursor() void BFMDemodGUI::channelMarkerHighlightedByCursor() { - getRollupContents()->setHighlighted(m_channelMarker.getHighlighted()); + setHighlighted(m_channelMarker.getHighlighted()); } void BFMDemodGUI::on_deltaFrequency_changed(qint64 value) diff --git a/plugins/channelrx/demodchirpchat/chirpchatdemodgui.cpp b/plugins/channelrx/demodchirpchat/chirpchatdemodgui.cpp index a2ca6ffd6..0521ef30e 100644 --- a/plugins/channelrx/demodchirpchat/chirpchatdemodgui.cpp +++ b/plugins/channelrx/demodchirpchat/chirpchatdemodgui.cpp @@ -162,7 +162,7 @@ void ChirpChatDemodGUI::on_deltaFrequency_changed(qint64 value) void ChirpChatDemodGUI::channelMarkerHighlightedByCursor() { - getRollupContents()->setHighlighted(m_channelMarker.getHighlighted()); + setHighlighted(m_channelMarker.getHighlighted()); } void ChirpChatDemodGUI::on_BW_valueChanged(int value) diff --git a/plugins/channelrx/demoddab/dabdemodgui.cpp b/plugins/channelrx/demoddab/dabdemodgui.cpp index b64e9e8a5..d52f9ebbc 100644 --- a/plugins/channelrx/demoddab/dabdemodgui.cpp +++ b/plugins/channelrx/demoddab/dabdemodgui.cpp @@ -318,7 +318,7 @@ void DABDemodGUI::channelMarkerChangedByCursor() void DABDemodGUI::channelMarkerHighlightedByCursor() { - getRollupContents()->setHighlighted(m_channelMarker.getHighlighted()); + setHighlighted(m_channelMarker.getHighlighted()); } void DABDemodGUI::on_deltaFrequency_changed(qint64 value) diff --git a/plugins/channelrx/demoddatv/datvdemodgui.cpp b/plugins/channelrx/demoddatv/datvdemodgui.cpp index 6e5b70674..921200494 100644 --- a/plugins/channelrx/demoddatv/datvdemodgui.cpp +++ b/plugins/channelrx/demoddatv/datvdemodgui.cpp @@ -148,7 +148,7 @@ void DATVDemodGUI::channelMarkerChangedByCursor() void DATVDemodGUI::channelMarkerHighlightedByCursor() { - getRollupContents()->setHighlighted(m_channelMarker.getHighlighted()); + setHighlighted(m_channelMarker.getHighlighted()); } diff --git a/plugins/channelrx/demoddsd/dsddemodgui.cpp b/plugins/channelrx/demoddsd/dsddemodgui.cpp index 5f5d30829..fc256d618 100644 --- a/plugins/channelrx/demoddsd/dsddemodgui.cpp +++ b/plugins/channelrx/demoddsd/dsddemodgui.cpp @@ -534,7 +534,7 @@ void DSDDemodGUI::channelMarkerChangedByCursor() void DSDDemodGUI::channelMarkerHighlightedByCursor() { - getRollupContents()->setHighlighted(m_channelMarker.getHighlighted()); + setHighlighted(m_channelMarker.getHighlighted()); } void DSDDemodGUI::audioSelect() diff --git a/plugins/channelrx/demodfreedv/freedvdemodgui.cpp b/plugins/channelrx/demodfreedv/freedvdemodgui.cpp index 2ed6714be..6f534d75b 100644 --- a/plugins/channelrx/demodfreedv/freedvdemodgui.cpp +++ b/plugins/channelrx/demodfreedv/freedvdemodgui.cpp @@ -133,7 +133,7 @@ void FreeDVDemodGUI::channelMarkerChangedByCursor() void FreeDVDemodGUI::channelMarkerHighlightedByCursor() { - getRollupContents()->setHighlighted(m_channelMarker.getHighlighted()); + setHighlighted(m_channelMarker.getHighlighted()); } void FreeDVDemodGUI::on_deltaFrequency_changed(qint64 value) diff --git a/plugins/channelrx/demodnfm/nfmdemodgui.cpp b/plugins/channelrx/demodnfm/nfmdemodgui.cpp index ae06b8e1b..56c190aea 100644 --- a/plugins/channelrx/demodnfm/nfmdemodgui.cpp +++ b/plugins/channelrx/demodnfm/nfmdemodgui.cpp @@ -125,7 +125,7 @@ void NFMDemodGUI::channelMarkerChangedByCursor() void NFMDemodGUI::channelMarkerHighlightedByCursor() { - getRollupContents()->setHighlighted(m_channelMarker.getHighlighted()); + setHighlighted(m_channelMarker.getHighlighted()); } void NFMDemodGUI::on_deltaFrequency_changed(qint64 value) diff --git a/plugins/channelrx/demodpacket/packetdemodgui.cpp b/plugins/channelrx/demodpacket/packetdemodgui.cpp index e132d0547..45fc8e6d5 100644 --- a/plugins/channelrx/demodpacket/packetdemodgui.cpp +++ b/plugins/channelrx/demodpacket/packetdemodgui.cpp @@ -257,7 +257,7 @@ void PacketDemodGUI::channelMarkerChangedByCursor() void PacketDemodGUI::channelMarkerHighlightedByCursor() { - getRollupContents()->setHighlighted(m_channelMarker.getHighlighted()); + setHighlighted(m_channelMarker.getHighlighted()); } void PacketDemodGUI::on_deltaFrequency_changed(qint64 value) diff --git a/plugins/channelrx/demodpager/pagerdemodgui.cpp b/plugins/channelrx/demodpager/pagerdemodgui.cpp index 8ae79a3a3..4e63805ac 100644 --- a/plugins/channelrx/demodpager/pagerdemodgui.cpp +++ b/plugins/channelrx/demodpager/pagerdemodgui.cpp @@ -313,7 +313,7 @@ void PagerDemodGUI::channelMarkerChangedByCursor() void PagerDemodGUI::channelMarkerHighlightedByCursor() { - getRollupContents()->setHighlighted(m_channelMarker.getHighlighted()); + setHighlighted(m_channelMarker.getHighlighted()); } void PagerDemodGUI::on_deltaFrequency_changed(qint64 value) diff --git a/plugins/channelrx/demodradiosonde/radiosondedemodgui.cpp b/plugins/channelrx/demodradiosonde/radiosondedemodgui.cpp index 1c34e1542..ec051fbcc 100644 --- a/plugins/channelrx/demodradiosonde/radiosondedemodgui.cpp +++ b/plugins/channelrx/demodradiosonde/radiosondedemodgui.cpp @@ -360,7 +360,7 @@ void RadiosondeDemodGUI::channelMarkerChangedByCursor() void RadiosondeDemodGUI::channelMarkerHighlightedByCursor() { - getRollupContents()->setHighlighted(m_channelMarker.getHighlighted()); + setHighlighted(m_channelMarker.getHighlighted()); } void RadiosondeDemodGUI::on_deltaFrequency_changed(qint64 value) diff --git a/plugins/channelrx/demodssb/ssbdemodgui.cpp b/plugins/channelrx/demodssb/ssbdemodgui.cpp index ad1e1c61b..9b12529f4 100644 --- a/plugins/channelrx/demodssb/ssbdemodgui.cpp +++ b/plugins/channelrx/demodssb/ssbdemodgui.cpp @@ -122,7 +122,7 @@ void SSBDemodGUI::channelMarkerChangedByCursor() void SSBDemodGUI::channelMarkerHighlightedByCursor() { - getRollupContents()->setHighlighted(m_channelMarker.getHighlighted()); + setHighlighted(m_channelMarker.getHighlighted()); } void SSBDemodGUI::on_audioBinaural_toggled(bool binaural) diff --git a/plugins/channelrx/demodvor/vordemodgui.cpp b/plugins/channelrx/demodvor/vordemodgui.cpp index 14410fb43..1a4820be2 100644 --- a/plugins/channelrx/demodvor/vordemodgui.cpp +++ b/plugins/channelrx/demodvor/vordemodgui.cpp @@ -889,7 +889,7 @@ void VORDemodGUI::channelMarkerChangedByCursor() void VORDemodGUI::channelMarkerHighlightedByCursor() { - getRollupContents()->setHighlighted(m_channelMarker.getHighlighted()); + setHighlighted(m_channelMarker.getHighlighted()); } void VORDemodGUI::on_thresh_valueChanged(int value) diff --git a/plugins/channelrx/demodvorsc/vordemodscgui.cpp b/plugins/channelrx/demodvorsc/vordemodscgui.cpp index 8cdd2be12..dd657e541 100644 --- a/plugins/channelrx/demodvorsc/vordemodscgui.cpp +++ b/plugins/channelrx/demodvorsc/vordemodscgui.cpp @@ -194,7 +194,7 @@ void VORDemodSCGUI::channelMarkerChangedByCursor() void VORDemodSCGUI::channelMarkerHighlightedByCursor() { - getRollupContents()->setHighlighted(m_channelMarker.getHighlighted()); + setHighlighted(m_channelMarker.getHighlighted()); } void VORDemodSCGUI::on_deltaFrequency_changed(qint64 value) diff --git a/plugins/channelrx/demodwfm/wfmdemodgui.cpp b/plugins/channelrx/demodwfm/wfmdemodgui.cpp index a191a7601..47237c6f7 100644 --- a/plugins/channelrx/demodwfm/wfmdemodgui.cpp +++ b/plugins/channelrx/demodwfm/wfmdemodgui.cpp @@ -113,7 +113,7 @@ void WFMDemodGUI::channelMarkerChangedByCursor() void WFMDemodGUI::channelMarkerHighlightedByCursor() { - getRollupContents()->setHighlighted(m_channelMarker.getHighlighted()); + setHighlighted(m_channelMarker.getHighlighted()); } void WFMDemodGUI::on_deltaFrequency_changed(qint64 value) diff --git a/plugins/channelrx/filesink/filesinkgui.cpp b/plugins/channelrx/filesink/filesinkgui.cpp index 98ddc362e..b4127e18f 100644 --- a/plugins/channelrx/filesink/filesinkgui.cpp +++ b/plugins/channelrx/filesink/filesinkgui.cpp @@ -333,7 +333,7 @@ void FileSinkGUI::channelMarkerChangedByCursor() void FileSinkGUI::channelMarkerHighlightedByCursor() { - getRollupContents()->setHighlighted(m_channelMarker.getHighlighted()); + setHighlighted(m_channelMarker.getHighlighted()); } void FileSinkGUI::handleSourceMessages() diff --git a/plugins/channelrx/freqtracker/freqtrackergui.cpp b/plugins/channelrx/freqtracker/freqtrackergui.cpp index 3e946492f..c1ebd541d 100644 --- a/plugins/channelrx/freqtracker/freqtrackergui.cpp +++ b/plugins/channelrx/freqtracker/freqtrackergui.cpp @@ -144,7 +144,7 @@ void FreqTrackerGUI::channelMarkerChangedByCursor() void FreqTrackerGUI::channelMarkerHighlightedByCursor() { - getRollupContents()->setHighlighted(m_channelMarker.getHighlighted()); + setHighlighted(m_channelMarker.getHighlighted()); } void FreqTrackerGUI::on_deltaFrequency_changed(qint64 value) diff --git a/plugins/channelrx/noisefigure/noisefiguregui.cpp b/plugins/channelrx/noisefigure/noisefiguregui.cpp index 8c0b1f427..703f69ebc 100644 --- a/plugins/channelrx/noisefigure/noisefiguregui.cpp +++ b/plugins/channelrx/noisefigure/noisefiguregui.cpp @@ -320,7 +320,7 @@ void NoiseFigureGUI::channelMarkerChangedByCursor() void NoiseFigureGUI::channelMarkerHighlightedByCursor() { - getRollupContents()->setHighlighted(m_channelMarker.getHighlighted()); + setHighlighted(m_channelMarker.getHighlighted()); } void NoiseFigureGUI::on_deltaFrequency_changed(qint64 value) diff --git a/plugins/channelrx/radioastronomy/radioastronomygui.cpp b/plugins/channelrx/radioastronomy/radioastronomygui.cpp index d37a99def..77070dd1a 100644 --- a/plugins/channelrx/radioastronomy/radioastronomygui.cpp +++ b/plugins/channelrx/radioastronomy/radioastronomygui.cpp @@ -1111,7 +1111,7 @@ void RadioAstronomyGUI::channelMarkerChangedByCursor() void RadioAstronomyGUI::channelMarkerHighlightedByCursor() { - getRollupContents()->setHighlighted(m_channelMarker.getHighlighted()); + setHighlighted(m_channelMarker.getHighlighted()); } // Calculate Tsys0 - i.e. receiver noise temperature when there's no source signal, just unwanted noise diff --git a/plugins/channelrx/radioclock/radioclockgui.cpp b/plugins/channelrx/radioclock/radioclockgui.cpp index 72976c9b0..6e98b8dc6 100644 --- a/plugins/channelrx/radioclock/radioclockgui.cpp +++ b/plugins/channelrx/radioclock/radioclockgui.cpp @@ -168,7 +168,7 @@ void RadioClockGUI::channelMarkerChangedByCursor() void RadioClockGUI::channelMarkerHighlightedByCursor() { - getRollupContents()->setHighlighted(m_channelMarker.getHighlighted()); + setHighlighted(m_channelMarker.getHighlighted()); } void RadioClockGUI::on_deltaFrequency_changed(qint64 value) diff --git a/plugins/channelrx/sigmffilesink/sigmffilesinkgui.cpp b/plugins/channelrx/sigmffilesink/sigmffilesinkgui.cpp index eca7730ba..c88796bf9 100644 --- a/plugins/channelrx/sigmffilesink/sigmffilesinkgui.cpp +++ b/plugins/channelrx/sigmffilesink/sigmffilesinkgui.cpp @@ -325,7 +325,7 @@ void SigMFFileSinkGUI::channelMarkerChangedByCursor() void SigMFFileSinkGUI::channelMarkerHighlightedByCursor() { - getRollupContents()->setHighlighted(m_channelMarker.getHighlighted()); + setHighlighted(m_channelMarker.getHighlighted()); } void SigMFFileSinkGUI::handleSourceMessages() diff --git a/plugins/channelrx/udpsink/udpsinkgui.cpp b/plugins/channelrx/udpsink/udpsinkgui.cpp index 44bf1b881..3043c2a1c 100644 --- a/plugins/channelrx/udpsink/udpsinkgui.cpp +++ b/plugins/channelrx/udpsink/udpsinkgui.cpp @@ -120,7 +120,7 @@ void UDPSinkGUI::channelMarkerChangedByCursor() void UDPSinkGUI::channelMarkerHighlightedByCursor() { - getRollupContents()->setHighlighted(m_channelMarker.getHighlighted()); + setHighlighted(m_channelMarker.getHighlighted()); } void UDPSinkGUI::tick() diff --git a/sdrgui/channel/channelgui.cpp b/sdrgui/channel/channelgui.cpp index 6f2f91cf0..590d9b65b 100644 --- a/sdrgui/channel/channelgui.cpp +++ b/sdrgui/channel/channelgui.cpp @@ -46,8 +46,8 @@ ChannelGUI::ChannelGUI(QWidget *parent) : setWindowFlags(windowFlags() | Qt::FramelessWindowHint); setObjectName("ChannelGUI"); setStyleSheet(QString(tr("#ChannelGUI { border: 1px solid %1; background-color: %2; }") - .arg(palette().highlight().color().darker(115).name())) - .arg(palette().dark().color().darker(115).name())); + .arg(palette().highlight().color().darker(115).name()) + .arg(palette().dark().color().darker(115).name()))); m_indexLabel = new QLabel(); m_indexLabel->setFixedSize(50, 16); @@ -395,6 +395,13 @@ bool ChannelGUI::isOnMovingPad() return m_indexLabel->underMouse() || m_titleLabel->underMouse() || m_statusFrequency->underMouse() || m_statusLabel->underMouse(); } +void ChannelGUI::setHighlighted(bool highlighted) +{ + setStyleSheet(QString(tr("#ChannelGUI { border: 1px solid %1; background-color: %2; }") + .arg(highlighted ? "#FFFFFF" : palette().highlight().color().darker(115).name()) + .arg(palette().dark().color().darker(115).name()))); +} + QString ChannelGUI::getDeviceTypeTag() { switch (m_deviceType) diff --git a/sdrgui/channel/channelgui.h b/sdrgui/channel/channelgui.h index 79199dd33..d05b71f36 100644 --- a/sdrgui/channel/channelgui.h +++ b/sdrgui/channel/channelgui.h @@ -96,6 +96,7 @@ protected: void resetContextMenuType() { m_contextMenuType = ContextMenuNone; } void updateIndexLabel(); int getAdditionalHeight() const { return 25 + 22; } + void setHighlighted(bool highlighted); DeviceType m_deviceType; int m_deviceSetIndex; diff --git a/sdrgui/gui/rollupcontents.cpp b/sdrgui/gui/rollupcontents.cpp index aa5c90b5e..148fc66ea 100644 --- a/sdrgui/gui/rollupcontents.cpp +++ b/sdrgui/gui/rollupcontents.cpp @@ -28,7 +28,6 @@ RollupContents::RollupContents(QWidget* parent) : QWidget(parent), - m_highlighted(false), m_streamIndicator("S"), // m_channelWidget(true), m_newHeight(0) @@ -83,15 +82,6 @@ void RollupContents::restoreState(const RollupState& state) } } -void RollupContents::setHighlighted(bool highlighted) -{ - if (m_highlighted != highlighted) - { - m_highlighted = highlighted; - update(); - } -} - bool RollupContents::hasExpandableWidgets() { for (int i = 0; i < children().count(); ++i) @@ -219,7 +209,7 @@ void RollupContents::paintEvent(QPaintEvent*) // p.drawRect(width() - 5, height() - 5, 5, 5); // Rahmen (frame) - p.setPen(m_highlighted ? Qt::white : frameColor); + // p.setPen(m_highlighted ? Qt::white : frameColor); p.setBrush(palette().window()); QRectF r(rect()); // r.adjust(0.5, 0.5, -0.5, -0.5); diff --git a/sdrgui/gui/rollupcontents.h b/sdrgui/gui/rollupcontents.h index 898505a59..db6d2365b 100644 --- a/sdrgui/gui/rollupcontents.h +++ b/sdrgui/gui/rollupcontents.h @@ -30,7 +30,6 @@ class SDRGUI_API RollupContents : public QWidget { public: RollupContents(QWidget* parent = nullptr); - void setHighlighted(bool highlighted); void saveState(RollupState& state) const; void restoreState(const RollupState& state); int arrangeRollups(); @@ -44,7 +43,6 @@ protected: VersionMarker = 0xff }; - bool m_highlighted; QString m_streamIndicator; QString m_helpURL; From 03874673806c1b04405ded430f35778101224820 Mon Sep 17 00:00:00 2001 From: f4exb Date: Tue, 26 Apr 2022 08:32:33 +0200 Subject: [PATCH 067/115] Massive UI revamping (v7): Channel Analyzer GUI: fixed log2 decimation handling --- plugins/channelrx/chanalyzer/chanalyzergui.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/channelrx/chanalyzer/chanalyzergui.cpp b/plugins/channelrx/chanalyzer/chanalyzergui.cpp index 54fdddb94..e32fc95e9 100644 --- a/plugins/channelrx/chanalyzer/chanalyzergui.cpp +++ b/plugins/channelrx/chanalyzer/chanalyzergui.cpp @@ -730,6 +730,7 @@ void ChannelAnalyzerGUI::makeUIConnections() QObject::connect(ui->pllBandwidth, &QDial::valueChanged, this, &ChannelAnalyzerGUI::on_pllBandwidth_valueChanged); QObject::connect(ui->pllDampingFactor, &QDial::valueChanged, this, &ChannelAnalyzerGUI::on_pllDampingFactor_valueChanged); QObject::connect(ui->pllLoopGain, &QDial::valueChanged, this, &ChannelAnalyzerGUI::on_pllLoopGain_valueChanged); + QObject::connect(ui->log2Decim, QOverload::of(&QComboBox::currentIndexChanged), this, &ChannelAnalyzerGUI::on_log2Decim_currentIndexChanged); QObject::connect(ui->useRationalDownsampler, &ButtonSwitch::toggled, this, &ChannelAnalyzerGUI::on_useRationalDownsampler_toggled); QObject::connect(ui->signalSelect, QOverload::of(&QComboBox::currentIndexChanged), this, &ChannelAnalyzerGUI::on_signalSelect_currentIndexChanged); QObject::connect(ui->rrcFilter, &ButtonSwitch::toggled, this, &ChannelAnalyzerGUI::on_rrcFilter_toggled); From 243ad04a3775c3580a980ad8c6e2e29f84f953c4 Mon Sep 17 00:00:00 2001 From: Jon Beniston Date: Tue, 26 Apr 2022 18:18:48 +0100 Subject: [PATCH 068/115] Fix height of top and bottom bars in windows --- sdrgui/channel/channelgui.h | 2 +- sdrgui/device/devicegui.h | 2 +- sdrgui/feature/featuregui.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/sdrgui/channel/channelgui.h b/sdrgui/channel/channelgui.h index d05b71f36..22486f400 100644 --- a/sdrgui/channel/channelgui.h +++ b/sdrgui/channel/channelgui.h @@ -95,7 +95,7 @@ protected: void mouseMoveEvent(QMouseEvent* event) override; void resetContextMenuType() { m_contextMenuType = ContextMenuNone; } void updateIndexLabel(); - int getAdditionalHeight() const { return 25 + 22; } + int getAdditionalHeight() const { return 22 + 22; } // height of top and bottom bars void setHighlighted(bool highlighted); DeviceType m_deviceType; diff --git a/sdrgui/device/devicegui.h b/sdrgui/device/devicegui.h index 1417ca8a8..78993d887 100644 --- a/sdrgui/device/devicegui.h +++ b/sdrgui/device/devicegui.h @@ -83,7 +83,7 @@ protected: void mouseReleaseEvent(QMouseEvent* event) override; void mouseMoveEvent(QMouseEvent* event) override; void resetContextMenuType() { m_contextMenuType = ContextMenuNone; } - int getAdditionalHeight() const { return 25 + 22; } + int getAdditionalHeight() const { return 26 + 22; } // height of top and bottom bars DeviceType m_deviceType; int m_deviceSetIndex; diff --git a/sdrgui/feature/featuregui.h b/sdrgui/feature/featuregui.h index fbccad7b6..2d764f2a2 100644 --- a/sdrgui/feature/featuregui.h +++ b/sdrgui/feature/featuregui.h @@ -72,7 +72,7 @@ protected: void mouseReleaseEvent(QMouseEvent* event) override; void mouseMoveEvent(QMouseEvent* event) override; void resetContextMenuType() { m_contextMenuType = ContextMenuNone; } - int getAdditionalHeight() const { return 25 + 22; } + int getAdditionalHeight() const { return 22 + 22; } // height of top and bottom bars int m_featureIndex; QString m_helpURL; From bc38ca84870b58bd4c153a0d8b81e5fc21db6397 Mon Sep 17 00:00:00 2001 From: Jon Beniston Date: Tue, 26 Apr 2022 18:19:31 +0100 Subject: [PATCH 069/115] Add stacking window layout --- sdrgui/gui/workspace.cpp | 374 ++++++++++++++++++++++++++++++++++++++- sdrgui/gui/workspace.h | 18 ++ 2 files changed, 391 insertions(+), 1 deletion(-) diff --git a/sdrgui/gui/workspace.cpp b/sdrgui/gui/workspace.cpp index a9345417e..5c28578ea 100644 --- a/sdrgui/gui/workspace.cpp +++ b/sdrgui/gui/workspace.cpp @@ -16,6 +16,8 @@ // along with this program. If not, see . // /////////////////////////////////////////////////////////////////////////////////// +#include + #include #include #include @@ -23,14 +25,23 @@ #include #include #include +#include +#include #include "gui/samplingdevicedialog.h" +#include "gui/rollupcontents.h" +#include "channel/channelgui.h" +#include "feature/featuregui.h" +#include "device/devicegui.h" +#include "mainspectrum/mainspectrumgui.h" #include "workspace.h" Workspace::Workspace(int index, QWidget *parent, Qt::WindowFlags flags) : QDockWidget(parent, flags), m_index(index), - m_featureAddDialog(this) + m_featureAddDialog(this), + m_stacking(false), + m_userChannelMinWidth(0) { m_mdi = new QMdiArea(this); m_mdi->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); @@ -100,6 +111,19 @@ Workspace::Workspace(int index, QWidget *parent, Qt::WindowFlags flags) : m_tileSubWindows->setToolTip("Tile sub windows"); m_tileSubWindows->setFixedSize(20, 20); + m_stackSubWindows = new QPushButton("S"); + //QIcon stackSubWindowsIcon(":/stack.png"); // FIXME + //m_stackSubWindows->setIcon(stackSubWindowsIcon); + m_stackSubWindows->setToolTip("Stack sub windows"); + m_stackSubWindows->setFixedSize(20, 20); + + m_autoStackSubWindows = new QPushButton("AS"); + m_autoStackSubWindows->setCheckable(true); + //QIcon autoStackSubWindowsIcon(":/autostack.png"); // FIXME + //m_autoStackSubWindows->setIcon(autoStackSubWindowsIcon); + m_autoStackSubWindows->setToolTip("Automatically stack sub windows"); + m_autoStackSubWindows->setFixedSize(20, 20); + m_normalButton = new QPushButton(); QIcon normalIcon(":/dock.png"); m_normalButton->setIcon(normalIcon); @@ -122,6 +146,8 @@ Workspace::Workspace(int index, QWidget *parent, Qt::WindowFlags flags) : m_titleBarLayout->addWidget(m_vline2); m_titleBarLayout->addWidget(m_cascadeSubWindows); m_titleBarLayout->addWidget(m_tileSubWindows); + m_titleBarLayout->addWidget(m_stackSubWindows); + m_titleBarLayout->addWidget(m_autoStackSubWindows); m_titleBarLayout->addStretch(1); m_titleBarLayout->addWidget(m_normalButton); m_titleBarLayout->addWidget(m_closeButton); @@ -176,6 +202,20 @@ Workspace::Workspace(int index, QWidget *parent, Qt::WindowFlags flags) : &Workspace::tileSubWindows ); + QObject::connect( + m_stackSubWindows, + &QPushButton::clicked, + this, + &Workspace::stackSubWindows + ); + + QObject::connect( + m_autoStackSubWindows, + &QPushButton::clicked, + this, + &Workspace::autoStackSubWindows + ); + QObject::connect( m_normalButton, &QPushButton::clicked, @@ -191,6 +231,7 @@ Workspace::Workspace(int index, QWidget *parent, Qt::WindowFlags flags) : this, &Workspace::addFeatureEmitted ); + } Workspace::~Workspace() @@ -198,6 +239,8 @@ Workspace::~Workspace() qDebug("Workspace::~Workspace"); delete m_closeButton; delete m_normalButton; + delete m_autoStackSubWindows; + delete m_stackSubWindows; delete m_tileSubWindows; delete m_cascadeSubWindows; delete m_vline2; @@ -288,15 +331,344 @@ void Workspace::tileSubWindows() m_mdi->tileSubWindows(); } +void Workspace::orderByIndex(QList &list) +{ + std::sort(list.begin(), list.end(), + [](const ChannelGUI *a, const ChannelGUI *b) -> bool + { + if (a->getDeviceSetIndex() == b->getDeviceSetIndex()) { + return a->getIndex() < b->getIndex(); + } else { + return a->getDeviceSetIndex() < b->getDeviceSetIndex(); + } + }); +} + +void Workspace::orderByIndex(QList &list) +{ + std::sort(list.begin(), list.end(), + [](const FeatureGUI *a, const FeatureGUI *b) -> bool + { + return a->getIndex() < b->getIndex(); + }); +} + +void Workspace::orderByIndex(QList &list) +{ + std::sort(list.begin(), list.end(), + [](const DeviceGUI *a, const DeviceGUI *b) -> bool + { + return a->getIndex() < b->getIndex(); + }); +} + +void Workspace::orderByIndex(QList &list) +{ + std::sort(list.begin(), list.end(), + [](const MainSpectrumGUI *a, const MainSpectrumGUI *b) -> bool + { + return a->getIndex() < b->getIndex(); + }); +} + +// Try to arrange windows somewhat like in earlier versions of SDRangel +// Devices and fixed size features stacked on left +// Spectrum and expandable features stacked in centre +// Channels stacked on right +void Workspace::stackSubWindows() +{ + // Set a flag so event handler knows if it's this code or the user that + // resizes a window + m_stacking = true; + + // Categorise windows according to type + QList windows = m_mdi->subWindowList(QMdiArea::CreationOrder); + QList devices; + QList spectrums; + QList channels; + QList fixedFeatures; + QList features; + + for (auto window : windows) + { + if (window->isVisible()) + { + if (window->inherits("DeviceGUI")) { + devices.append(qobject_cast(window)); + } else if (window->inherits("MainSpectrumGUI")) { + spectrums.append(qobject_cast(window)); + } else if (window->inherits("ChannelGUI")) { + channels.append(qobject_cast(window)); + } else if (window->inherits("FeatureGUI")) { + if (window->sizePolicy().verticalPolicy() == QSizePolicy::Fixed) { // Test vertical, as horizontal can be adjusted a little bit + fixedFeatures.append(qobject_cast(window)); + } else { + features.append(qobject_cast(window)); + } + } + } + } + + // Order windows by device/feature/channel index + orderByIndex(devices); + orderByIndex(spectrums); + orderByIndex(channels); + orderByIndex(fixedFeatures); + orderByIndex(features); + + // Spacing between windows + const int spacing = 2; + + // Calculate width and height needed for devices + int deviceMinWidth = 0; + int deviceTotalMinHeight = 0; + for (auto window : devices) + { + int winMinWidth = std::max(window->minimumSizeHint().width(), window->minimumWidth()); + deviceMinWidth = std::max(deviceMinWidth, winMinWidth); + deviceTotalMinHeight += window->minimumSizeHint().height() + spacing; + } + + // Calculate width & height needed for spectrums + int spectrumMinWidth = 0; + int spectrumTotalMinHeight = 0; + int expandingSpectrums = 0; + for (auto window : spectrums) + { + int winMinWidth = std::max(window->minimumSizeHint().width(), window->minimumWidth()); + spectrumMinWidth = std::max(spectrumMinWidth, winMinWidth); + spectrumTotalMinHeight += window->minimumSizeHint().height() + spacing; + expandingSpectrums++; + } + + // Calculate width & height needed for channels + int channelMinWidth = m_userChannelMinWidth; + int channelTotalMinHeight = 0; + int expandingChannels = 0; + for (auto window : channels) + { + int winMinWidth = std::max(window->minimumSizeHint().width(), window->minimumWidth()); + channelMinWidth = std::max(channelMinWidth, winMinWidth); + channelTotalMinHeight += window->minimumSizeHint().height() + spacing; + if (window->sizePolicy().verticalPolicy() == QSizePolicy::Expanding) { + expandingChannels++; + } + } + + // Calculate width & height needed for features + // These are spilt in to two groups - fixed size and expandable + int fixedFeaturesWidth = 0; + int fixedFeaturesTotalMinHeight = 0; + int featuresMinWidth = 0; + int featuresTotalMinHeight = 0; + int expandingFeatures = 0; + for (auto window : fixedFeatures) + { + int winMinWidth = std::max(window->minimumSizeHint().width(), window->minimumWidth()); + fixedFeaturesWidth = std::max(fixedFeaturesWidth, winMinWidth); + fixedFeaturesTotalMinHeight += window->minimumSizeHint().height() + spacing; + } + for (auto window : features) + { + int winMinWidth = std::max(window->minimumSizeHint().width(), window->minimumWidth()); + featuresMinWidth = std::max(featuresMinWidth, winMinWidth); + featuresTotalMinHeight += window->minimumSizeHint().height() + spacing; + expandingFeatures++; + } + + // Calculate width for left hand column + int devicesFeaturesWidth = std::max(deviceMinWidth, fixedFeaturesWidth); + // Calculate min width for centre column + int spectrumFeaturesMinWidth = std::max(spectrumMinWidth, featuresMinWidth); + + // Calculate spacing between columns + int spacing1 = devicesFeaturesWidth > 0 ? spacing : 0; + int spacing2 = spectrumFeaturesMinWidth > 0 ? spacing : 0; + + // Will we need scroll bars? + QSize mdiSize = m_mdi->size(); + int minWidth = devicesFeaturesWidth + spacing1 + spectrumFeaturesMinWidth + spacing2 + channelMinWidth; + int minHeight = std::max(std::max(deviceTotalMinHeight + fixedFeaturesTotalMinHeight, channelTotalMinHeight), channelTotalMinHeight + featuresTotalMinHeight); + bool requiresHScrollBar = minWidth > mdiSize.width(); + bool requiresVScrollBar = minHeight > mdiSize.height(); + + // Reduce available size if scroll bars needed + int sbWidth = qApp->style()->pixelMetric(QStyle::PM_ScrollBarExtent); + if (requiresVScrollBar) { + mdiSize.setWidth(mdiSize.width() - sbWidth); + } + if (requiresHScrollBar) { + mdiSize.setHeight(mdiSize.height() - sbWidth); + } + + // Now position the windows + int x = 0; + int y = 0; + + // Put devices down left hand side + for (auto window : devices) + { + window->move(x, y); + y += window->size().height() + spacing; + } + + // Put fixed height features underneath devices + // Resize them to be same width + for (auto window : fixedFeatures) + { + window->move(x, y); + window->resize(devicesFeaturesWidth, window->size().height()); + y += window->size().height() + spacing; + } + + // Calculate width needed for spectrum and features in the centre - use all available space + int spectrumFeaturesWidth = std::max(mdiSize.width() - channelMinWidth - devicesFeaturesWidth - spacing1 - spacing2, spectrumFeaturesMinWidth); + + // Put channels on right hand side + // Try to resize them horizontally so they are the same width + // Share any available vertical space between expanding channels + + x = devicesFeaturesWidth + spacing1 + spectrumFeaturesWidth + spacing2; + y = 0; + int extraSpacePerWindow; + int extraSpaceFirstWindow; + if ((channelTotalMinHeight < mdiSize.height()) && (expandingChannels > 0)) + { + extraSpacePerWindow = (mdiSize.height() - channelTotalMinHeight) / expandingChannels; + extraSpaceFirstWindow = (mdiSize.height() - channelTotalMinHeight) % expandingChannels; + } + else + { + extraSpacePerWindow = 0; + extraSpaceFirstWindow = 0; + } + + for (auto window : channels) + { + window->move(x, y); + int channelHeight = window->minimumSizeHint().height(); + if (window->sizePolicy().verticalPolicy() == QSizePolicy::Expanding) + { + channelHeight += extraSpacePerWindow + extraSpaceFirstWindow; + extraSpaceFirstWindow = 0; + } + window->resize(channelMinWidth, channelHeight); + y += window->size().height() + spacing; + } + + // Split remaining space in the middle between spectrums and expandable features, with spectrums stacked on top + x = devicesFeaturesWidth + spacing1; + y = 0; + if ((spectrumTotalMinHeight + featuresTotalMinHeight < mdiSize.height()) && (expandingSpectrums + expandingFeatures > 0)) + { + int h = mdiSize.height() - spectrumTotalMinHeight - featuresTotalMinHeight; + int f = expandingSpectrums + expandingFeatures; + extraSpacePerWindow = h / f; + extraSpaceFirstWindow = h % f; + } + else + { + extraSpacePerWindow = 0; + extraSpaceFirstWindow = 0; + } + + for (auto window : spectrums) + { + window->move(x, y); + int w = spectrumFeaturesWidth; + int h = window->minimumSizeHint().height() + extraSpacePerWindow + extraSpaceFirstWindow; + window->resize(w, h); + extraSpaceFirstWindow = 0; + y += window->size().height() + spacing; + } + for (auto window : features) + { + window->move(x, y); + int w = spectrumFeaturesWidth; + int h = window->minimumSizeHint().height() + extraSpacePerWindow + extraSpaceFirstWindow; + window->resize(w, h); + extraSpaceFirstWindow = 0; + y += window->size().height() + spacing; + } + + m_stacking = false; +} + +void Workspace::autoStackSubWindows() +{ + // FIXME: Need to save whether this is checked as a preference + if (m_autoStackSubWindows->isChecked()) { + stackSubWindows(); + } +} + +void Workspace::resizeEvent(QResizeEvent *event) +{ + QDockWidget::resizeEvent(event); + autoStackSubWindows(); +} + void Workspace::addToMdiArea(QMdiSubWindow *sub) { + // Add event handler to auto-stack when sub window shown or hidden + sub->installEventFilter(this); + // Can't use Close event, as it's before window is closed, so + // catch sub-window destroyed signal instead + connect(sub, &QObject::destroyed, this, &Workspace::autoStackSubWindows); m_mdi->addSubWindow(sub); sub->show(); + // Auto-stack when sub-window's widgets are rolled up + ChannelGUI *channel = qobject_cast(sub); + if (channel) { + connect(channel->getRollupContents(), &RollupContents::widgetRolled, this, &Workspace::autoStackSubWindows); + } + FeatureGUI *feature = qobject_cast(sub); + if (feature) { + connect(feature->getRollupContents(), &RollupContents::widgetRolled, this, &Workspace::autoStackSubWindows); + } } void Workspace::removeFromMdiArea(QMdiSubWindow *sub) { m_mdi->removeSubWindow(sub); + sub->removeEventFilter(this); + disconnect(sub, &QObject::destroyed, this, &Workspace::autoStackSubWindows); + ChannelGUI *channel = qobject_cast(sub); + if (channel) { + disconnect(channel->getRollupContents(), &RollupContents::widgetRolled, this, &Workspace::autoStackSubWindows); + } + FeatureGUI *feature = qobject_cast(sub); + if (feature) { + disconnect(feature->getRollupContents(), &RollupContents::widgetRolled, this, &Workspace::autoStackSubWindows); + } +} + +bool Workspace::eventFilter(QObject *obj, QEvent *event) +{ + if (event->type() == QEvent::Show) + { + autoStackSubWindows(); + } + else if (event->type() == QEvent::Hide) + { + autoStackSubWindows(); + } + else if (event->type() == QEvent::Resize) + { + if (!m_stacking && m_autoStackSubWindows->isChecked()) + { + ChannelGUI *channel = qobject_cast(obj); + if (channel) + { + // Allow width of channels column to be set by user when they + // resize a channel window + QResizeEvent *resizeEvent = static_cast(event); + m_userChannelMinWidth = resizeEvent->size().width(); + stackSubWindows(); + } + } + } + return QDockWidget::eventFilter(obj, event); } int Workspace::getNumberOfSubWindows() const diff --git a/sdrgui/gui/workspace.h b/sdrgui/gui/workspace.h index fff92eaa7..339c83252 100644 --- a/sdrgui/gui/workspace.h +++ b/sdrgui/gui/workspace.h @@ -31,6 +31,10 @@ class QStringList; class QMdiArea; class QMdiSubWindow; class QFrame; +class ChannelGUI; +class FeatureGUI; +class DeviceGUI; +class MainSpectrumGUI; class SDRGUI_API Workspace : public QDockWidget { @@ -49,6 +53,10 @@ public: QByteArray saveMdiGeometry(); void restoreMdiGeometry(const QByteArray& blob); QList getSubWindowList() const; + void orderByIndex(QList &list); + void orderByIndex(QList &list); + void orderByIndex(QList &list); + void orderByIndex(QList &list); private: int m_index; @@ -61,6 +69,8 @@ private: QFrame *m_vline2; QPushButton *m_cascadeSubWindows; QPushButton *m_tileSubWindows; + QPushButton *m_stackSubWindows; + QPushButton *m_autoStackSubWindows; QWidget *m_titleBar; QHBoxLayout *m_titleBarLayout; QLabel *m_titleLabel; @@ -68,6 +78,12 @@ private: QPushButton *m_closeButton; FeatureAddDialog m_featureAddDialog; QMdiArea *m_mdi; + bool m_stacking; // Set when stackSubWindows() is running + int m_userChannelMinWidth; // Minimum width of channels column for stackSubWindows(), set by user resizing a channel window + +protected: + void resizeEvent(QResizeEvent *event) override; + bool eventFilter(QObject *obj, QEvent *event) override; private slots: void addRxDeviceClicked(); @@ -77,6 +93,8 @@ private slots: void featurePresetsDialog(); void cascadeSubWindows(); void tileSubWindows(); + void stackSubWindows(); + void autoStackSubWindows(); void addFeatureEmitted(int featureIndex); void toggleFloating(); From 00b72891dd5c506f1a0e26686633976c2586b6f0 Mon Sep 17 00:00:00 2001 From: Jon Beniston Date: Tue, 26 Apr 2022 18:20:23 +0100 Subject: [PATCH 070/115] Adjust some plugin window constraints so they work better with stack layout --- plugins/channelrx/demodais/aisdemodgui.ui | 2 +- .../feature/antennatools/antennatoolsgui.ui | 14 ++--- plugins/feature/aprs/aprsgui.cpp | 4 -- plugins/feature/aprs/aprsgui.ui | 62 +++++++++++++++---- plugins/feature/simpleptt/simplepttgui.ui | 2 +- .../samplesource/sdrplayv3/sdrplayv3gui.ui | 6 +- .../samplesource/testsource/testsourcegui.ui | 6 +- 7 files changed, 64 insertions(+), 32 deletions(-) diff --git a/plugins/channelrx/demodais/aisdemodgui.ui b/plugins/channelrx/demodais/aisdemodgui.ui index 85ea93b7b..dce05b852 100644 --- a/plugins/channelrx/demodais/aisdemodgui.ui +++ b/plugins/channelrx/demodais/aisdemodgui.ui @@ -487,7 +487,7 @@ - 60 + 66 0 diff --git a/plugins/feature/antennatools/antennatoolsgui.ui b/plugins/feature/antennatools/antennatoolsgui.ui index 69f88452a..c7f0cc066 100644 --- a/plugins/feature/antennatools/antennatoolsgui.ui +++ b/plugins/feature/antennatools/antennatoolsgui.ui @@ -6,25 +6,25 @@ 0 0 - 374 + 360 584 - + 0 0 - 374 + 360 584 - 374 + 560 584 @@ -59,7 +59,7 @@ Calculators - 1 + 0 @@ -70,7 +70,7 @@ - 94 + 90 0 @@ -266,7 +266,7 @@ - 94 + 90 0 diff --git a/plugins/feature/aprs/aprsgui.cpp b/plugins/feature/aprs/aprsgui.cpp index 416de7d4b..f251c9a44 100644 --- a/plugins/feature/aprs/aprsgui.cpp +++ b/plugins/feature/aprs/aprsgui.cpp @@ -668,10 +668,6 @@ void APRSGUI::resizeEvent(QResizeEvent* size) plotWeather(); plotTelemetry(); plotMotion(); - int maxWidth = getRollupContents()->maximumWidth(); - int minHeight = getRollupContents()->minimumHeight() + getAdditionalHeight(); - resize(width() < maxWidth ? width() : maxWidth, minHeight); - size->accept(); } void APRSGUI::onMenuDialogCalled(const QPoint &p) diff --git a/plugins/feature/aprs/aprsgui.ui b/plugins/feature/aprs/aprsgui.ui index 1a6fa0712..92014f20f 100644 --- a/plugins/feature/aprs/aprsgui.ui +++ b/plugins/feature/aprs/aprsgui.ui @@ -11,7 +11,7 @@ - + 0 0 @@ -22,12 +22,6 @@ 0 - - - 700 - 16777215 - - Liberation Sans @@ -154,7 +148,7 @@ - + 0 0 @@ -187,6 +181,12 @@ 0 + + + 0 + 0 + + Stations and Objects @@ -568,6 +568,12 @@ + + + 0 + 0 + + Weather @@ -840,6 +846,12 @@ + + + 0 + 0 + + Motion @@ -1007,6 +1019,12 @@ + + + 0 + 0 + + Telemetry @@ -1271,6 +1289,12 @@ + + + 0 + 0 + + Status @@ -1323,6 +1347,12 @@ + + + 0 + 0 + + Packets @@ -1374,6 +1404,12 @@ + + + 0 + 0 + + Messages @@ -1453,17 +1489,17 @@ + + ButtonSwitch + QToolButton +
    gui/buttonswitch.h
    +
    RollupContents QWidget
    gui/rollupcontents.h
    1
    - - ButtonSwitch - QToolButton -
    gui/buttonswitch.h
    -
    QChartView QGraphicsView diff --git a/plugins/feature/simpleptt/simplepttgui.ui b/plugins/feature/simpleptt/simplepttgui.ui index e3d49567e..0f1e865f7 100644 --- a/plugins/feature/simpleptt/simplepttgui.ui +++ b/plugins/feature/simpleptt/simplepttgui.ui @@ -24,7 +24,7 @@ - 360 + 560 155 diff --git a/plugins/samplesource/sdrplayv3/sdrplayv3gui.ui b/plugins/samplesource/sdrplayv3/sdrplayv3gui.ui index 3f9470b80..aef48776b 100644 --- a/plugins/samplesource/sdrplayv3/sdrplayv3gui.ui +++ b/plugins/samplesource/sdrplayv3/sdrplayv3gui.ui @@ -6,7 +6,7 @@ 0 0 - 410 + 360 234 @@ -18,13 +18,13 @@ - 410 + 360 234 - 410 + 360 234 diff --git a/plugins/samplesource/testsource/testsourcegui.ui b/plugins/samplesource/testsource/testsourcegui.ui index bd492ff6a..6a25d1d73 100644 --- a/plugins/samplesource/testsource/testsourcegui.ui +++ b/plugins/samplesource/testsource/testsourcegui.ui @@ -6,7 +6,7 @@ 0 0 - 381 + 360 331 @@ -18,13 +18,13 @@ - 381 + 360 331 - 381 + 360 331 From 9be0776d328515413288385d1ba017e730315ba4 Mon Sep 17 00:00:00 2001 From: f4exb Date: Tue, 26 Apr 2022 19:44:14 +0200 Subject: [PATCH 071/115] Massive UI revamping (v7): unify top bar size to 22px and correct the getAdditionaHeight methods. Part of #1209 --- sdrgui/channel/channelgui.h | 2 +- sdrgui/device/devicegui.cpp | 1 + sdrgui/device/devicegui.h | 2 +- sdrgui/feature/featuregui.h | 2 +- sdrgui/mainspectrum/mainspectrumgui.cpp | 2 +- 5 files changed, 5 insertions(+), 4 deletions(-) diff --git a/sdrgui/channel/channelgui.h b/sdrgui/channel/channelgui.h index d05b71f36..b1f3d7bdc 100644 --- a/sdrgui/channel/channelgui.h +++ b/sdrgui/channel/channelgui.h @@ -95,7 +95,7 @@ protected: void mouseMoveEvent(QMouseEvent* event) override; void resetContextMenuType() { m_contextMenuType = ContextMenuNone; } void updateIndexLabel(); - int getAdditionalHeight() const { return 25 + 22; } + int getAdditionalHeight() const { return 22 + 22; } void setHighlighted(bool highlighted); DeviceType m_deviceType; diff --git a/sdrgui/device/devicegui.cpp b/sdrgui/device/devicegui.cpp index eefa2355b..19f4a8e15 100644 --- a/sdrgui/device/devicegui.cpp +++ b/sdrgui/device/devicegui.cpp @@ -54,6 +54,7 @@ DeviceGUI::DeviceGUI(QWidget *parent) : m_indexLabel->setToolTip("Device type and set index"); m_settingsButton = new QPushButton(); + m_settingsButton->setFixedSize(20, 20); QIcon settingsIcon(":/gear.png"); m_settingsButton->setIcon(settingsIcon); m_settingsButton->setToolTip("Common settings"); diff --git a/sdrgui/device/devicegui.h b/sdrgui/device/devicegui.h index 1417ca8a8..a615e2643 100644 --- a/sdrgui/device/devicegui.h +++ b/sdrgui/device/devicegui.h @@ -83,7 +83,7 @@ protected: void mouseReleaseEvent(QMouseEvent* event) override; void mouseMoveEvent(QMouseEvent* event) override; void resetContextMenuType() { m_contextMenuType = ContextMenuNone; } - int getAdditionalHeight() const { return 25 + 22; } + int getAdditionalHeight() const { return 22 + 22; } DeviceType m_deviceType; int m_deviceSetIndex; diff --git a/sdrgui/feature/featuregui.h b/sdrgui/feature/featuregui.h index fbccad7b6..7e80d30ef 100644 --- a/sdrgui/feature/featuregui.h +++ b/sdrgui/feature/featuregui.h @@ -72,7 +72,7 @@ protected: void mouseReleaseEvent(QMouseEvent* event) override; void mouseMoveEvent(QMouseEvent* event) override; void resetContextMenuType() { m_contextMenuType = ContextMenuNone; } - int getAdditionalHeight() const { return 25 + 22; } + int getAdditionalHeight() const { return 22 + 22; } int m_featureIndex; QString m_helpURL; diff --git a/sdrgui/mainspectrum/mainspectrumgui.cpp b/sdrgui/mainspectrum/mainspectrumgui.cpp index dfc44d99e..55de3a1f0 100644 --- a/sdrgui/mainspectrum/mainspectrumgui.cpp +++ b/sdrgui/mainspectrum/mainspectrumgui.cpp @@ -97,7 +97,7 @@ MainSpectrumGUI::MainSpectrumGUI(GLSpectrum *spectrum, GLSpectrumGUI *spectrumGU // m_statusLabel->setToolTip("Spectrum status"); m_layouts = new QVBoxLayout(); - m_layouts->setContentsMargins(m_resizer.m_gripSize, 4, m_resizer.m_gripSize, 4); + m_layouts->setContentsMargins(m_resizer.m_gripSize, m_resizer.m_gripSize, m_resizer.m_gripSize, m_resizer.m_gripSize); m_layouts->setSpacing(0); m_topLayout = new QHBoxLayout(); From 69a66adf88e682ff0802807473a40a978f3cb3b2 Mon Sep 17 00:00:00 2001 From: f4exb Date: Tue, 26 Apr 2022 19:44:18 +0200 Subject: [PATCH 072/115] Massive UI revamping (v7): make correction to sub windows sizes and placement at the top when loading a configuration --- sdrbase/settings/configuration.cpp | 5 ----- sdrbase/settings/configuration.h | 2 +- sdrgui/gui/workspace.cpp | 24 ++++++++++++++++++++++++ sdrgui/gui/workspace.h | 1 + sdrgui/mainwindow.cpp | 8 +++++--- 5 files changed, 31 insertions(+), 9 deletions(-) diff --git a/sdrbase/settings/configuration.cpp b/sdrbase/settings/configuration.cpp index d2ef6ebcd..4d194c07f 100644 --- a/sdrbase/settings/configuration.cpp +++ b/sdrbase/settings/configuration.cpp @@ -107,11 +107,6 @@ bool Configuration::deserialize(const QByteArray& data) } } -int Configuration::getNumberOfWorkspaces() const -{ - return m_workspaceGeometries.size(); -} - void Configuration::clearData() { m_deviceSetPresets.clear(); diff --git a/sdrbase/settings/configuration.h b/sdrbase/settings/configuration.h index f99e6ed44..296db22c3 100644 --- a/sdrbase/settings/configuration.h +++ b/sdrbase/settings/configuration.h @@ -47,7 +47,7 @@ public: void setDescription(const QString& description) { m_description = description; } const QString& getDescription() const { return m_description; } - int getNumberOfWorkspaces() const; + int getNumberOfWorkspaceGeometries() const { return m_workspaceGeometries.size(); } QList& getWorkspaceGeometries() { return m_workspaceGeometries; } const QList& getWorkspaceGeometries() const { return m_workspaceGeometries; } FeatureSetPreset& getFeatureSetPreset() { return m_featureSetPreset; } diff --git a/sdrgui/gui/workspace.cpp b/sdrgui/gui/workspace.cpp index a9345417e..94f7a1474 100644 --- a/sdrgui/gui/workspace.cpp +++ b/sdrgui/gui/workspace.cpp @@ -25,6 +25,10 @@ #include #include "gui/samplingdevicedialog.h" +#include "device/devicegui.h" +#include "channel/channelgui.h" +#include "mainspectrum/mainspectrumgui.h" +#include "feature/featuregui.h" #include "workspace.h" Workspace::Workspace(int index, QWidget *parent, Qt::WindowFlags flags) : @@ -313,3 +317,23 @@ void Workspace::restoreMdiGeometry(const QByteArray& blob) { m_mdi->restoreGeometry(qUncompress(blob)); } + +void Workspace::adjustSubWindowsAfterRestore() +{ + QList subWindowList = m_mdi->subWindowList(); + + for (auto& subWindow : subWindowList) + { + if ((subWindow->y() >= 20) && (subWindow->y() < 40)) { + subWindow->move(subWindow->x(), subWindow->y() - 20); + } + + if (qobject_cast(subWindow)) { + subWindow->resize(subWindow->width(), subWindow->height() - 22); + } + + if (qobject_cast(subWindow)) { + subWindow->resize(subWindow->width(), subWindow->height() - 8); + } + } +} diff --git a/sdrgui/gui/workspace.h b/sdrgui/gui/workspace.h index fff92eaa7..804d1d91e 100644 --- a/sdrgui/gui/workspace.h +++ b/sdrgui/gui/workspace.h @@ -49,6 +49,7 @@ public: QByteArray saveMdiGeometry(); void restoreMdiGeometry(const QByteArray& blob); QList getSubWindowList() const; + void adjustSubWindowsAfterRestore(); private: int m_index; diff --git a/sdrgui/mainwindow.cpp b/sdrgui/mainwindow.cpp index 338c08567..d1a7faac9 100644 --- a/sdrgui/mainwindow.cpp +++ b/sdrgui/mainwindow.cpp @@ -1173,7 +1173,7 @@ void MainWindow::loadConfiguration(const Configuration *configuration, bool from qDebug("MainWindow::loadConfiguration: configuration [%s | %s] %d workspace(s) - %d device set(s) - %d feature(s)", qPrintable(configuration->getGroup()), qPrintable(configuration->getDescription()), - configuration->getNumberOfWorkspaces(), + configuration->getNumberOfWorkspaceGeometries(), configuration->getDeviceSetPresets().size(), configuration->getFeatureSetPreset().getFeatureCount() ); @@ -1210,7 +1210,7 @@ void MainWindow::loadConfiguration(const Configuration *configuration, bool from // Reconstruct // Workspaces - for (int i = 0; i < configuration->getNumberOfWorkspaces(); i++) { + for (int i = 0; i < configuration->getNumberOfWorkspaceGeometries(); i++) { addWorkspace(); } @@ -1321,8 +1321,10 @@ void MainWindow::loadConfiguration(const Configuration *configuration, bool from waitBox->setInformativeText("Finalizing..."); } - for (int i = 0; i < configuration->getNumberOfWorkspaces(); i++) { + for (int i = 0; i < configuration->getNumberOfWorkspaceGeometries(); i++) + { m_workspaces[i]->restoreGeometry(configuration->getWorkspaceGeometries()[i]); + m_workspaces[i]->adjustSubWindowsAfterRestore(); } if (waitBox) From 2f1d7002012a25c70791e4acfb101cee0f7cd942 Mon Sep 17 00:00:00 2001 From: f4exb Date: Tue, 26 Apr 2022 22:56:01 +0200 Subject: [PATCH 073/115] Spectrum markers dialog: fixed issue when there are no histogram markers. Fixes #1200 --- sdrgui/gui/spectrummarkersdialog.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sdrgui/gui/spectrummarkersdialog.cpp b/sdrgui/gui/spectrummarkersdialog.cpp index 7461cdcec..75e4711ff 100644 --- a/sdrgui/gui/spectrummarkersdialog.cpp +++ b/sdrgui/gui/spectrummarkersdialog.cpp @@ -109,9 +109,9 @@ void SpectrumMarkersDialog::displayHistogramMarker() m_histogramMarkers[m_histogramMarkerIndex].m_markerColor.getRgb(&r, &g, &b, &a); ui->markerColor->setStyleSheet(tr("QLabel { background-color : rgb(%1,%2,%3); }").arg(r).arg(g).arg(b)); ui->showMarker->setChecked(m_histogramMarkers[m_histogramMarkerIndex].m_show); + ui->fixedPower->setVisible(m_histogramMarkers[m_histogramMarkerIndex].m_markerType == SpectrumHistogramMarker::SpectrumMarkerTypeManual); + ui->fixedPowerUnits->setVisible(m_histogramMarkers[m_histogramMarkerIndex].m_markerType == SpectrumHistogramMarker::SpectrumMarkerTypeManual); } - ui->fixedPower->setVisible(m_histogramMarkers[m_histogramMarkerIndex].m_markerType == SpectrumHistogramMarker::SpectrumMarkerTypeManual); - ui->fixedPowerUnits->setVisible(m_histogramMarkers[m_histogramMarkerIndex].m_markerType == SpectrumHistogramMarker::SpectrumMarkerTypeManual); ui->markerFrequency->blockSignals(false); ui->centerFrequency->blockSignals(false); From 520106c7baf8c029626b8f9044805f54d932b54f Mon Sep 17 00:00:00 2001 From: f4exb Date: Wed, 27 Apr 2022 02:06:17 +0200 Subject: [PATCH 074/115] Massive UI revamping (v7): fixed devices .ui files to correct display wwhen creating device. Part of #1209 --- .../samplemimo/bladerf2mimo/bladerf2mimogui.ui | 14 +++++++------- plugins/samplemimo/limesdrmimo/limesdrmimogui.ui | 14 +++++++------- plugins/samplemimo/metismiso/metismisogui.ui | 14 +++++++------- .../samplemimo/plutosdrmimo/plutosdrmimogui.ui | 14 +++++++------- plugins/samplemimo/testmi/testmigui.ui | 14 +++++++------- plugins/samplemimo/xtrxmimo/xtrxmimogui.ui | 14 +++++++------- plugins/samplesink/audiooutput/audiooutputgui.ui | 2 +- .../bladerf1output/bladerf1outputgui.ui | 14 +++++++------- .../bladerf2output/bladerf2outputgui.ui | 14 +++++++------- plugins/samplesink/fileoutput/fileoutputgui.ui | 12 ++++++------ .../samplesink/hackrfoutput/hackrfoutputgui.ui | 12 ++++++------ .../samplesink/limesdroutput/limesdroutputgui.ui | 14 +++++++------- plugins/samplesink/localoutput/localoutputgui.ui | 4 ++-- .../plutosdroutput/plutosdroutputgui.ui | 14 +++++++------- .../samplesink/remoteoutput/remoteoutputgui.ui | 4 ++-- plugins/samplesink/usrpoutput/usrpoutputgui.ui | 16 ++++++++-------- plugins/samplesink/xtrxoutput/xtrxoutputgui.ui | 14 +++++++------- plugins/samplesource/airspy/airspygui.ui | 14 +++++++------- plugins/samplesource/airspyhf/airspyhfgui.ui | 14 +++++++------- plugins/samplesource/audioinput/audioinputgui.ui | 2 +- .../bladerf1input/bladerf1inputgui.ui | 14 +++++++------- .../bladerf2input/bladerf2inputgui.ui | 14 +++++++------- plugins/samplesource/fcdpro/fcdprogui.ui | 14 +++++++------- plugins/samplesource/fcdproplus/fcdproplusgui.ui | 14 +++++++------- plugins/samplesource/fileinput/fileinputgui.ui | 2 +- .../samplesource/hackrfinput/hackrfinputgui.ui | 14 +++++++------- plugins/samplesource/kiwisdr/kiwisdrgui.ui | 14 +++++++------- .../samplesource/limesdrinput/limesdrinputgui.ui | 14 +++++++------- plugins/samplesource/localinput/localinputgui.ui | 2 +- plugins/samplesource/perseus/perseusgui.ui | 14 +++++++------- .../plutosdrinput/plutosdrinputgui.ui | 14 +++++++------- .../samplesource/remoteinput/remoteinputgui.ui | 12 ++++++------ plugins/samplesource/rtlsdr/rtlsdrgui.ui | 14 +++++++------- plugins/samplesource/sdrplay/sdrplaygui.ui | 14 +++++++------- plugins/samplesource/sdrplayv3/sdrplayv3gui.ui | 14 +++++++------- .../sigmffileinput/sigmffileinputgui.ui | 2 +- .../soapysdrinput/soapysdrinputgui.ui | 12 ++++++------ plugins/samplesource/testsource/testsourcegui.ui | 14 +++++++------- plugins/samplesource/usrpinput/usrpinputgui.ui | 14 +++++++------- plugins/samplesource/xtrxinput/xtrxinputgui.ui | 14 +++++++------- 40 files changed, 237 insertions(+), 237 deletions(-) diff --git a/plugins/samplemimo/bladerf2mimo/bladerf2mimogui.ui b/plugins/samplemimo/bladerf2mimo/bladerf2mimogui.ui index b4ea73810..8cab40fc7 100644 --- a/plugins/samplemimo/bladerf2mimo/bladerf2mimogui.ui +++ b/plugins/samplemimo/bladerf2mimo/bladerf2mimogui.ui @@ -24,8 +24,8 @@ - 370 - 208 + 390 + 220 @@ -770,17 +770,17 @@
    - - ButtonSwitch - QToolButton -
    gui/buttonswitch.h
    -
    ValueDial QWidget
    gui/valuedial.h
    1
    + + ButtonSwitch + QToolButton +
    gui/buttonswitch.h
    +
    TransverterButton QPushButton diff --git a/plugins/samplemimo/limesdrmimo/limesdrmimogui.ui b/plugins/samplemimo/limesdrmimo/limesdrmimogui.ui index 141c06234..a474b5909 100644 --- a/plugins/samplemimo/limesdrmimo/limesdrmimogui.ui +++ b/plugins/samplemimo/limesdrmimo/limesdrmimogui.ui @@ -24,8 +24,8 @@ - 370 - 244 + 390 + 286 @@ -1329,17 +1329,17 @@ QToolTip{background-color: white; color: black;} - - ButtonSwitch - QToolButton -
    gui/buttonswitch.h
    -
    ValueDial QWidget
    gui/valuedial.h
    1
    + + ButtonSwitch + QToolButton +
    gui/buttonswitch.h
    +
    ValueDialZ QWidget diff --git a/plugins/samplemimo/metismiso/metismisogui.ui b/plugins/samplemimo/metismiso/metismisogui.ui index 15158d88d..bcb622bba 100644 --- a/plugins/samplemimo/metismiso/metismisogui.ui +++ b/plugins/samplemimo/metismiso/metismisogui.ui @@ -24,8 +24,8 @@
    - 360 - 200 + 397 + 234 @@ -816,17 +816,17 @@ - - ButtonSwitch - QToolButton -
    gui/buttonswitch.h
    -
    ValueDial QWidget
    gui/valuedial.h
    1
    + + ButtonSwitch + QToolButton +
    gui/buttonswitch.h
    +
    TransverterButton QPushButton diff --git a/plugins/samplemimo/plutosdrmimo/plutosdrmimogui.ui b/plugins/samplemimo/plutosdrmimo/plutosdrmimogui.ui index 2ecdf064d..d5eaaeb65 100644 --- a/plugins/samplemimo/plutosdrmimo/plutosdrmimogui.ui +++ b/plugins/samplemimo/plutosdrmimo/plutosdrmimogui.ui @@ -24,8 +24,8 @@
    - 385 - 278 + 390 + 319 @@ -1266,17 +1266,17 @@ - - ButtonSwitch - QToolButton -
    gui/buttonswitch.h
    -
    ValueDial QWidget
    gui/valuedial.h
    1
    + + ButtonSwitch + QToolButton +
    gui/buttonswitch.h
    +
    TransverterButton QPushButton diff --git a/plugins/samplemimo/testmi/testmigui.ui b/plugins/samplemimo/testmi/testmigui.ui index 162172ed1..9ede84f55 100644 --- a/plugins/samplemimo/testmi/testmigui.ui +++ b/plugins/samplemimo/testmi/testmigui.ui @@ -24,8 +24,8 @@
    - 370 - 297 + 390 + 368 @@ -1063,17 +1063,17 @@ - - ButtonSwitch - QToolButton -
    gui/buttonswitch.h
    -
    ValueDial QWidget
    gui/valuedial.h
    1
    + + ButtonSwitch + QToolButton +
    gui/buttonswitch.h
    +
    ValueDialZ QWidget diff --git a/plugins/samplemimo/xtrxmimo/xtrxmimogui.ui b/plugins/samplemimo/xtrxmimo/xtrxmimogui.ui index 19ba68c81..d5319eb42 100644 --- a/plugins/samplemimo/xtrxmimo/xtrxmimogui.ui +++ b/plugins/samplemimo/xtrxmimo/xtrxmimogui.ui @@ -24,8 +24,8 @@
    - 370 - 242 + 390 + 284 @@ -1284,17 +1284,17 @@ QToolTip{background-color: white; color: black;} - - ButtonSwitch - QToolButton -
    gui/buttonswitch.h
    -
    ValueDial QWidget
    gui/valuedial.h
    1
    + + ButtonSwitch + QToolButton +
    gui/buttonswitch.h
    +
    ValueDialZ QWidget diff --git a/plugins/samplesink/audiooutput/audiooutputgui.ui b/plugins/samplesink/audiooutput/audiooutputgui.ui index 07a82a594..db815efd5 100644 --- a/plugins/samplesink/audiooutput/audiooutputgui.ui +++ b/plugins/samplesink/audiooutput/audiooutputgui.ui @@ -24,7 +24,7 @@
    - 360 + 380 143 diff --git a/plugins/samplesink/bladerf1output/bladerf1outputgui.ui b/plugins/samplesink/bladerf1output/bladerf1outputgui.ui index 3106a75e5..b63d61509 100644 --- a/plugins/samplesink/bladerf1output/bladerf1outputgui.ui +++ b/plugins/samplesink/bladerf1output/bladerf1outputgui.ui @@ -24,8 +24,8 @@ - 360 - 159 + 380 + 214 @@ -557,17 +557,17 @@ - - ButtonSwitch - QToolButton -
    gui/buttonswitch.h
    -
    ValueDial QWidget
    gui/valuedial.h
    1
    + + ButtonSwitch + QToolButton +
    gui/buttonswitch.h
    +
    diff --git a/plugins/samplesink/bladerf2output/bladerf2outputgui.ui b/plugins/samplesink/bladerf2output/bladerf2outputgui.ui index 0916b7243..c53e8cbd2 100644 --- a/plugins/samplesink/bladerf2output/bladerf2outputgui.ui +++ b/plugins/samplesink/bladerf2output/bladerf2outputgui.ui @@ -24,8 +24,8 @@
    - 360 - 165 + 380 + 202 @@ -496,17 +496,17 @@ - - ButtonSwitch - QToolButton -
    gui/buttonswitch.h
    -
    ValueDial QWidget
    gui/valuedial.h
    1
    + + ButtonSwitch + QToolButton +
    gui/buttonswitch.h
    +
    TransverterButton QPushButton diff --git a/plugins/samplesink/fileoutput/fileoutputgui.ui b/plugins/samplesink/fileoutput/fileoutputgui.ui index b525560bf..c36dbb4e6 100644 --- a/plugins/samplesink/fileoutput/fileoutputgui.ui +++ b/plugins/samplesink/fileoutput/fileoutputgui.ui @@ -24,7 +24,7 @@
    - 360 + 380 144 @@ -373,17 +373,17 @@ - - ButtonSwitch - QToolButton -
    gui/buttonswitch.h
    -
    ValueDial QWidget
    gui/valuedial.h
    1
    + + ButtonSwitch + QToolButton +
    gui/buttonswitch.h
    +
    diff --git a/plugins/samplesink/hackrfoutput/hackrfoutputgui.ui b/plugins/samplesink/hackrfoutput/hackrfoutputgui.ui index a0a70366e..72924fe1d 100644 --- a/plugins/samplesink/hackrfoutput/hackrfoutputgui.ui +++ b/plugins/samplesink/hackrfoutput/hackrfoutputgui.ui @@ -24,7 +24,7 @@ - 360 + 380 207 @@ -546,17 +546,17 @@ - - ButtonSwitch - QToolButton -
    gui/buttonswitch.h
    -
    ValueDial QWidget
    gui/valuedial.h
    1
    + + ButtonSwitch + QToolButton +
    gui/buttonswitch.h
    +
    TransverterButton QPushButton diff --git a/plugins/samplesink/limesdroutput/limesdroutputgui.ui b/plugins/samplesink/limesdroutput/limesdroutputgui.ui index 0ecc90907..91ff5d9cc 100644 --- a/plugins/samplesink/limesdroutput/limesdroutputgui.ui +++ b/plugins/samplesink/limesdroutput/limesdroutputgui.ui @@ -24,8 +24,8 @@ - 360 - 209 + 380 + 264 @@ -934,17 +934,17 @@ QToolTip{background-color: white; color: black;} - - ButtonSwitch - QToolButton -
    gui/buttonswitch.h
    -
    ValueDial QWidget
    gui/valuedial.h
    1
    + + ButtonSwitch + QToolButton +
    gui/buttonswitch.h
    +
    ValueDialZ QWidget diff --git a/plugins/samplesink/localoutput/localoutputgui.ui b/plugins/samplesink/localoutput/localoutputgui.ui index 1037d177e..4d03c6d63 100644 --- a/plugins/samplesink/localoutput/localoutputgui.ui +++ b/plugins/samplesink/localoutput/localoutputgui.ui @@ -24,8 +24,8 @@
    - 360 - 47 + 380 + 68 diff --git a/plugins/samplesink/plutosdroutput/plutosdroutputgui.ui b/plugins/samplesink/plutosdroutput/plutosdroutputgui.ui index f60be2671..374ebddb2 100644 --- a/plugins/samplesink/plutosdroutput/plutosdroutputgui.ui +++ b/plugins/samplesink/plutosdroutput/plutosdroutputgui.ui @@ -24,8 +24,8 @@ - 360 - 197 + 380 + 250 @@ -775,17 +775,17 @@ - - ButtonSwitch - QToolButton -
    gui/buttonswitch.h
    -
    ValueDial QWidget
    gui/valuedial.h
    1
    + + ButtonSwitch + QToolButton +
    gui/buttonswitch.h
    +
    TransverterButton QPushButton diff --git a/plugins/samplesink/remoteoutput/remoteoutputgui.ui b/plugins/samplesink/remoteoutput/remoteoutputgui.ui index 7e2b69535..c356eb699 100644 --- a/plugins/samplesink/remoteoutput/remoteoutputgui.ui +++ b/plugins/samplesink/remoteoutput/remoteoutputgui.ui @@ -27,8 +27,8 @@
    - 380 - 231 + 400 + 284 diff --git a/plugins/samplesink/usrpoutput/usrpoutputgui.ui b/plugins/samplesink/usrpoutput/usrpoutputgui.ui index a148de972..7acddfa8d 100644 --- a/plugins/samplesink/usrpoutput/usrpoutputgui.ui +++ b/plugins/samplesink/usrpoutput/usrpoutputgui.ui @@ -7,7 +7,7 @@ 0 0 360 - 163 + 214 @@ -24,8 +24,8 @@ - 360 - 163 + 380 + 214 @@ -714,17 +714,17 @@ - - ButtonSwitch - QToolButton -
    gui/buttonswitch.h
    -
    ValueDial QWidget
    gui/valuedial.h
    1
    + + ButtonSwitch + QToolButton +
    gui/buttonswitch.h
    +
    ValueDialZ QWidget diff --git a/plugins/samplesink/xtrxoutput/xtrxoutputgui.ui b/plugins/samplesink/xtrxoutput/xtrxoutputgui.ui index 52734149e..44d32e25c 100644 --- a/plugins/samplesink/xtrxoutput/xtrxoutputgui.ui +++ b/plugins/samplesink/xtrxoutput/xtrxoutputgui.ui @@ -24,8 +24,8 @@
    - 360 - 207 + 380 + 264 @@ -886,17 +886,17 @@ QToolTip{background-color: white; color: black;} - - ButtonSwitch - QToolButton -
    gui/buttonswitch.h
    -
    ValueDial QWidget
    gui/valuedial.h
    1
    + + ButtonSwitch + QToolButton +
    gui/buttonswitch.h
    +
    ValueDialZ QWidget diff --git a/plugins/samplesource/airspy/airspygui.ui b/plugins/samplesource/airspy/airspygui.ui index c0bd4ebb4..d729442ad 100644 --- a/plugins/samplesource/airspy/airspygui.ui +++ b/plugins/samplesource/airspy/airspygui.ui @@ -24,8 +24,8 @@
    - 360 - 187 + 380 + 244 @@ -618,17 +618,17 @@ - - ButtonSwitch - QToolButton -
    gui/buttonswitch.h
    -
    ValueDial QWidget
    gui/valuedial.h
    1
    + + ButtonSwitch + QToolButton +
    gui/buttonswitch.h
    +
    TransverterButton QPushButton diff --git a/plugins/samplesource/airspyhf/airspyhfgui.ui b/plugins/samplesource/airspyhf/airspyhfgui.ui index 399a64899..ab1849681 100644 --- a/plugins/samplesource/airspyhf/airspyhfgui.ui +++ b/plugins/samplesource/airspyhf/airspyhfgui.ui @@ -24,8 +24,8 @@
    - 360 - 137 + 380 + 177 @@ -587,17 +587,17 @@ - - ButtonSwitch - QToolButton -
    gui/buttonswitch.h
    -
    ValueDial QWidget
    gui/valuedial.h
    1
    + + ButtonSwitch + QToolButton +
    gui/buttonswitch.h
    +
    TransverterButton QPushButton diff --git a/plugins/samplesource/audioinput/audioinputgui.ui b/plugins/samplesource/audioinput/audioinputgui.ui index 6be0b2e39..0559cd88e 100644 --- a/plugins/samplesource/audioinput/audioinputgui.ui +++ b/plugins/samplesource/audioinput/audioinputgui.ui @@ -24,7 +24,7 @@
    - 360 + 380 191 diff --git a/plugins/samplesource/bladerf1input/bladerf1inputgui.ui b/plugins/samplesource/bladerf1input/bladerf1inputgui.ui index 4d9ffaf4f..239cbe106 100644 --- a/plugins/samplesource/bladerf1input/bladerf1inputgui.ui +++ b/plugins/samplesource/bladerf1input/bladerf1inputgui.ui @@ -24,8 +24,8 @@ - 360 - 173 + 380 + 250 @@ -690,17 +690,17 @@ - - ButtonSwitch - QToolButton -
    gui/buttonswitch.h
    -
    ValueDial QWidget
    gui/valuedial.h
    1
    + + ButtonSwitch + QToolButton +
    gui/buttonswitch.h
    +
    diff --git a/plugins/samplesource/bladerf2input/bladerf2inputgui.ui b/plugins/samplesource/bladerf2input/bladerf2inputgui.ui index fa03a988c..d66972d0d 100644 --- a/plugins/samplesource/bladerf2input/bladerf2inputgui.ui +++ b/plugins/samplesource/bladerf2input/bladerf2inputgui.ui @@ -24,8 +24,8 @@
    - 360 - 172 + 380 + 209 @@ -558,17 +558,17 @@ - - ButtonSwitch - QToolButton -
    gui/buttonswitch.h
    -
    ValueDial QWidget
    gui/valuedial.h
    1
    + + ButtonSwitch + QToolButton +
    gui/buttonswitch.h
    +
    TransverterButton QPushButton diff --git a/plugins/samplesource/fcdpro/fcdprogui.ui b/plugins/samplesource/fcdpro/fcdprogui.ui index 9f777151b..394b09a1d 100644 --- a/plugins/samplesource/fcdpro/fcdprogui.ui +++ b/plugins/samplesource/fcdpro/fcdprogui.ui @@ -24,8 +24,8 @@
    - 360 - 394 + 380 + 443 @@ -619,17 +619,17 @@ - - ButtonSwitch - QToolButton -
    gui/buttonswitch.h
    -
    ValueDial QWidget
    gui/valuedial.h
    1
    + + ButtonSwitch + QToolButton +
    gui/buttonswitch.h
    +
    TransverterButton QPushButton diff --git a/plugins/samplesource/fcdproplus/fcdproplusgui.ui b/plugins/samplesource/fcdproplus/fcdproplusgui.ui index 191bca334..6c70b2ab0 100644 --- a/plugins/samplesource/fcdproplus/fcdproplusgui.ui +++ b/plugins/samplesource/fcdproplus/fcdproplusgui.ui @@ -24,8 +24,8 @@
    - 360 - 162 + 380 + 219 @@ -461,17 +461,17 @@ - - ButtonSwitch - QToolButton -
    gui/buttonswitch.h
    -
    ValueDial QWidget
    gui/valuedial.h
    1
    + + ButtonSwitch + QToolButton +
    gui/buttonswitch.h
    +
    TransverterButton QPushButton diff --git a/plugins/samplesource/fileinput/fileinputgui.ui b/plugins/samplesource/fileinput/fileinputgui.ui index 6285afe43..75136ae6e 100644 --- a/plugins/samplesource/fileinput/fileinputgui.ui +++ b/plugins/samplesource/fileinput/fileinputgui.ui @@ -24,7 +24,7 @@
    - 360 + 380 202 diff --git a/plugins/samplesource/hackrfinput/hackrfinputgui.ui b/plugins/samplesource/hackrfinput/hackrfinputgui.ui index 08a06ddef..691547490 100644 --- a/plugins/samplesource/hackrfinput/hackrfinputgui.ui +++ b/plugins/samplesource/hackrfinput/hackrfinputgui.ui @@ -24,8 +24,8 @@ - 360 - 230 + 380 + 272 @@ -667,17 +667,17 @@ - - ButtonSwitch - QToolButton -
    gui/buttonswitch.h
    -
    ValueDial QWidget
    gui/valuedial.h
    1
    + + ButtonSwitch + QToolButton +
    gui/buttonswitch.h
    +
    TransverterButton QPushButton diff --git a/plugins/samplesource/kiwisdr/kiwisdrgui.ui b/plugins/samplesource/kiwisdr/kiwisdrgui.ui index e1b799d65..3c0cfda1d 100644 --- a/plugins/samplesource/kiwisdr/kiwisdrgui.ui +++ b/plugins/samplesource/kiwisdr/kiwisdrgui.ui @@ -24,8 +24,8 @@
    - 360 - 106 + 380 + 143 @@ -317,17 +317,17 @@ - - ButtonSwitch - QToolButton -
    gui/buttonswitch.h
    -
    ValueDial QWidget
    gui/valuedial.h
    1
    + + ButtonSwitch + QToolButton +
    gui/buttonswitch.h
    +
    diff --git a/plugins/samplesource/limesdrinput/limesdrinputgui.ui b/plugins/samplesource/limesdrinput/limesdrinputgui.ui index 61d89ebaf..6c18b71f2 100644 --- a/plugins/samplesource/limesdrinput/limesdrinputgui.ui +++ b/plugins/samplesource/limesdrinput/limesdrinputgui.ui @@ -24,8 +24,8 @@
    - 370 - 209 + 390 + 266 @@ -1163,17 +1163,17 @@ QToolTip{background-color: white; color: black;} - - ButtonSwitch - QToolButton -
    gui/buttonswitch.h
    -
    ValueDial QWidget
    gui/valuedial.h
    1
    + + ButtonSwitch + QToolButton +
    gui/buttonswitch.h
    +
    ValueDialZ QWidget diff --git a/plugins/samplesource/localinput/localinputgui.ui b/plugins/samplesource/localinput/localinputgui.ui index ba4ffa686..084a027f6 100644 --- a/plugins/samplesource/localinput/localinputgui.ui +++ b/plugins/samplesource/localinput/localinputgui.ui @@ -24,7 +24,7 @@
    - 360 + 380 110 diff --git a/plugins/samplesource/perseus/perseusgui.ui b/plugins/samplesource/perseus/perseusgui.ui index 0b22c36a5..a5a056d8b 100644 --- a/plugins/samplesource/perseus/perseusgui.ui +++ b/plugins/samplesource/perseus/perseusgui.ui @@ -24,8 +24,8 @@ - 360 - 136 + 380 + 182 @@ -464,17 +464,17 @@ - - ButtonSwitch - QToolButton -
    gui/buttonswitch.h
    -
    ValueDial QWidget
    gui/valuedial.h
    1
    + + ButtonSwitch + QToolButton +
    gui/buttonswitch.h
    +
    TransverterButton QPushButton diff --git a/plugins/samplesource/plutosdrinput/plutosdrinputgui.ui b/plugins/samplesource/plutosdrinput/plutosdrinputgui.ui index 999f168af..96300f189 100644 --- a/plugins/samplesource/plutosdrinput/plutosdrinputgui.ui +++ b/plugins/samplesource/plutosdrinput/plutosdrinputgui.ui @@ -24,8 +24,8 @@
    - 360 - 240 + 380 + 291 @@ -1016,17 +1016,17 @@ - - ButtonSwitch - QToolButton -
    gui/buttonswitch.h
    -
    ValueDial QWidget
    gui/valuedial.h
    1
    + + ButtonSwitch + QToolButton +
    gui/buttonswitch.h
    +
    TransverterButton QPushButton diff --git a/plugins/samplesource/remoteinput/remoteinputgui.ui b/plugins/samplesource/remoteinput/remoteinputgui.ui index 62c10e414..b3dc8729c 100644 --- a/plugins/samplesource/remoteinput/remoteinputgui.ui +++ b/plugins/samplesource/remoteinput/remoteinputgui.ui @@ -24,7 +24,7 @@
    - 360 + 380 400 @@ -1077,17 +1077,17 @@ - - ButtonSwitch - QToolButton -
    gui/buttonswitch.h
    -
    ValueDial QWidget
    gui/valuedial.h
    1
    + + ButtonSwitch + QToolButton +
    gui/buttonswitch.h
    +
    diff --git a/plugins/samplesource/rtlsdr/rtlsdrgui.ui b/plugins/samplesource/rtlsdr/rtlsdrgui.ui index 74fce0106..97977c5b7 100644 --- a/plugins/samplesource/rtlsdr/rtlsdrgui.ui +++ b/plugins/samplesource/rtlsdr/rtlsdrgui.ui @@ -24,8 +24,8 @@ - 360 - 217 + 380 + 229 @@ -651,17 +651,17 @@ - - ButtonSwitch - QToolButton -
    gui/buttonswitch.h
    -
    ValueDial QWidget
    gui/valuedial.h
    1
    + + ButtonSwitch + QToolButton +
    gui/buttonswitch.h
    +
    TransverterButton QPushButton diff --git a/plugins/samplesource/sdrplay/sdrplaygui.ui b/plugins/samplesource/sdrplay/sdrplaygui.ui index 000007b44..296236f62 100644 --- a/plugins/samplesource/sdrplay/sdrplaygui.ui +++ b/plugins/samplesource/sdrplay/sdrplaygui.ui @@ -24,8 +24,8 @@
    - 360 - 202 + 380 + 251 @@ -651,17 +651,17 @@ - - ButtonSwitch - QToolButton -
    gui/buttonswitch.h
    -
    ValueDial QWidget
    gui/valuedial.h
    1
    + + ButtonSwitch + QToolButton +
    gui/buttonswitch.h
    +
    diff --git a/plugins/samplesource/sdrplayv3/sdrplayv3gui.ui b/plugins/samplesource/sdrplayv3/sdrplayv3gui.ui index aef48776b..2dae1e0bc 100644 --- a/plugins/samplesource/sdrplayv3/sdrplayv3gui.ui +++ b/plugins/samplesource/sdrplayv3/sdrplayv3gui.ui @@ -24,8 +24,8 @@
    - 360 - 234 + 409 + 297 @@ -767,17 +767,17 @@ - - ButtonSwitch - QToolButton -
    gui/buttonswitch.h
    -
    ValueDial QWidget
    gui/valuedial.h
    1
    + + ButtonSwitch + QToolButton +
    gui/buttonswitch.h
    +
    TransverterButton QPushButton diff --git a/plugins/samplesource/sigmffileinput/sigmffileinputgui.ui b/plugins/samplesource/sigmffileinput/sigmffileinputgui.ui index 35307e9bc..1c50bcce2 100644 --- a/plugins/samplesource/sigmffileinput/sigmffileinputgui.ui +++ b/plugins/samplesource/sigmffileinput/sigmffileinputgui.ui @@ -24,7 +24,7 @@
    - 450 + 470 16777215 diff --git a/plugins/samplesource/soapysdrinput/soapysdrinputgui.ui b/plugins/samplesource/soapysdrinput/soapysdrinputgui.ui index e5d81f55c..9c3494e51 100644 --- a/plugins/samplesource/soapysdrinput/soapysdrinputgui.ui +++ b/plugins/samplesource/soapysdrinput/soapysdrinputgui.ui @@ -24,7 +24,7 @@ - 360 + 380 16777215 @@ -425,17 +425,17 @@ - - ButtonSwitch - QToolButton -
    gui/buttonswitch.h
    -
    ValueDial QWidget
    gui/valuedial.h
    1
    + + ButtonSwitch + QToolButton +
    gui/buttonswitch.h
    +
    TransverterButton QPushButton diff --git a/plugins/samplesource/testsource/testsourcegui.ui b/plugins/samplesource/testsource/testsourcegui.ui index 6a25d1d73..4852944ea 100644 --- a/plugins/samplesource/testsource/testsourcegui.ui +++ b/plugins/samplesource/testsource/testsourcegui.ui @@ -24,8 +24,8 @@ - 360 - 331 + 386 + 343 @@ -1020,17 +1020,17 @@ - - ButtonSwitch - QToolButton -
    gui/buttonswitch.h
    -
    ValueDial QWidget
    gui/valuedial.h
    1
    + + ButtonSwitch + QToolButton +
    gui/buttonswitch.h
    +
    ValueDialZ QWidget diff --git a/plugins/samplesource/usrpinput/usrpinputgui.ui b/plugins/samplesource/usrpinput/usrpinputgui.ui index 6916cc9eb..0d6c974d7 100644 --- a/plugins/samplesource/usrpinput/usrpinputgui.ui +++ b/plugins/samplesource/usrpinput/usrpinputgui.ui @@ -24,8 +24,8 @@
    - 360 - 174 + 380 + 221 @@ -780,17 +780,17 @@ - - ButtonSwitch - QToolButton -
    gui/buttonswitch.h
    -
    ValueDial QWidget
    gui/valuedial.h
    1
    + + ButtonSwitch + QToolButton +
    gui/buttonswitch.h
    +
    ValueDialZ QWidget diff --git a/plugins/samplesource/xtrxinput/xtrxinputgui.ui b/plugins/samplesource/xtrxinput/xtrxinputgui.ui index c26cd98a5..4146cc88f 100644 --- a/plugins/samplesource/xtrxinput/xtrxinputgui.ui +++ b/plugins/samplesource/xtrxinput/xtrxinputgui.ui @@ -24,8 +24,8 @@
    - 370 - 207 + 390 + 264 @@ -1078,17 +1078,17 @@ QToolTip{background-color: white; color: black;} - - ButtonSwitch - QToolButton -
    gui/buttonswitch.h
    -
    ValueDial QWidget
    gui/valuedial.h
    1
    + + ButtonSwitch + QToolButton +
    gui/buttonswitch.h
    +
    ValueDialZ QWidget From e1640e7ee1fe46d2d1688815096dbf1dc08284a8 Mon Sep 17 00:00:00 2001 From: f4exb Date: Wed, 27 Apr 2022 13:53:56 +0200 Subject: [PATCH 075/115] Massive UI revamping (v7): Changed auto stack tool button to button switch for better on/off visibility. Part of #1209 --- sdrgui/gui/workspace.cpp | 4 +++- sdrgui/gui/workspace.h | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/sdrgui/gui/workspace.cpp b/sdrgui/gui/workspace.cpp index 143dde748..eb0652ccc 100644 --- a/sdrgui/gui/workspace.cpp +++ b/sdrgui/gui/workspace.cpp @@ -30,6 +30,7 @@ #include "gui/samplingdevicedialog.h" #include "gui/rollupcontents.h" +#include "gui/buttonswitch.h" #include "channel/channelgui.h" #include "feature/featuregui.h" #include "device/devicegui.h" @@ -117,7 +118,8 @@ Workspace::Workspace(int index, QWidget *parent, Qt::WindowFlags flags) : m_stackSubWindows->setToolTip("Stack sub windows"); m_stackSubWindows->setFixedSize(20, 20); - m_autoStackSubWindows = new QPushButton("AS"); + m_autoStackSubWindows = new ButtonSwitch(); + m_autoStackSubWindows->setText("AS"); m_autoStackSubWindows->setCheckable(true); //QIcon autoStackSubWindowsIcon(":/autostack.png"); // FIXME //m_autoStackSubWindows->setIcon(autoStackSubWindowsIcon); diff --git a/sdrgui/gui/workspace.h b/sdrgui/gui/workspace.h index a12762253..5f23e9cbb 100644 --- a/sdrgui/gui/workspace.h +++ b/sdrgui/gui/workspace.h @@ -31,6 +31,7 @@ class QStringList; class QMdiArea; class QMdiSubWindow; class QFrame; +class ButtonSwitch; class ChannelGUI; class FeatureGUI; class DeviceGUI; @@ -71,7 +72,7 @@ private: QPushButton *m_cascadeSubWindows; QPushButton *m_tileSubWindows; QPushButton *m_stackSubWindows; - QPushButton *m_autoStackSubWindows; + ButtonSwitch *m_autoStackSubWindows; QWidget *m_titleBar; QHBoxLayout *m_titleBarLayout; QLabel *m_titleLabel; From 88de17a8d4eb47285d7842748c4a789f45c16992 Mon Sep 17 00:00:00 2001 From: f4exb Date: Wed, 27 Apr 2022 19:38:20 +0200 Subject: [PATCH 076/115] Massive UI revamping (v7): Changed pacement of presets and add channels in device top bar --- sdrgui/device/devicegui.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdrgui/device/devicegui.cpp b/sdrgui/device/devicegui.cpp index 19f4a8e15..3b72ca117 100644 --- a/sdrgui/device/devicegui.cpp +++ b/sdrgui/device/devicegui.cpp @@ -143,8 +143,8 @@ DeviceGUI::DeviceGUI(QWidget *parent) : m_topLayout->addWidget(m_settingsButton); m_topLayout->addWidget(m_changeDeviceButton); m_topLayout->addWidget(m_reloadDeviceButton); - m_topLayout->addWidget(m_addChannelsButton); m_topLayout->addWidget(m_deviceSetPresetsButton); + m_topLayout->addWidget(m_addChannelsButton); m_topLayout->addWidget(m_titleLabel); // m_topLayout->addStretch(1); m_topLayout->addWidget(m_helpButton); From aac0e8954c367f42589819f7f5b99dd7e261a1ef Mon Sep 17 00:00:00 2001 From: f4exb Date: Wed, 27 Apr 2022 21:40:29 +0200 Subject: [PATCH 077/115] Massive UI revamping (v7): updated documentation with latest changes --- doc/img/ChannelWindow.png | Bin 31954 -> 31799 bytes doc/img/ChannelWindow.xcf | Bin 74318 -> 74993 bytes doc/img/ChannelWindow_bottom.png | Bin 5530 -> 5257 bytes doc/img/ChannelWindow_bottom.xcf | Bin 18590 -> 16566 bytes doc/img/ChannelWindow_top.png | Bin 9647 -> 9229 bytes doc/img/ChannelWindow_top.xcf | Bin 35027 -> 32333 bytes doc/img/DeviceWindow.png | Bin 30881 -> 30872 bytes doc/img/DeviceWindow.xcf | Bin 81246 -> 82915 bytes doc/img/DeviceWindow_bottom.png | Bin 3276 -> 3032 bytes doc/img/DeviceWindow_bottom.xcf | Bin 14194 -> 12178 bytes doc/img/DeviceWindow_top.png | Bin 11528 -> 11279 bytes doc/img/DeviceWindow_top.xcf | Bin 47954 -> 47194 bytes doc/img/FeatureWindow.png | Bin 33838 -> 30250 bytes doc/img/FeatureWindow.xcf | Bin 103916 -> 93994 bytes doc/img/MainSpectrum.png | Bin 57910 -> 116015 bytes doc/img/MainSpectrum.xcf | Bin 152027 -> 343815 bytes doc/img/Workspace_top.png | Bin 9124 -> 10867 bytes doc/img/Workspace_top.xcf | Bin 38063 -> 55439 bytes doc/img/Workspaces.png | Bin 54229 -> 54627 bytes doc/img/Workspaces.xcf | Bin 137042 -> 129997 bytes sdrgui/channel/readme.md | 8 ---- sdrgui/device/readme.md | 73 ++++++++++++++----------------- sdrgui/feature/readme.md | 11 +---- sdrgui/mainspectrum/readme.md | 12 +---- sdrgui/readme.md | 17 ++++++- 25 files changed, 49 insertions(+), 72 deletions(-) diff --git a/doc/img/ChannelWindow.png b/doc/img/ChannelWindow.png index fe980a6377f76527b60eb187a5ba2bc675751623..c08a109d044eca25bbd2b48e9761dccaed458b1b 100644 GIT binary patch literal 31799 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 IzjWLG1K*pmGynhq diff --git a/doc/img/ChannelWindow.xcf b/doc/img/ChannelWindow.xcf index 834ea8e2fdf3e5534ea9c50433e0c148bae4de7c..e0f63eaef32ab6d3ab7e176c46b3fc660a59ce13 100644 GIT binary patch literal 74993 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 KEZwSE_WuC2WzSCl literal 74318 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 OF}K#-BykPmvi}D%UsMYK diff --git a/doc/img/ChannelWindow_bottom.png b/doc/img/ChannelWindow_bottom.png index d3602df2dd9cf73ab65a5b7805d629b25f9596ce..81953046d82781cf39123813626ebb9bb0ecf413 100644 GIT binary patch delta 4872 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
    s|DI6{rLfX5!WRk8p$EGk Wx1|WZOu;i2*aLNKMCm>2=l=s%uc8M4 delta 5185 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 diff --git a/doc/img/ChannelWindow_bottom.xcf b/doc/img/ChannelWindow_bottom.xcf index d92c777b9d88b870010bb38869191ce197c2f070..09d2acbd8b0343e13f090e368c1eaf6dd35f412b 100644 GIT binary patch delta 1581 zcmcK4Ur19?90%~-Gu>>v%yrefap$_OmTof?R$xu98=4VG8WmBgS-n(H`4C|cG7P~W z$m&1_g%9;$?WO2|MukQ%1tR?cDJrVBguoz%MZwlOzpoEH7Bsl~?0f$0{LaQ1yQoYI zDtGoMJ@;+$L zcAw%98CA10r@^NU!*F)c40Q}<-~{gt>uKkEip~X?BTF8V%Z|g#aEf%DftTQQc$ai1 z-~`0?s1C~Nc`46^{X7-)ird~<2lKplVWul#DP|SLjFe9IH3>Lzy^pPFj7xBl7v`4(u>>&(^AF82kJHotFFb)KPsHN z;@epC5$}#0Ml?$!8++gpc#>a;>#^86ifeID8`>x*Fg$S#c|bgm&w0Bm^KRS!dc>{P zpu(Dl*It))i+$z=;XI>=O_?n;o-RBJ4NC4KQ@AryW5^k~_av^MErxE=0-d*RY{{+U+b+IC&rLC3n4y*l;|i!b9v*R{LwhekmYi8=)om5A{rEJEZ$ zR7ljw%VUB;A-*L>G=XrwMH8oP0j0MMRDwYhj58{jXtHQVAnW6E&+l7{#zK?3&;6eJ zJ!RPA9C&ASct4G~|%?$&zME7)G`YSBDcxsBZuSf_c;vhB&ZR${P+X2-OW z1T%uQ6<%&AD%TZ_m*r(Q&8cIz2ThAl7>+pW=t|*eX2Nkqrf4f1@d@4G!-Y(KSeF^9 zGjr5At~osG#%0P())DMGW-K39X>BV6- zUcH3vh(xTwb|uI=zmS;_G3-nnhNi z$mNSot9Uz=W%-ke81s_Ty{H`PGKp^OLB}1`3ViAge93!Od~*Nom!GVfWPw=5&Z;x2 z=HD#8#r{3=TkJn%=R9GL7CbCpu^`@=C|_mU*=J z5m`w50`*K};Ym0t3#odR!m5|ZxmX^n>nN^X08?-|Tn!(EPeM*6HbIP^!1#%`(f+a2 zEyVskyjs_iUQtAqS|@8r_OhHOZofMFdrhlfAnT}KOC6iN5Wfe_HL{NSX=-l3=|-eC z4pDpKHn4VK7SgtkdUo}~0k~Wi(sqHu_F=LEH@1TzejVCy1bz)C$xb}T&hHTa1~GnD8O2>y zaK2Pk*ztybduKzRhxHiGv9(v;Xmzil6nC%QFLsq;D{&)5>3)$+y#?QcpONnVpFZNa z`&(-7JwRT6hD@oj941Lc;dW1p6DqMh2e^GlZ?&rRACvbnjTvNKpv=rG@Ev&{(`Zh+ zKjY$)eix3yZ{SbxPcoAqHiEbwE`xgL3sKrvPE ZNr_GTxbUKz=Q=Ky=Hi>iIjf1){|#+r=UV^( diff --git a/doc/img/ChannelWindow_top.png b/doc/img/ChannelWindow_top.png index 31d84ece0ef47cab515cf8a6073e5d60ff9a7d78..aeee32ccc19a2de6262a4b52b0d4be29f213c7f3 100644 GIT binary patch literal 9229 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 diff --git a/doc/img/ChannelWindow_top.xcf b/doc/img/ChannelWindow_top.xcf index 91e1fedbc16926cf07207557b858d57ea4cd7c1c..2074ca6bb462b6a8846ddbeeed6e352b75f0631f 100644 GIT binary patch delta 2273 zcmciC?N3uz90%~*(+4O8wG`LTYF7p6Xa-&Bq|)-xQVRj3g3k|A;s}hf7wAk(wgPn{ zT{c)GA zd+z!6-23}=g5CQ^cYcR1rCX51pXpoDGDj|bLKeLwiw8*8*W~IS$@fg;``x7f5V`g) z8EzmO4wG?Cwp(8vm&id+R5HP;e5ciQhVw(Ku0leL%@Df$`y3Fk#7 z>lBsN%kU0-1n1!!(iVlCa34GXzk(C+6r3T8RDTe)mTA8=pPQU!BeNHXO!jg$lRYZV zs>{7x&m5m?xg2L`LDyc`4-bi4&T1;1O>i6Rg}A?*-%!2cK70&cz<>?OF64!r3RzUEl<=snfsy=OOCh=^6lDHe4+d|#!6=&6Ty;N3)Aog0h3pMuYyFj%Md-BnqBp-e%-vZ5l zz+fRrBEkfiey3=jT# z7)SGqqxr?r{NiYS@s#*J@|bvEYmbRF&xt(E&(u848LscV&RDC`p{a?tF4Bb)f52yO zQRHbGp>jhQ;^XZ&)^;0>x0k>GjKR%NT{cN|0_T;$&zCrd@vCr7$iLrmQZKuiEMMHw zk?P2&N#(yzxzQcjmqDo{sUsqglISc+JQzuIGG$i#PWFArVP!;;wwcL256JGVmOuqDgy!hjhl{z-qqn*?sZl&RJ+R^Y@7!@ZqgyEqY8XjtaJK?AB e3-}$xvO`m-FT*?V5uAr_$YC=q7V_D&NB19VBvQ`+ delta 3702 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 EFSn2xv;Y7A literal 30881 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

    F)LQ*>pE$G^93pJx!;t_E%iM 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 diff --git a/doc/img/DeviceWindow.xcf b/doc/img/DeviceWindow.xcf index fa1ca2efbc70dfcd580184f584942a1092449c5f..8177ac32defcb09c9be0afd1252ff09a5c12bf0f 100644 GIT binary patch literal 82915 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

    -;|kYJiU9*eE{=~0k|pgCZr+4 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 KL%-{ivi}E+8*C^5 diff --git a/doc/img/DeviceWindow_bottom.png b/doc/img/DeviceWindow_bottom.png index eb436eabc2b6cc349bd28f3c6c0777aeaa03a360..e6a39858820b2d5451bf74336ceb931371b332a9 100644 GIT binary patch literal 3032 zcmcImc{r478=q*5tOqgK$=FVoGL^BF-Hd&wNNA9KnX#Rrkt{ip3`1&=r8D73$%Je% z2r(oP#xjY}VT2lF`Cj#%>-w&9{{H@W-|KyU*K^;`b6>yTeLuf6TWd33E-@|;2*is* z8ry?FhiJezj*|m;@8){813O85JBPxt5%$p73*@JyIL=Zq#jU_zV2%*_4t%b;Z!Y__Ps;EIp^pH%!gAq)Q~&fkz;e3G;r#! zF5J7!VZB4ZyLhlGLi|}tv7n8BnXpr79KOpZd0oVh;_lOrTZ5)x#(OnJ{dr}to2VEh zfkd5hY*UhtEsH5D*kh$wtNhy6vfMQq%soH4>VtoUz2e`4wBiEi6&2}aVG;{5PnRtk$d1LUSA*Lza|ydz`k*QkszyOpOAaL z2r@OWaKA>MsX9oR|5g=y`z=Jqs@Z%B1WA2)^?J>y-qhsSR2@F>-tPB9zx@$W;gM*H z1$g1`wlFgW?SuM2ph|+!D1aOZLtemuKx%Ra&!L!y=c51+f<;-GK;{pN3ra~8KRd|; zAW^J|E7mA9IM^oy3o?rE@xuC`WupDDS7pplR<u$VbM=3#B!`6d1UH#Uk{_3AZwXhbysbi1O> z`pP;TGYUe=pa?XJZUH0#jgUuakO(kQYZQTp3`3 z`wxzBnR(pEyT+Ni$4Jj{oZjM!`}}P$)9PmXR~PC)D`YHxH9MFFR8jt6#LRR4)#LG- zbM53xukEj^+3E)C=L@n4^OpP)L5VNo2UkN8n<8{uj_B$H8)0hieI7Cz;ed*UG&Mu{ zM%ozoww`^1t*X^A9a-%oAuk};ybwfP{@}&Nf@O-r!a|Z_EC1|JK^73xQ%Yo)#x+W5 zbWuN0?qoC5v3C7x2?Kdl%@%qzp81((tU9=neg-(mRF ze}~Xa#r#_&eahyJzD!g%{Dg{$&0ZgT)J;n&VXKdN{_GBqUxnHFRO`kinT;V+MAK;g zLP`B>af=OfyU&HuQqx$(KYM35Frof%7E^~@U>0dk5w8`1D^9cUNq>iP!`Gxra z>odJL9_Xhf!-E zAP$qYX9n%wuN~jnwmAqPoZIk@pSB_}B*a&WW_e(X8nY92KP4)kN)C8U5{LDinI201mJM`uR}1k#s-DZ8%9SdZROZ`>2@72*>5=U$WRk5?$BLrZww}lH zhB)Jq4CCX&)#d9tHUyqe3%6d)qS5G+fg>s!aQKl#ZXMx!NLUQBnT5bE3?_Us4VZlV z__1uMI;y-6j?VK$;BYu)H8lkZKFrSM7q2RTTb)X3Y8-o0%rC_wyvc@+j*hmDj>Z@a zyfJFIq^>TyJCa`lG2@|#N?C7@XGLuqg5BNNvK0++ipEg9scvv6LnjyM)h(=5@3YUnmhbeZk zI=$(-n~Tj)YD?7b$*E=`dFqo5WkuB7yDOY3U0dR`7-2Dk%Ytbg;3_wJdqW&fC-eLs zVtIKvCnv{!^#w3EVy(%IGsY)|naL15SaE`|e}xl z43$?iY;~s1z-Eju3X2%Fo4qZm(@mrK&h-+5CZ1O}FxVi)bmu-K0L2o4>Sdqkp1 zc=#!Qf9YM)$>8zR3O0ptQ)H*q@y9^It}auMn7H`j;-YAT8&w&V0-vKEIT^o={lhYU zD72AIclYhTcLsfQK#>G8F)PK^nY8P=;`hCULL1>oSOqiEYSXN(o@&;p&|Qr#%e;ag}HhC zQblEoBrP$K&6h^%aj?4qMy|=T)#6K{=8~k#o zu#i^c^BQSm(>haE8n;?F7>2MsEhf*&R`N!P5q5WHXU8BDEswf#dHmr{(fzZ*VitL7 zl51Nsob@X&KHt?0mBO)B^!(_FGM+S&mZOu?o2e;lgv1{uC2R?20!N;pdjz?-xlu4d zdfa-yB?*yLSSST#{`&Q6)0Zysm0;l7a9F>Jq1T7^73$r8SLp)3(v^-0!Osz0YJU3~ zkSO!u>C-Gg?gs4IkxP!imTJH@M$XP6F7Dyr0X>!kFE1~!s~sS4BO~_6$jBTbF?=Gp zE`04HN?Tj|u1>TXaOje%s(w9dp!`*d@9?7^@hZPD_vf?s3lo&zQQb5J(@tLL;t^53 zXsj#6WQ{#(Zf-6klZ9?|Zg-yIWUHvGOzZUiYr3_lu(0sZXVX$Ufc(C$3~^;u)rU1T z1izwqk%aN_@oN)OS`kUYC!AN8DywVo``69}z1Ih1Ib80r5`IwT>&3U^*!6x*$S$v< zU+UVd8PVXE!O2N0KR-n+EiLvvYYPi*D=Vu%-Rf>{Y+wPxqiGI}xNmrbfrEml8eq&M_#;m zF@}*0<@ob{Ru!neUv;o7b!g6a304umobyZ@iJ_-Q!I0d6F(|JSel?LrQ~13PnDADBh| z1P`2x8Q^<<_?5p`{%vRE?)A+KQ;y#@HLRbr6(Nqpeb-A+?iVYaLZI30iGzZ7{8h zr3kf)rM6g0sVx&rr3liB*7jxQd!Fz6{`k%x_qq2u_rCW$?>WzV&Ij;-%p)j}7+_)H zgtV}bM~B|I84z@Xherh$hcmfI{pEC*&)5Sw7sC%`B#UI`^~)}um&=?I1&dL)-nTD~ zYTX)rM5;#!l23q4rOHG#X>Sed1>+Y$f4m?No~LE)wvvVwyAv4LrbbSE3`ihd(0Q;8 zQ5cwmbLi{}3U*z?TJb1+QH%$)Q**z2oy$&K*?pUmSrMU~zZADIG*Yo>&%bN=mq&@L z)E58Nmy%-498B~Lo^-N6A`^1g>@OQ~zh-NQrlI=gKqU@2B$1NZ^&6=F%z$mOt+CZ< zSC{ndq}Tm|D;l)G;_fS)MJ?gw+Uy(Tv4`^at<)7xi0K5Xyf`MLeMRqAz>XAPx^U-P3UAg^@33WS%dSp9d`0H;U<*2`KG>2b*+~HLHuJ6cmq>C=D zJ$TQ5o{fxdF1O0OH+%sdGCQ$ovJeWAjvZCD2!)#37di3F4SjkR+SaBqOj+KUnC@BS zdsIZm>$kNlod1=y7M2;oIeB+yhn0&0OwkUZCD&zzz7jflEyAHAMW9scsb;ghvqkwbEPQb)BIpGZ<&nAW)+#}!K*qMupQ3(imug(xJF=7W{U(ZU{mrrP*Y z`_Yq>JXzH#<1@-r)!Pe9&qBueru`@EA~9_P3Bm1!Z39{omx&lSYtN6xYp!K-EQVqL zg8)(mQI#B~`D83)<4A1n1K*@K|7SULFgKhlgX&4HvI?aqy(0y@u^$XZDnIUM?H*PX zkxC*W)|aeksaZiSJ-Nclln(#N4wCxrY+z*9)f$M$Sg8dOi<)CGDaS`BI9U-g96-q;j*(XvM|sk7Zj>`b;OWjj`XvAAD5kQn9Bzet;DQ2Pz}WI8+kq|+bDyc%^VvWvoJm@ zH@b_vnCPSebWYTMEuHbHBO9wStQEzWsG#j^7&pqrE=Gusj*iN?bBemU1bOKh^01|1 zdn2o*pM16Pd#siRBRo9(r*#00hZAQkE{Ia6xVXN)3Uu_71kjJ70T12Pt+~g|73U4p zvHBvXKOQ^~M&K(vUN6`Mjnki~A^qJ*Y1Mh^Fkt zc`4$g$LqVgx@u}`w{{tsV-pkBAt76ZJ=xHPzl_WL^3Z>tI^4Rm>&!P310p3k??o)H ztThm_4NULPjqew&d$!-5fQl0Ha7A;(e~qS zXlPiQYu+PvOwG>P`}ry9>FL2QT*#N6N83Uw@S(P|(k(m#^^J?(01Hr~5e*1wnr}a4 z_1y6HcXn|ZXAKcGh^3|_g@yN+%s`%3Wo4==DhLKcq!gMS$G~>U2qvkht6%TSQiVB4 z{1Cs;*=}CZv@O_^DJM7jKo9@0M zUHPzUscN9S&l_WSz>}WVdjFx_E(f(6oJ6+^NBC7qD=RA}Qt3ahxJiM*n5{1G*vyPE z3BA0uq#d&pH1XPl$}K@|Vg5GwRBv%oFn?-Om%QXcKW595P>&fG!($Y7PTfdEtje+6}fxe@mA4F29mM<>H%gaO3#YIzk zdwUfm1sk5B@c4jlhVL}u%ZD;Pja`%-!WjE?3zJ(9zw2U61&)!Mwr$Dve+kz8{xN)_ z{)AcEOatcSt5?s<%hS;aJ1Z-eX8b8NR3(~jzRhMMq2lTu)rl{iOGmjWfkTSPJ^(#nc-3b{ts@xy0f243?Y>{w? zvcCRfo3l}gc)a*IHMOXnskzw`wY9aS#&r!1xy6?rZ%^N@sEXSQN0nc(QkP=9!vRXc z`*^r}KUTN_vyj?o-8Ci?p+@uT$*97t(6FZT#YLz5mRG{@_sC>TLqo%NeSJ)}u7-vN zTu(2AGaEt3&v&q}pn62Cw;X=|?Be3$bmdB4zDW!sa{d0I3L&oN|}o^N zesth3jGCI7&3*O|M@C;si3*N~ide3$pJ%_~pp*{OU6!*rb4LK>@dAt@O)C;ep{~uh z7VPiGR=;_Z0=KrY0dc?ni~*{X~c+wV($JoXk4@dXz68j0JsMi;8Z-*Fm?ez*29weThW?`(1wD8f$ zPw@(|#VS%0`wCQs+x)m-|Llvqk9ZWLS}j-MnnFBWLs~gllFhHh F{|l632_*mk diff --git a/doc/img/DeviceWindow_bottom.xcf b/doc/img/DeviceWindow_bottom.xcf index dc4a86e705418c026b037cc0eaa7f43efa2c9d1f..7f5e5fd38ac318a328b15c9d2e99232ea52ac900 100644 GIT binary patch delta 1433 zcmcK4&1=(O90%|xPqcB#+Vv$(x3p+2pqC+2DBc8zRPbd7 z4~jW>@D&sV3w0Ri#K9aQ^dQWE2M+@$dhjq%5t#=MHFwGiC=_Z+Iwombu!&U_Vtqaak6-j?9}eQu;o|PBKRmT1r5GHiM4BSuYWQK)<%_gsfdaFgs(ULtW=i2T$) z6l;U4^URP)Br`4&$xQp%{uR#6 ztVqN>PAl^^EWt66h>6|KKBVs4DR>Spz+3PMT!Jmq!l|)dA$|vOed{~oHkt1c`i}+| zf*qASuQ@bXvnOdVtfkXIl?lqOFk;NUOcv8{0J>yY|63EZ?Kjk47=}2`g`{)GdU#7l?7n<<^8J9iRryAk(_H{;;BoH#X{ez+eVhS)UcBw}3F zxrq1%d;k~WDqM%3;18icJNiW`yPtzyT?;zqgCfy{vO!j=@I1UKp1b@Q)9~FXHxNIE zP52V7kQIEODk|a_Ov27>O~iS)7lsFhG5j^uBib6%+r=H0oL9~DP1ItmEV{!hg-=pb J?|Kbu_%BRd*F*pS delta 2415 zcmb7_YiJx*6vywK+4pYuk$olGOtPDpbuBT~WRnRhKH4HapjE_2r4mJagNhXtM5j=} z2h|iWQmN?9CRHnbP#92As6tyU6bycRIICYmpi}nKljYt zd(PZF^UB20hd)@)@A!o2PpFp|dyJUeN6fuW?EH>s9UxYIAf6v54mXLd>*yBW5;O5U zuZxMEZ6Q9T^tiauxRl4mOUAfzA4wcF%x^_+v1FL@LM|&ffn@7`ho53aH^=zWh_Zz@ zUAGZZt|&8HsjUe6qv5Kr?+vq4Z@G=BCDC*lM2MYix^J{pke%4!y6lWxY`O+JtzvC| znE;d15w(1P>_*Fu=@38SEKBT8R-7m^rJ9v3Mp!`WFbjfQN@i#~q}Z~9%I5F~)%^zS z0E`2S8-BdLanr=wy+DyjCdkUuQ9(pDDU{gF&ns2h&mOtvaiX7--?l9>9C zL8wAq`)jGCRH2fG*0VEmar=9!P>5A^>WWDDDD6>}VRuVa9)z4_37#HQ55AJ$Dt4u> z%*0wxn;7mBsZ6O9zJ+9LEqFV4uUHu_IzJ(Jo)`&%1+WT^fmefHf!~93;36@KqZjRg z9s(~X#z3(*oE3LwFH@t_Nxsa|?vNhbO{k+Wq{ct7%yg_&v1)LsV%qZDV@)p0VtaN# z%ve_mHb8=2+ixpIVwX2(#!Vx&UmviPq(_>20^AHvssmPV(vu|94Pxe2&;j28XTamcj`zXO zK+I>aC!O60qNQecLmwvQ?$h-4gsqTK@N@!&8J$33)+;9G4WsLzPN0;fJ1h^xFIxL` z0;OqkmoVLp@a}%{caMOp!JELl!HwW%qJ?=2^A_f<*1SO6w5He-&Z&P#!22z8VbJJ3 zq7x~{bt2_{F=rOM2Z}olqwkPTroTvcS7*Uvpw!9qquS>^MDBr=;1%FHZ~}Y;bihes z6)&plbI{vBtY7^I`b+SDCTHTVQl7$ZrwHd)7I%G==zA{-C<}83cUASZxh) z_-XJ3@KvIJUAjlvwP(qH(J$ah;>A(03%r2HnDqAc{vHiD<`>e-udui=vP0K#QVXLjGkAKpfG?Q(Ixk)<7&)W{MFj)H&R2bN2 diff --git a/doc/img/DeviceWindow_top.png b/doc/img/DeviceWindow_top.png index 5cae79348d0c1b8425d7d40fb2b4dee2a464c7fe..7a6dfa4bf479ae121e97a3d47cb814101a5d8261 100644 GIT binary patch literal 11279 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 DOL6NZ literal 11528 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 D!BUnD diff --git a/doc/img/DeviceWindow_top.xcf b/doc/img/DeviceWindow_top.xcf index 89aec7d38b5609d92697eada0f976f99eb86f506..b183dce7a4e0fc1d0a76a2179a4a7f1c417a268e 100644 GIT binary patch delta 5628 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 z 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 G|N3w9*(aL- literal 33838 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 Z$FHlsm=^nc<5^3kt+ndqnpUG$_J1vt31R>M literal 103916 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

    T7&4OD>MJqo5OP|X=>+;I>$7*!m^S!D=uQ;DTI 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 B9l!tp diff --git a/doc/img/MainSpectrum.png b/doc/img/MainSpectrum.png index 8fb1ba3091af415ae19900d7181da3e56345342c..75533ecf940671b22cf58639e9278322b6b823ea 100644 GIT binary patch literal 116015 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 z zlYgrnfNu;q@N3xfV=-gq82&INHt2c=)>h=zTAsmK99jX`iEF^7VrUZYf3x5oinVxp zpwIDSq!=1IsPDMZ(*Vz$E%3dVkhjYWN$P(mtpC1yq>1B1QZ>8ez#{~LQZBr`D!F#O zB8GCg0N->Z%e|h2dI}E4aQ{qfo^8M*Qw?n*2X7_S=>5l2`07Fi^thuTmxHl@?I$N< z?^9X%ZLe41QVPrjOp@?^RS^$~wB**GY$`jfQ4+X+ejpijI?@E+#D;Hb@S?3@fLGTm zAoabzrbBg>PY3SLiXgKW4eqL!(`aXk+Y zEV!M3z9R=>3x3f^2V3&?HF z!;jX~nf>iZOit8=Zv*hIpTKSV*-B*>S{LxLqshUqJEHNA`x=j*jRdk@nqrzZl5l4Q zLQTmwwKjOBWHG~)nX})A=AJ0?(FD2&aN34Pry8wp_T|euHQ?qJ+}nivhfwk$)Kgb} zEdjqimYng<5}c(~4091N3258Sv zcFRIu_aPsL`8ffS_E0I(Ao(fg$jfjw1@9)c>0OMW*@DdlO~qxR@ zRmhq!Oo+L7Hwt;5#rnDq%?50h6_>ErgMg`+40|dT?=;7(SA?%p`g|=vMLsfv<0FBm zaSz%}`JEnCHTl?0Ys0uzg`6ueAM@aDL5AGZGs$ETTfimd!7puj?5ag5mtZRie|x4} zpzRCT-qQOXWyE}boYp;C3&YnLfrIf_Y5nOaeC255f6`OXt7~cKr0@Iax_$`kz=nq* zMP%Fx${S^kWoCAR@@wV8nrv)^HIT59fXe}8uh|I&b6%@J>`V#q7Zb?jB!BiYY7RgE z?OFvcB{Zy3O=$uzh=y1a!2TL6I07*TEip99hSvJnkOUV`024Uw>Xd^*N?xXNUJ|Tp z%ER@=g}jp&1$mW<5jBAMpoVDPT-1H&_u%at@N5VJ7bAVFXYl1rc8PdUQvbwObF!9_ ze`$NDQzHr)xU~i!ZYXji8G+HhWWe(=4^U!W6 zUL!by!=~~9D$*W)8tos;gqme(%z9ItZ_)4_F5S-J#E^lnCRuZ z1sK_4Uec}};;AW?=A#wm6CIkccMkWmI%1{*?0Ik!6zF?%K}PSzvJ9+)fja44815aY zK+H^q3nr%Uc2LDImV(2eiWP160Qher9;sGO(?($zys^67wXrtr)kPK2^{6};jx9DX9#`R-ujr^4*Mny< zeYc=6)v+a?%fu43o{ora@7j7zuLqTYKy5b+-IzQ_ksvJ0OU}9;6NtO$DW!XiXno6t zM-KFY&`PTtO(i77&c_4@uOwhAEe7YXt-P;F8|H!jSd371;0eE^QS@P5AJmoKKbNaYiG;SXIm-5H#22P?e?|C3IWMElYr#v zaY%Ar*oJ2{F-;qbFrU!mo@pqJ`|XmvdD9sLCdxq@8fr!^22f8bK=pSat+Ugy7^x_l zmR$1ze%w%#)3M-Y6_x{HD$8MgA7fi_9lMr-dT%et_xRLR2x%=K7A+W%4{tN2(8#GR zkXDU>=}*;W6Y$2m%=#wEg}SjQ>1%r+urf3hOuUg6&`(BTGR6Dq4p?D5=aCt##1y`I z(1P;|t&wG9XNZS273?~Y8YyUZ&W0aH;b~ny&iRlcLmutJH)oJ4D4ru77Ss07)HIzH zRP4ozV|acFyLI@;(mgpIOQL&ztozmVRL>iA__Ki;v%N8-Ca`oa)A`HZ%RfLHX$Bb7BbxLHN1>-yXnvQvdBmTTSW9 zGestJ22wGSSxLuUSIz2PU7pWZ*DE0Py}mMeIB9F5?D>V5hC~K3S+ObSBM6LO$$&=( z>JZ*smN#K14ZjV;Z+j~G`#uv~&G&QYt+)$V8I`HXBP0(%m^!8_2omn;-Y20|0fG>cfie$N;PvkS$T7bWd z!DW9`xjTis8}LS1pVf1qG!Gx7;c*={XRvz;-%a6<<~8DMkKy82=fFix9ksEVg>Mac zJ52xFzO$e{d%}X#nzCXpOo6Xr41yB@yMU|z^=I-i1w4%*N6_@)l?Ab1p!}c*T}f$E z19~x~N!$L}V~qhcW0EC{W!Q}CzFM|6){rN5AT`y-R*k~bnk1ok0>11EOi#y<@YBg{ zQ{K5yL>`)>jyCgb7_N>b=U!bDK-unSlzVeY&!q~aS5>-d?`RH@JV zZB6K1TJwh|9QY3bc;Ux2G?Us?f4T&=&(J-~!Py9^S@`)F_DV7;7Xt8;RZXHjs>4%D z?CHZ(u^wjwC3M?iuv;3iaBP^B;Dai3(u!Jm^EwzK_?wA>D6O$#M3z%p_9r;8YUtwItAySME~_@;z8AlURBV;j!eeouR^Xua)7a zD^LguT*ia&-I;*=jU`Qc?8WrmDh&sHSJ@Lope{gpPJ{ z5qq6Y=*W?dBU5-Xs13Fh({s39kqmy^f@xG!fuPXch7Hf_+JFzjnrgH{nkII_IzH-h zK{8#)=@g zR$N#x;Ro}IFey!8qohRraS&Qv*xlEAEhgboS!^I4q~p3z_fHkoVNX>%UtO<&)c5+z zCY7$7_roVm|CoSiSRsYp44jx~jz=1hjvzd6+u)=EiPRiT+OfzHoixuWT)(>^5n8X3Nk zhd@J{*H?#{p4rMlbD-l|Uc0Icn`;rsXW-i&JRQT;j5eHL7;Z;o#E+rxF&u!WA^8DX zU1;Yu88{z-KUjtBwxpH5A@*rw32Mlwo13cO11h6p{7w!lVat(ShHA2Q;swLfZo|m$M&S7l5!J1m@W39 z9f5un7AER&FQQ^9VtyRMu?;^AiP8Kd4tp`!iox?UZD6YjZ8S~*@_jLEJ4XVVFAM?n zoe?}*(Hg4zN%bY*w`p)E(2Z&{x5fg2?ojLIMMIN7i$$@2i$UnS@Nx%AO?amOcfv9S zSEDj{Z!N=pOOjf`5&&!UVK|TkYI|^JQ*7FK2cC~5yMDY1m3i1d24@T-4|W>*p3;Ug zpw<@UvAI7L)0Y^-Qb(Ch@u_6i`5|0sz>Bo}Z-*g`(A&21$)4Hps0%y$lA=D2!s`|I zM@5Xw=WruI&^*PG3g?g%Q~4TQYBV0zeFg7h-PSh$&!qW-0o@m?;zQZc4``VZygVCGhXq z@cD_P?p#ta5`l>t!IdTbuFzC0UBFc0_Gv=*sT6~^@{k?K!Q+RL%geOtT=Ewlmy+5Le-X!^3#!~q>ufxD8m!orw{#L(mQ1}>z#?tY>gSZSLQ(M zRwWESxhk*V?MoU^dUd@5Qs3*V-I7_bFwtSVwF}4;;93SgyaF%w;nos_&Xl<_XTTT! z8D{C3JV6ly-nyz_#aKj}#(D`JwA2X&LXhn#Y;&$EdjHuu>;R16egtYYxYki_(Rx80 zTHBNParaz*=XM6(n};`+WFBtVGBJmPup5Fw7>>^2%bJ2l7l+#T1Gc213L5Lb@HG0q zy9=*}1@u0+46o&&>c>3nmR00y9{fK8$w}|8%3qb9XcRaURqFR&H{diZ$?)TfB%=1I zMt9e0Vq9v65H=xwA}O%dmSlEo3DSK{ubp{15H~i{^i9!K=RKTgV$7MqC@iV99anhb zSybQog{=%2*Y~B(jbSY$*~KE^9kt3b(~JUrq@Ig^5QHP zAwN}nUs9^;0&HkVL1-B;`)wFLz3ups?WVU767q*#$>f3pu3aQ z^jFx0n^&M0P`Y*h6i&`G-kxjgc$TIEZUTOIT@2Sb^4K*daAyO4X~F$@{m#RoLR!~d z#b9JSc(S9zw|_9F$<9BUXu@!i!W$Ohxx=ts)jha#8S+)7ncrJf9#}7`Gag3)cw^0< zT%GEC$bUD2Uz~_#+6XCz;uiriuI;A6G*dySU%+i2V8>k@HDyipy%Y*iay7Z>8Dgz= zn}8$9a5t&AlV`SoVBZwTb^SE!fdxx`y7hRZMlCoMc-yrl2j*(>_vPalZw@utdY07z z!E26~wNO}qBh`UH9CjOej!IKaq~-$3I$ImU8xeTmh-JNK!reDw&2S>g_N2W{FNsL_QMr{ z$WP;X1|BtFXGe7~VT$>GqX2LCL93s&<$HWHp{6Su((zl-kS33NBLVuwxbm9D2{EyT zrQEN4SplA3_yMM!u?A31GjJ^oKS;vYCWN!_nWJHx@l1z)8#y%;zv@ehy zy@fHDiZT3o*-ymxV6iHjM8uGV;ls<4nd?3HavvH&ttWRXaQo_d1*E>$*Vc+yrN7*R z-<<16kwOL@oJs~LB*nb^c~hY1lRc%{u9x5>qBP~xiNfm&VW@Rr`CKy5N?x1poClqZ z^!d9bd0lR9!QKgcXA8Wg(r^?IgRtzu$LkuQT};E>706rgA6~-A6h5uNTLn!y-M>)R zcG8uk^MjB!pp`V#67X^l?u>M}=AjEebCs6+^*Q|GWo_z59Z7p9wl?@;MC{8~o_@!* zd5F*8(_?VM@S8sT&Og6x)bK~cr*NeLJ5fo4&8!OOx63d|z(2VpPsT5+Vo~nS>)_>` z2K3T8_N5fi*t`?h=F&pc{7KzU$Bx5G8=f^}6n@;7iGH;tsUmHuTQ`7j!jeky^m!k< zDoUe?#_o^e8sT5d`U_#^CsZeO3jIY>@>@Bs<2|-AV(ccq92=YIz2AsxQ%oe_n?9VH zaMlysu~~pxTGGXMrsVOjPvKcm2N1u0O-xor4b+;HxKa2wY1;Hw(Fvq`+@FV(Yf1@YUEiM(1HW0`G2U z!fUmtsmg_v{{M?}`B0j!CPFi5c|(>Xk~T^aO>cGwV8k)5*czE11|(CiO|;qltSEMH zXv6*#*38K-`o!>-Ub}aZe`T#i#Pe3@1=Ogklp`S7q29xp3cvcoP03 zBu3;`5UT#6&Uco?`0e>QLHUS6AP*+m$m3ms@?lJ;2E-zYvS|3zsQZ1ziX5~d98)%0 z$Ols6fTo1YA+f@hkmR9qPzLUJSkrIM`w%wpzN*4F4i!htY}1$UMt!l{ojJ+0Ka4Ak z_aF+{9Q-f`PulQH2e!kCZ%G*N(~26W-z4Eu81Aks+54ieIEhbR=-KR!pqPNu7Hn9O z!9SW;7L;L$c^aBJDE$!v7JUcG7OX6b?VnEdES-(O$V2@^KFj`4L7G1+!e$!cV@;Iq zdh-39cf=Cb8u0UT0kX4!B*=L~b!agUpVxKrLoTmS%+s-^wdt!VE#|~7eGyQD<+RjT z{mlTLmBdz-bIOXU#^mSf_Tc+t#_-e0XMK=pP-EPrdDoT}^o z(Kio!N{!Ftl#vz+Di`a$b$Gn1pZ)531*E>$*LhzoPdX!MuHDr%+FPrVFW#@pm$AAg z57C|L@Ut5O{KeS-03ZNKL_t)dxL?%ZM`dl8?<6&9%UH*zL|u*HF6E&UhMomi9r&^- zd8IypP*N<@n`OlceAZPF{;VwtD2;*^>o$DS5Djb@qRvljjYK!8Y!7ZLjn1g27UQOy64a`WCk6E;m>zeT#ov% zeG13tVoVMkKYF4o57?5c%&%=*>EMTTI1NI7px{x*Q9*4@bog$&rQEN8qrl2y7`6}K zXy|WPj?OC_4`InuS3j6YHfc=-mNyWKa6PB&uMcwqXr}>9iY+ZF0eLkiIc(rl_RbVK zAq5g<{SeP;5f1%vdpiIt0oXkdbF&`cZ2l3_<7fbY%2%@W+oYI3U6g@cYJi6VwJz)nq(8PO1Yb0mrB4^}j3 zxjB|!#U4oVcrpPyuLSrXEb7=68v?D20I%fzcaYP}K zJ6(O3J1GH*dp%gO1y1u3Rp=K(h)=~fm+O*!D>>zTC897thQ*i|tr?Q|_FD>4ZCPSeEnp=o&lr{(64Qp?*zj&l zz;I=h$$-IA$&7=y(ibHLa7Gf{WiQC`WGQz+6wyLOZBQ)=UKcqakT_e@~3%q$gSPilLR68;o z4`!Maa%WJ^=zOl*P36=4*9$7hOC8BWfuJ^+(FCrn%kNT-%Rn58z-x=TPcJ+jguIf1 z*hDA5$3t+rD3&MciiJC=DeEU26DW%%v~i3)u}GJEMk?>eN8Fx+m!2}9=0ZB7uJ4Cx z)?!f1!wF(W#tF$HuB{MHX9id2Wi~Hnlz+4`kXNo45vYpzhvAk3@L@`(`rZ`MLwN6& zn6*zE8n@0T1^Rw_0{2?VLb~4sGoX%jHd06YTN5^N@W1w8^+Hh<<+2RGR!9ucYC$m? zzuVVl-wex;|7=gbn-_KH<|HG!Gfn=DI$~jaQ+YrJX?atEL9vMAo@C-jr;?D|DXhkE zLI{M^#xOEqm{(3(u>cEG1rU>cF%WbtyeOYD2q9h!!R6jIGj8^opY|P z>h8)p+>56yLnj{&@ZJL2#S|cjWY2V^|Nr;<{=VPm`8-Zo^1>%Y(eQUm(jpzQX`@<9 zNCUU#Q#bGG9z;Cwu166;m8@K1_9eON7M(JKwEFc~-)n(E3-om09J;Q!jRvFGXAc~d zsC_8`kBz{KDL9Z+nn5%LvrfoZU}jG5J&_fMDCCDXV(L(L_$2}!t%<~=DRpdGn#B#; z=awirT@ZGnrPj@}7{yL<+I6p{%nF{~?~%U7VAXZ(u63k57Cn!_AYkX7O$VYcd@_U- zD{wuc&iRa0?z&7;4!(nS*l(0(t2LnOFPfE8ZoLZ@Yq3d6bdpDmGjp_+(w|t84X;+0$rkAOPkX&~|MF+M2XmeYPM_sks7iAmk7T zIpk0t<$WF)FsOh}S>aYzl7){FaNG?!hcsh_tj2sHFN`F0SlTQ?pI1OpCmOF#6s6bt zY)0SHf=^hAKv`~JmrbL;gEmbCj0QMm)wu0O4aJhK!s4FUfFa%xDDYwSXqZnvo=pd1x`DZ z;T74GX#4R|bq4Ayx>*O>1?aRGmBfCcN6CvM3oPF06N|7`rtu_}-BY>9Z;T zoYFV57y+JvlB1LIXc1s#o5T>Bh32kWXk&eUd z0IWI`%v?@sf!;~MLs5NJ9YJ_!L+q>5op5ANY4R;z7^}e5b&=jPMZJbANr5Ljof_Ho z`6R#|$-`1RT;70*poHr)&C;ASHb{dq(pZC8nqf2p-AU;&Hu8!(iA1z$&Fn$aCH_jd zMo4dUYXP6#h2eH+aluiS_&Ak}7WIM=4z)sXOYiOTqPR?C)QM~;>XaHYN&n<6Ys~2~ zK*%H=NCoI?f@Z|1*$gW}V=625R63(blz0skXmyFLRm@9F9=B`UdT|3*+!FEjnc$%& zxVK+|?~`^oFbqx~+?bNnFqRZ2>|lpJtNZ+LrRK&gTVd3#BDkZd0`1}&EG)sj6}Wd$ zQ9ze>VW?Ms$MQEKdV|Jy>uDJ6&~th-@L5);;;<1$lX4STE704iLbAH2&;8Rm$Q3aL zH${EU8)UYazi^%|=?7+vXqU$@%vV(Un%M}|osxZr>6|>XqkU+dz74Xy*SC`Rb zgpLjsRzIsjAR9{>%Z#`b?U6C5gPYHi+UV0^ux!XcQZg$sfQpP34JODY1flgAVA=v7 zCg4PeIw%$^EJff#E&8aT2AXWn!hv>}SWuKwr%ea6kJh0j4C}QRoVEtIJP!T_r5Ci= zq1OgSdZD_jj`7hEfh^5HSCbM3j*P+bmShfwst%=BmY|f?wdQld)rg>)7b`GllGy#G z5xVL{2p?&Nw|Ar;N~FNw2aG^zMc2kj$n zjjGeT0_0jtu7eLTgXaSZZyc%8`-Xn^lIyfwxa1O3cN}G_|u8mayLrmuXlV$3CVupaSg@$l5a#5{O1FrHHPX=z29aw4MWspiheI;xZ>A!;Pt4J`1!6nT_yxnrK(N6-to7*B%={#RuxOrl#tzjJF2wzgk7D$ zfuKTIhmF$Btk(ic4OKYlk+#iMi@5Q*)xDfpk>L2#qTbJD0=E3pzZFv&hpp^@#S1sA z8ZVyhfzNW|)wOQB4 zHST+PK^h>ZK^w;FRUINsNo`&M8$2)oZyMmYOAswn*$Jv}8uLNULV2Y}I=A(8xF@Lh zQZnj!0vWj30H%s;`E_n>!WU=dYAY5Xx(%N#!xwoKr`4=Py_pi+-v!fIm<+?QnjO70 zq%h@685j;?in`!HMxn`V2EiU*G>I2h&gr>rcK9?c!TS44S{I5Yh-S1wMq?6W$B}q` zZcl{jsahUnB(1_~CI)j`@OD*Nyh;Z8a~hRySJYX~Ss`YGQHvO7k+L?`S*r-~7NgvB zAiPG_s!-)}Sp{qnaM%?9JroccrP-&kW}r!;^0~C~I~^_^#5_)2AFUxV!%9j0uH+tk z9Mx$1kyeceKVH_~d1eSc^T7FK6?CU<8bkSPy1zcWtw^SgEWEuZ7F~Nqofs+*-qay+ zWk(S&4Q_a*N6^#Z5ExT(%Rba9S;fqz@-R0d@bQ8^^L$0W|H`zk4XX`~c{SqSF6sAo zO$jUj_0RO(AJUlNkb=5SkPIWMWo@W!efC6Y7K)%gO{b z7BniqJGdYRnYj%mZF%tjBq?AJ=_C%b+*es#_m6FKw#E?#GYs+xF2UeCfJ}^|^m>Yh>1Sbci z=Wtr24M`;>8lPALmme04BJYm$sDSv{B0L^~t%Ah1m9n%DcC+Z(_c>}Bnvza=SEt2K zxv>q?4mneXLhxw6HsG;3!45+$T1ed%Xh^Dnb6Mfqyae)fahT4*kh#XTi7QXg?u5@L zVLGn-uKsQnW1Bnh$-FG<=`wij(6In*MvV^2WjN>)jIYD1tIlCmH!hu%6J?+sKAzKP z>cgulu(nb)0BIJkCp9`K79blDJw0OwR~24cRJ2NL2NFiOnboLiVNdS9r440*ZB}G~ zuaCjThB`J@gGQ@{svLQR45S^fx(mx2au6;jrCBhTG-67*lywy;X@dh5PN858?rG9u zTkn?mI#Yz%EUa&;3y?ResLAFf^qtrckE>W#akIKEM^rWq*K33 zt4WQPUK|8p1!7i-r9a&O?kirnOd( z)|l+a>|-CS@m7tecUai zeytx39#~$1OB2x2DsIYf0iN6s`vZ!5F(qIoB-^eb1rIgDTv(li8#91GBeuWUwsbk4FG}2dysCxhXFJjv+)Tm)9pJB*9lUCU*0MI8&t?VD`#b}!Vd)^c z8)RQU8-PiNv_p>#s!KEMgPjKH4KC~{9era*1x2MKr`YBu*o>kN?ST(EAJP;E7ysaSG@R52!B3Bl{ z5|rSxWKe+?Y=QF|8ec}!I?Z)>Ay}^y`u=X6`0Yly827vqAD4hZm*~Yow+fD+M?9bI z1~^d%2V3RzNm<12*(>WrKV*lFyow*FAry8bHXo>~!6z?pq=QzcW{3@Q~ENnmK5g{2lK zxL|w>2AgH4Uyn;)k~YKs7##OYBwOc`jXR!$mZ(Nkqc(LhD;5chi@<71F&Ot&)df6P z4|92#nt=hE#-~#`E#M!lilBaTQex*Jr}RwYQ8;FRPb2COj0WILUKw)sI=Np*>*1bu zc;BPZayAEh+i<2+2Z`Vsj8BX9ZZ*S&RT%3AYZ7Lc^gW-ehe`yNTSYW~byWq{P!AmL zQE}52kkGtb64UFLTN`=E3rG9pjBR&|On)IE6v5|tjn5KQSP8>>%Nl$CRtL1zL%R$1 zTUC5E)+xX52Jo^G9xke5?Dt7O*x-<$zN?|e=h_ySvN@@(n@qrGwy#x=|^p@_d?EZ-)_!=eHFPUH~2K$>J5>N-y5(j@)UFfp` zU6%`6aJl1eaRJ+L_&lsJd@=^XHff&L_jKK+@?hT8b3IXngmUbB*U*=E#1_8ShB%QgT{6sO1YzUnx9WWw@G}W-Lf=MJ4Nw^ zY!(IU7Ocv7v!-D_rcEblPVmWUur)Z$#k1Ar*`XgQAIk z5`~wh;ptKMQeKCUl@!d{U@0sCe7dIfGI?Ow4FeX9MC}zB3GQsw#z9sUdC`PAOQt+L z)dII?VB4wO!8=>|d z>4u-`Cl%;%YqV46Q@5lpr6B0{Y8tA1nat%OxK?xdZPvqPNii?=W_ZgAE%g#!+YEAw zrBTk~ZdwJLD~EA-K}@T}t_%uWX0h*98`Vv*l%TI2K6I%VzPMY9LGppusrO?jYqMX- ztLweEqC@13EZkStNbS98jq`&RVPs~u#r8VY1YIsUI`=ih(PoL(lNNRDf9BU=YPwyh zmYX}$m%XzLZ_jGPc>l1rxI1M?BJI>i%Wz+t1nhfp9USg8t4llYg;RYhqGK)?ss~>g z+B)G$zefH03vjB&K5L1Cw*t58HFjN!NaI>6i58#C!4m%y{V$w6`2ZG+h3B7tk)QnJ2lVy^ z_|A8p<6r!X|Ba!c5di-5Pya2MOon~?+Btpt2oFAZj&FVI4}bMZ>gxs9n~TCz``};~ zSPDAf-aiKIUf5g{g?k|ndwCe>QwJlFf={O8!gDw^lIinn^KQ<-BRz1i5vJX+m4%fp zf#Hf3FdKCGrKp=avZ;1-R~F8-z_ZQjRv8Q81zlT%*H#q+5vssKEqdlc9L~q#=S%7= z&91^mS+BLDTO0a~Dd~Rp8R4N(jS<)O;N#oy#hUa&_ctg9tF;x(bh@0K)PuSBJ#@@AFxKD7l=rxu>( zEuEC}Ie|fY>a>|=ZSct+)HR6zQmI98y&AZ7nGi>uN^UePcx$Dvw=@ z;mJm^pZpC9l+6@1a;jF>V^at;YPMBP&+2KpdRcoy2v;1D^llmPHu}A~6nuflhI${-@LZS^t>FX*5jY8*AINx_*0=@J*#)a^7QH)PI5<-;~1E1GPIz1rB;{awimf3lvH z{wNt!u^)2jATnDPMAL6mp?$C(y6noWJK8Cl{M@p*QqiDH7Ozf8$JFLlcl7c7@JJ@k%bxW`)^B9Znu}!0&kA$d*h3 zxs3E_-Z~vbLN<7`8%`gE={yY9X$O994-UD+9JAZu^+|n?39nKTx}wr`S;_+Yp6-OJ z^BT*3JfrnsA)|Z4QiU%M%aL2mO5E>s+^ux}Ge1ngKlv~D4Ky}3^4xHgmsh)3T3Y&l z6jB~mR+jjyzxo~k+uK___SjQwZm#2Uxq0G=r}@rzp3}i?bAy3_AzE8I_~MJt`13#i z0l)p*&#=3@Lnf0ZlgaShbHB^;&;N&C{Qfg%&hXJzR2Jnz0V*zymgY8KGAgQd(JT<6 z*9+@wasnN4NZ`Dafy3Pz^L?dBi*Lay(YT>edI(2BV9B)|*W8(QcZJm9J*2khP+2~SxUmJ%UAci=b{KHLl`SnoBLQs=ezzQ9n^73D2}BtT>bo&i)ukvDbrQ4~dG>4f z@{|AdycYd#pZHgHFGO+*hrY6?Q9`R-?4Wo?8x#hG5@)L7)O6U@oeDIH1J>+OgvN4F zMV-+?ye$BuZBULwr$PFc0W%cc(m+kmsTh6twnk=gz~)wz&`cB_2q<%Gv|Ucit`MBf z!W|>D_!ZE)k&u?F+pMwdT1n8>+p7{)XAE#GAn|d(8K!nLmfB50p;0J_g_4B)@f57C z!taM6+AA2L*#S*Ojl=vtQTjbKp>?UMd&Hj?3ZrU(u_bsj27y*(F@6z+s7ag8ov`p2 z7mR-z001BWNklwBYT!j6%7GS6u!VcxZwN}8< zpyFjYB@y;=RIfE;PzTLoffH-6*D0P^uR+&CzgGh9sdf0;Abfuw&W^&hsPGUEJK@uH zxijBg!FSZI>$=YhWw(q3^&Vvo_IpI{UfTh)S8GdWqs$*Jo4O3U8Q}uj>Y*+I1mqf? z+tC5QlBA^q-fMri`pTI|!IRq3#XYUl@d2s#=BH_jC#=H9Ax>+k88 zi*9K{QCllv@D0c)un-okcV!bE8j${Qc|)DMcpA>ufS6l5@QqF#2+~fC>WUUYm?xLT zp4x7J)p{Wra!K%qbe%LiW6>b~}tVDg@VQ0$;PbAtj4i>LUS|jq1c&C_~N- z-EoMn)-sriFj;dK^*3rUTbPD(2jGiIb*5TFTJ$^0@Z>&t$OlQk_%>haf`~zO>SDbD zUQf2e@}dOn*x`T$Y)R;87gOlqupoZy z)M8|=Du*u|fi4FuJ9N_Z?!Z>7AfYRJ&>ey15Lk1{ySreN&gpzauk}kuB|_h_!WTAp z)Gc1nPD*;TP(U5C`jnVI?%D{`oQEg-6hzwMfN)9ro1|45k^Ux_+!9>xa!ez%fhM@V zsIx`7AWC{ugW!Po)}E)5)!6RZhBoCw8T!+@4xDBQ#+UQ5)7vVr6OkC+WK?mp5s)=J zo!4o3Dh0E77gi$#LK#}r}yb7!kMhJZLU4|W*Nrn;q3+$5Umy!pI39rO?=}f+&`is z+hx(UVWd2jG6;k7aC5?hT8JKH@_v6s82^%PwyfrBXnZXE`*I{W+ z-L~;Cv=*T!sE&A7i|FweCg4Dq;;x2!yx;vn56Rz$;T}}>W zs{w8-3h(gBoW9Fg8iqUI>s?S^g`6GUUxbTB7%r>Bz3zr1qtIf4=0>gMy*1NMV;C%Z zaDG?g?qUW!dHBQ!C&pA%C#s5+dUIL_mf;rY&4~Y2XNBW!(3O^8-s!&EGP+wx{r~d_ z23z>)PhaDo|MP#s%*+%{r;A)J`%8Z&o6X>Ky8h0eWitBNuRI3pG+xMN1sY70)QyU7 zLtm%HAD)n0a-|HcY^Y0cu3NP7exFX=Kb(j52o&t_doGxq)Og5e6Gy4VA)Un^c8W;c z*rdWCvL$-%`53HM^o?c>@b)~!O0v}sy5YhdEifhrJT$Bp_y^bEr5Si>116?n)TxEB zvnnQ0y-g>y3o)49(S|tS(?U_MK*|Go2aNT=(N<{OhQ3yKq6({fD(0?5YMh%QykUZC zG4Q0}fCoPINsRsej0&b)MT_x~PFcmHHBHW;s@#Ebr$$pQyGEv~>pGdf84+dMW`PSc z3W~i^6b@i9C3oB|%C~w4byQB7HD2CNdIOZ>_5+N*T0~Yyijm!I+WO=5}0TghH+GE0kcoCN$sjz_o-b6`l;EfV9yBNm?q*yrF{1^{ zQP{Vl5uw>6=ifW45RXW-4I=iR>k!(YSca8t`0G{pqafVz!r5MFzFyjduQ%)ZiNpn~ zJ=m{;W4Tk9hLt|J=7g4Qp(#4d;7BSgdTCvS@WW#;H?Qu&?>2}9bgV(Ht*LrAyC32{ zIC}{435EQQbW5D?bE|;5VuIl=flklIAnAoOO>iQs?&0(@L{Ki|o&&Bgt2>=YD?`%Z z1XmqwrommO&!JpakzF>(G1lvXn?)I0&W2#xF8wjs7tEQY;XyWV6(&5y5O-ctwFEN z3%}s7z?co5v`b>KmetSK_n@<^$f1ZqoiTeJ&K`jiUTAZ{#ZDc(40)|Zrjne19X5D$ z1m@$?s?N>9>q)s{s|L7l1cvHhu~nMJOh#Siz8nmhGy$kH!FCvaumu$_9B6|NH(`1O zKG+sQ<&@|5uL%UxFj-M{Ufkw$UvK6>!fZed(~`yCzw+>wz~Pg7EzqKcxWO&p z-}oeaa9tycnPoWEtODeNTLKzxrX+0calxrW8hPc7@KF#BjKZA?e0W8RY{CeG71;AD zxYg$rdH9db>H@V?;N$|B3nEwdb!y}?7J~N{lrq1W24h;3=c!dW3w!%DbFXifo@9Or zTt(?eMmJ%*EWS*k2+=k8a27f&8h5-oF0IgL20mJZmv>bdy-NU6jozJh%nF(FTE2gC&$>jH}( zuFZhkCo(uj=xSA0$bdwk`wZYR>N|QpsZ;v0Rb!&5FkIY$_OP^Kojy5eG9|$ci)DRw zr@A$^jHNZo`kTvSI4Eb%qm8f_Rt{XOss*wPd=!RQO*gTbgS_eZm@|spD2q|{nV&5 zWiBf`yDaWi+5HCvZPG%`$FzZYcEOo|x0m1^4_uys6COC%CVk#+ zRHyr|9aIASVh$FvI_QpduDIt8gT46j3T6&3@q(gIgK@Kyp=UGP#IgQ)FPg7&gTRi}F)Q_Fp94#8KOR0M?^VP75W8FcXZ ztpH^0>f)?KB-jm^V9yHSgkqRlSF~9rJz&TPH2LDLbai%1Ew|4Cp?;6RQOnv#VoXDQk1SmC$qgEh?6-h2hgSYEfTQiz0T)zW{{Blcvu`O9awhV=&#;JefEWZ1=TS)zVABPVg z<3~SwnLqiHKjbg}^8fh7f1jF~_=T|iZyqBfV@yokW@%{wfbQ6$fSxw7PPg%$5ye9>~8(M z-3uq$pwj_^joQGbx1@X9-Bu?Dluw9J-4#nMt1n=ZPHinM5%{eo>D0nIkS{}W7l!@X zh%+9&X0ubBpgO;bM1NCFJY81NWkAJBy#pTafVDMvst9#?*o?tF4*2;NnCfaVLPhXd z!DW*B@ZB2{9{;NvdHF&~0{mPN9vz08OK_<}TDnM7(HD>P!wIu2@|GZ^cGOiJ?U$o) zsRA1-631UzgM?MOxptTCi+~?qorj?|`1+t2Uz;X)VgydN!%`4FToN9{wgIzAk^0}h z4h|R9yhl#PeZUjF`V8Vp-AD6#8jo!>L(vYe%quT#KBr)9vk@M(!Gv8z{vX|zx#Pe( zoNR%(8K!@|rgPFc-5S@u;W5IcIjE#02bj*PtAA|)Ch~f(6$do8b5HQX3qK^E&-2ntKjk0)<9~+T zZl|TCjqiTXt z-fSDB%`n-a+n`*w;Q|adNUR)9sE`Tf1slCIC(-JbM}pRS%{n!=)i$iE5yrF7T!e!j zcpxZCxdSn{0$JfBvL$Wkb-=lxPN7y4ycdCykRoE7W&sTw&0;UD8&ps>H;adJU=Owu zuwa0v0`R6&jr!acYOa&J|uG6Q@zuMU^p34hS0g?4!vE;YdXvQD=xAsF8P ze?YFFD>-!lHcYT)1X8s_gGn(Xvk?jAjll1%Du8=pQ$~ims6L{X7I1GV-sG!3=wE?v zhv38<9BhJv-O{C;?S+3}l*{U={qVzG5%o((b!?t4%E6Z|Ni*4Og9obMO^6EL=Y~gW zl>C5G8JtD~ob*B{q|M)ictWWP^tXs_b!-q024K*r&3bYRqFYe6tKXF_3X^bs5hnHo z3kI17-1W-r^IBk^2ln~owDbdqYLP_-FWg>HF%a2;F%$IGtJAoZ7viM5SvswYcVI9H z-;0B{9&WEIF3Odb9_NLppF=DKZ6d#H?fG-R^cVMC*P2{CCJb3{82|vRBJ$fz^*ab9ld z)P_c;6_eKbfI-mCf(Z_e$rb3%!DI%S?C`ZiG(2;O?N1&McH^-XMOIbIaMZ2CvMnyH zV6O{K8)5v8y2GPpz5Y^8(K~k^cMGY%@8h9|9;K_Rhadm=Rl?!iFaGzZfBJ7Jm&-i& z+*kO@SDs~Yafg5NZ~k|_``zDXets5!fA|modrT%1rj@u@9q)#V`l0p2C>2+=1m!j%Y{6$J>-Uh$MU^myb zv5X9=Kx^|Wa^>2R#y{9fHHS#@6eJK(R)@aARkR0ZpNa39>;4zCqpY!v=k z1^%ooxa9V#LQ-e9VYwuxO)dp*tY{ml;|`4W5zRm8ihVO5Buuj#sVy7;hqkSIi?r&d&Z+eOWa9n z3}&=S&|Njk@%h2Bu7~UQX+(3h843>gs|C0)1^>=1;`>HiC-#jcb$tRxjZn|@z)}hB zJ)qABgANe88JG;>88nMzzV3wAw-kQu$iRg#OhlnE2fxz-#RR+@R$&@!gqLqa+NICh zVUt)mu}yV$znDU+F?jkAd|^=WIKCmi*v2+I;DOZuyfO)yfQtK)1%3)lG{IZl3RK?7 z)&P%X_-Rgs&MS8ywFm1l38mNVu-2-g|NU#~3RexV*{t6=zbFUqnn%`qPn|Z~NEB|H z)tMR(%c0fjQkSYF3ti3Xkq}HT> z2YwWR=$;&!Z`EGQU#4Jm1OhSmAALH=nq0bWzS<9e+yrl4(KKLT4Z^$d*C}{=Qk`d? zOQZf4ry#zm5*+owl|lH|MR5DIHa=8_Hzwdm^ST~$RamhI5wQo{Z-U4B;5Ygp7J!f3 zaJ6<_Oq*4R2HlWKz(>2{@YVa_tu>X?Qj3)|M!!a ztO9b9gH%ew-!+TCa%(9V9+4=~Vunvwptnu|xU)t$n1I{6T1<)=u!msJ3=i~b99Bqa zW7*C?UjuwNp&9!7Gujj_E(v$TdF4_ClM0&M>VUcyZSsRwokV*Y;A|r-0`3Ml+6`Bi z;SXD3tr@bL+88zq@YO>aom@#mr&}}jRwo=7RL5z21>W<@W#cq!*-yDW_lR8~JWQ3V2OwQ_LR;lPzHSU7d8Fl&ar0^5F}5?ZZbw!vavpiQqsM+<8a zwiDtAU7S;kW37In`r%0 zWyQ`^D)7W1K{CfyNJd)V`?uhcd!>8pGsD$_v?Zw=T&TeEJUnt7{>TL3s=5-#o8axN z^iP{vxU~xV$KdLsI6@wiuK7_rboFT5^Y$FOmck|w^bl44`#pk^ZsZkp zlCiITiYCE%3>%MzR)%ps#I>x<2DcIN^fDT7iFgTUmbqO#yAX-A(FhH5I^> zf-kCaW>(EGza@Rnp$4rjpDjT{0JhTLc0kai?)F%Z3jS`tKG!>b*xeJ4?CP`vtIv3} z&b1Yw*$5vc;E@iEVgY_OrNHzv1M0vXXn`9Wko7B03j;jf33-zamq*7mGX5f|!gh8B zo;?i1!*G5M4)j5JO%YDl=hW%nPJlNKE8Vc)3^!wPKff@k>&$9}q7@z;f>*u(vmLxv z*z&^GDm3QdQwOwJpsfhszp2=vM~_MuJFy7^9{5V_J>1%qykXI-PT~10km!P?Wkt#i z*DE7%pBY}9l!JC=8NB_PYMAUobmWt8{E*NLquYYLhEs4Tq4Wg8M=rFzxL|V=Wq8B{587ebrNuJP1fQm0 zJ_?m~IBkN_AqY0W_L{<3O?mh&H;i<^2VcO_HhjLM-}$qvuoi$d1B`abg;!@+&Ri)g z5$n4tXt&EPldM3@BnJ&fjjWtmIirjwaK_-VCXFUamRjO{0Di-$Q}Fnl++Coq)0`LD zSD>?1i&`qB#q8mb^mEP=_Eq4ox75*iWedhy;M|Z#vyTU0 zI}ac9z@i=AZt+`-ONzEHt6%8Yb4m@ zl5@2sCXuqq46QjZXC=Cgd!-8+U)H$CSb+04V6RD{*yu2@hfq_aie%_#>=&FkPY(g>!aoTmT{ z&9LBvr@hLQ^g3a3Ph;y_d06sF6kTtUoWN#P$91s?vuQZoB`k{F3cV)X8yV^4DVa2e zJJYD~(fuJMO5gOC{MtnQJFTLs%gUSV9iQ*dt!Om4t;dSRuiaq+1^fljaH)XnS&LVZB5{fAZw z($@>nSr13+!R>`_^~2MHic)#)3jA?9{C*1@?UC%kXoT5)k^uON@JSke^RUL~^);kT zPYWa)l?z!Hmwqc})ittXfPc^_2d&u)>#HJ=-yVnCW%zaguHDpq;Wy|WA8;u4>D(bW zI|J#2a5k}&9Nw0q9MYq0I!rei;HDo2+N7Dc4=;1IJ)x z3Hn=MVn(@UmurzDQ&SMEQ&4Cj3m;vFx8{_JzLJF-yR!KH=#DP@H@38xUoC=RPek5) z8BSJV(J;q?W$Cnco9HyWYnQiQ>~H)PpO)>ti9r{LTBBr3hX4~EmQw%ZEXyLov@bC$#{GL=E^X&P_u3twa6Uk)EFjf(`#(bX)JlU<|a!RAqe!l zwYl%wU>lgN3gB2N!*-i+1P`{ucrDY+QWa)lt_Aidp*g9}8wTYZ4g|$_x>{1?N>9x> zX1D6(zmwPE+~8J7a?T4zyNdHVhx8~mo3aY+xfzmDxujk|_{A!1EcJpiS%>xR=wZ!1Q9oT4q)lK+s%5r=a9q@ZC;EOAScwZBg zEwF3VMmrA7bxJ?<^%1yggWv7}R}Pvj@ZmKH{lC&FA@EVB#zGAZ5Uyp$wbv^fGM9tf z8HMKh&2XX*Vq5x*zl8L9yK#8>hOX&oP@?)j%!o-=F+jRDhMSMU^E>c^O+g)fwOqQx zed4IujVhd72^d;~l_qKH#)8mm5tQ{{n+ouF5pqtAz+cU)cyOCwGAYOC2T6&Vy?$wx z+zxeu=2wL^n6bl&R^6+gFTgudvC=?Jy|M+O&G2Z=iI`3ZThZ-P@N&?o$f1dCMe>|! zl#$|iM(a|Y4_3pXtlP{mv<#Q0VL736h7BX^YX?tEF;KCLat_Dc@Yy;Hbm?_$lwjKq zpKZZA1!mW#|`kqZQavP)GHG) zlLLPodbR};jdTkNx@U$jIRrRujS8>QZifARGA%^Xnhbbzu)HU&)^HunMIc<%J$(0Z zw~+d^ek|?6$%Am|Iy^iIeNBoBh;2bfi#j)nUEOTYjeyS!DW|#;jV}0c8os_y7Ww`* zh$SU-_P4;AS({|WC6vPCJRE9)SQ*as!H*Kq(FosPkPd5WPaTk|M<=#U6Py}@*CPV@ z*{ZPBr1^U=s?*^MQH5h#z0!BoS;3!#*0{n!cZwpXyUhCkTM6iJiW9Wmp)lG_1AHnBIW)b@*^_IsEceuDtXMI^^?7Zm^C=Z=^UDyfX}K&FSTIAxDcnM$AYGNwDqWe& z38w?_=XoI*zPPP1o6!IhF}S!aF?cEu_l&}|qBu;IEcoi+>08P=JnV;`&PkBJx(NFR z_2)J%qJ@8W13uV-{Rfm2X)23*77M`R1JJSr{zh=Q;muW8E5MZ~{0P~yLoINrr>2Fo z!V7s_<8B)aC&7l|e>xlGmb9ec)g`!`6JdI@4niyXT~8i@hQ|M!z4r>T^gQqUekbP~ zy3gsHXQrnoV+I3Y01yBHFpHE{qzWyHDzqbMnRiP%N3cT1izb*L*i9^mI7qJMZ_t&+~u&L6Ij*O(7<; zqk8}M4xnlgy6SJJ5jk&%=j)1G3EJVg0X>He4?K5PB8~OyaL)m2Z3XkTopMOOnFM=O z;jwznYL*CpP$rgy0@lvX)u7d1E1}|+f5k=PYsLP zwA3$-+9tR$B*oW88$Qa4h<+Rq3Y*O;+<7Yj8$0k*hg7e#c32ovGv%m2$ELMfOiK;9 zm4p`~GWPt+v_|pUWq8F5`voxeUS+s#OC78%S=n!%iSdI75x(;{;Vc#Hi=BW_$ zN-C&r4XxW&2Q~nWKytrqus0|k+JD@KTW;;K|7l4n2mV9Ige5fk$gR(`(H7m~up#v8 z_69s^OC<8p1^3eO1Y1ogY2wNCq>%cJzJ|Q8kPA7%N?PAqEFKu;PVx?#ScE z?SZycZo)e~czzTPy)Zp2jusv8=HY`oilwl1;g&}*{k0+355Y>`Z>9#kHV&PT>?bU3 z9a?t_8s*&Yz!j7HZA>*qajezh);6pcAa;uXzQN%y|7q> z#eN}WFsfL}l+=-*L0#g5RDZL~t3bl4jK8%COvP28mTMv+Tbkha%NnEFm4mhs6+*iQ zu%3jX3Bpy;8|y70OanD>?_73RYe9SjhQjcLGq9S5cTF%d0JD>@oP^&6?ruu;b^B0b z6q6ZBakw!o600o$r2_oRIazV+=i%J8*2H(Fgj7Vk${<`UiEom0!tEOTA852UJE$=p z9r3}!P8kWVrnFYdb{Ou#Ng6)wD~pcZuyz8o5pl(KtKcq!FQEI^X=(JC55R0*76dOw zbkH8>wceT?sFomesIcvxp>HLS0b5Ij(Uf0u^BoJ!mf)}Z&*t4lIO@nf+GB?2q9WBN zSK)F@_qf{ADDbyDlDYTF@NrdMY_+QRW`-V&df_9F(4k-4SAciUuh%ot(Rwj-#EDAu zWLc1`!Cn)#j$t4NQQ++wlv+B_jeS(eY|wLaX;KA1wg|^v@dPi={)!i4vZ>j5c0fPt zb18miz$-*^`AF+CV^^bfrlTezlY+&RkmhJu{#+)bxQ^$du$WiO)q@=fTfk_8A02`t z3*TIjN$QuQaA=Slf5RjJL&OZXta8s!WaVCOG{gUvP$7M4PO4tF6D9{F_g`LtA2lIg zS0vQ)mnFm)_Q9bYc1>V+!8^CKK5tCHl{u*9l~ZZ7X)iR{6vV!5l6O_Bs}bd53;uaP zqxyhTREiA~{HGGQT|$HlZJ~*UhDhm!fKnxvThPwJ4+_w+!e|y;2KeZ%nxEG~Qt0jV z;IJxzK&>TY_Z1U7a~9H7cyA3x8ZZ=<5wOJukr7$gJn+lZ^wJP4ZO9toy#t|5V=kEh ze{)Q#&?y@%C!kP-xe*n>rYH6XPYS8u^ea_XAn)^MHK%S=<;!yOzBY&lOOlAUjF3&j zm;K^YeX8?X^a*L{^rU$DunlPsZ0!q)yIB_}rqL8x-0so+->z$Oop;KGesqpUB-4Ym3Wr!!Q^@`135>>%qfg@q50ShOPxRHzbPq#chrC$B*DE7vPdbLAb{axK)GA zG*FO=E}4bBs>bnV2V5D1xAIUotFSOzVa^1fAM1DC*-$}uZ}V3?y_1|wMF$EH@#$-R zFU*GFURecKu_7Onhcg>eyq0>>*6J;pI!>M4hyh?hy5<__KE5KOD)C{``RUrXQ_Ccn}`9 z)dbbOS|9UHWg42yFfyuqz5SdD+=CiKyfEpLDtV#}LmgoeNF&cwjjf_sN>aiAXtzX=N+aSH$0kSGqrQTfvK zT3~g-a$8<#pC;8LIGk|jq39*;C&%%|vevvVy#0Q;r{{BE z^GLZ_EK_*typ(Ydjx_QZj>$pZ>J+-Qu_rg`QcI-fd=}b6iob9Sz~vs49dNA7BO1i67owhU?&tCTJdy2r((!l7qCke*lEb%qQeqz0WHx%I2^XwFOG-A8_a}xVO`qw&kp~V+_yQIc%3+T_ zy9-f>`{iX->w(`U-=|quIANE6 z-78kuFi?1H4rWp+SpQ}XzGH(%Smn}~LZQNE#sH^A;s+TR55t``2_n|FHCDVlCZ6Zx1BJ$4@+xe+Xw*K}vdR*~ zU5CpNFuG)|@>~HDAsDQ|M+fr3+iMD0owvXcaC1{!rG2x^jBg%DbYd`w?|ObjO~Yq9 zFdC40@u01Eoxzay+E7dTPuZ;|?5$N5vAHHVTtc(D7Ku}WNJTn7t&!pTo3P$g(_Uzb z&sXokWDJ_eaBu=Wiv&HD28@ot^*AqHxWfBC`SCx=8Tcf$_z5EQk8^n)u)C$v#-oG? z=X?7K70xyl-uv;I3+%;;8es(>Of(sm5z+zHaEqU zx`X0t;$HptxDB=--1BNvytfG1mv5^`sLyf43@qbi(h!6eFAo1T)S*jEq=k5x=f`gI`h_v0Xr^CGu~L$GOq36mtw2D3<> zojx28AB2Pn9vf6d{Z<=dHF&S5!g^_61>XFi9PZZ(;6eFxr-q?cfky_Rkmsf)t+tpI zsnhKCIc|12GrzScWbE3IGXKnW9gNvJTs(2(!_#Ji z4-zu<+cUr>@VN_oWlVs$&etsa+&_v&#Cso#(SgEO)-_OBj0`}YRpSLxVE9Dg4JQ{+6RVc^a4n)%$&*Yjq zh@D;Wx%MjHu_)Ymd_=`v%%y^@R8oL&&M2qtgb$KADEs=Ts7bEbt92o!^nIkF5t+yq zO|X|$K`=d__#D4Y#=zrAJr7-jEH}z+NL0i}Dw$MhZ1k0_-L3>K1p~}QAvY-3^sW_B zK`DiuK`0Js^jK?2B^U|8Mnz-l&22g0n{7hO(tdcm2)j1eK87DZ0JB*ZC^fr$kgQI4 za3D14n-Q&lw+YVA3cZ}35m%^JRp9v3G4KRo-J+tn)rPfwMdmz6z%C-1-&#|I(7kcU z9>YgXSQrLZ6$%xN6W>_UNZucY9~_D%@X@l+No+8Yf@?uo&8f(Zg(295r)J=FpNQ<~ z3cTcjKt}?E8gS@?fgyNq9Ii)|$9d9$UpEm{3KkpOA!DENDgbvhP zS))LURSEX@bCUbs7}X}&FvDh3>bR91cyU3b&(lFzm;-;mv%R|u7Ys5vJZ;hVYrh0h zhYF($lgjlPP3UzS8g=N>O;_S+x2)u5aI@ZmCkco~7=7(gTFk(@+UoooiDRi~LhpJi!#X5{NAnJyz zV^AB0oCiWqxfGjiLW>?`b!bhwzTNa$JHGjD*xOEuC|K3=|Q2GpYBTl;UCcGIp&gDZVwo5%ZYx> z563}~gP%GFnKGQSNV(VPDl_jVU2u8f|4C~M7%#$g4~&kBz&sw&*znOJoSs!zy)X*i z9#o5RD$9cnmU z%fpvPG!9F&ug z6nunUd#=tV&sC zGfR*Vw8(xV9uOMmK@QyK z?NG?WNf)LLHFmld6Bn}D(3tutsRr))BwReJg2W6g7W6)zIe|tQK6nf(77?{8O`&nQ zIxM(^ChwQ^jJ>lWrKisZ*8)&E5zV1zlEuT-5pme22O)8+*ITja-@J9m`QSw-jGJI= zN){fu6x=u^l-gof6SA=dCoy=KRoqmqAGdU?C@YxvD)OZZcHqO5)^W=QKYa-H6g(Y< z)q?CwQe~LxK(GQ&EePGN^f`Thy#g1Eip?>aB~Ds1!cUguL3mtOBWbe1@+y3m&~y3z zL(wLlT>qq(`N=K#2VP%159vOKXnYVJm^6ZTu%Qkn>`{^R+5{}sRl5FeR1)dm9fcoN z;k;Q7OtIFFTj=WNLlG4Wy$*akCSr854PWZQ{W$z|N8O2KP~4mI(=geQG&f&^bV{&% zwhs3bdc8||b;0(YA_VRnDK^DXhnM40JiT`WPA_zw>f}}$I=Dw2>J-h0Z}I68bf;l! zNnD<6TVsgz1eiSVP|R167u2lgWsjY?y%31ig@6t2Wy)uC_zN;;eet|EEaj& z)VmP0!QDN*zIaHAt>cO~Xya}vy|DCEgc-RbdwXE0!|QYE(g(Xj-pX~DiNSOO+6@@@ zL2&>sIpO1)0+^eC(;y__(ka-@!*wq_Xb3&~?bGnEDlZ&&5K4P6Gzyok;$NK~hF}(U zfQ1ni;XmJnuTE&ZnQgM&0~|e!ZHd2SvjbO z9WY;p&odAT$QEG(6;4ej>=fhMt^rH17Erb`*m4F43Fr2{7q9q@F52*@50p?g(D~3P(FdT zN)Q-OF`jKfU>a81B6@Eh2)QaXU_LINy7wMwU%h+|u6X6mb~6EAAC;=t)sR&~%nlUc z){1Bv?``xWu7cu99XXW35RO6K43kbxB2LOGw0dTk3u@mSHOY~GHw^P8NcPdTYQM?8 z7KEWKxHBrQ+uan*%)*fmu10_wTn)lUYY+*l5UjU_)(v{${sSpbi*j zoYPMbso(IcWY9=p(kila&mfecR+qD~zn^Gs>uKz8&ILcoE6U=>hww~Wek>ugHrE*= z%obt5uZQpDDfrr`h}sRS+;(?daH#;V*yJF-ot9L5)GQ)(GOYvMV$}n-oRYk~+f_&8 zw<=HWR0O`!gHvX4c-mdKbEplV(^GM_aVW2uXB$F@oDFeQ4k}W3xlJ%OE?7BgfQJVV z9}qI;G{}iKKCEtcqpq>p#+F7H81=X2q7aR%2%8uMR|_ufX=LiP3!V-aMH+Rs;OkS8 zg^wJ8rwkX4;hj}@)g*34%m$gHMilF5ZN8Q+OgNz7R+w+$L~$ldCMo0|6*TIy2B78! zZ%74CA_W)gQk)IP;LNyqOG6#~UT?n_G4)`q0KqU+%!-|vv_QNrkE)PckryVD#$NNI zBEXNA^?Q7F9a5>5j1lv7t)+HX=7GZ={lCoyAMMI`QJ~-VzPpVvl(E*`JmpnpHUUk6lgoCxpyZ`_o07*naR3K=D-A;eJ z)qwBq$hh^mu7c<3Q@T$>Eg@GI#$b2?uAGJ2DfxmO=7gH22O#N?QD~>9;@n__3nmz9 zz}!QP?yNQ`Q9UPeFZUZXa;}A?41I51&dw$ad@CyS^QnlGPxTt?b-~?%D+TfPo;xQa z(bEo?Ybge(WrB4N+_yo?3H}azWeRd9aPht(nAXd13wc59Pn>G>WdJpCTP`!DwirkHilh7QMQ^wUlyD2DAzZ{L?cy$Nlgh|aR%qAq73A`5)8C$ z@CBQS{HkAjSEUYFVAQXCx@K2ACRYRM75RTTj1bI0*CTJOr45;RuB2tN@W1Eb?X-&6 z!-5*K^Fe)H0jCUIheHsp!rg5V&2KEI;VCv0ARTJK@4LjKa~kBV{>7-?&#qg@+HVKN zrz&P4l@xuV*@lCZ)Ok0e;=u*YBA?HO;mZ?n;S`+7z-S5X9)Q=c2&=FGwkoo#aJJ#u zx&$Y$3}|fjiW_d!g_iql@})aU35gmxRPb}N0WbC&se!7jM@kL2X^~iAD+$LXjmlqf zsE~5>V1G)y(Saa*eMGT26Pr>BXKf18J~ssihp@G$Fze}lf&JpVVwX;vl+XCdn!=GY zr6=d~lS1k@{HnF#{wDnNF@&u;5a&lEg}$HBX0wxopDe@HmL8tKfF9@@vyh0vm=UP^-hqK0qz~ced3Aao2TTXxLRB1w)06Od-vH9m7B6Pp4|mt~---pk*MPvFI9-M= zY&oR3If}?>+G|ik`@On|=#{qM_RO?+7hju_5#*Rxql}%7kP}A>#v-sVuA-%m3fNO& z7!5+NDc+6K1dIFXvTvJ26dg80peqOIL!-uK$-X2eXoRXAGHHFr4m<3&;Hb~7@OH&> zFd6jOJ555kmW}%Rr4Fp`!jKK_oCwu3x8-IZ?&vV>S>WyoTpfnRG<>Z4G(t*R6n5<}inBLShL_rKe-B;^%4NK1fa$VAU$2)`xO%;Cx30o`>x_gG z5!7|}4Di8`kh84~p*?o5ED2V#8sqsoFgz?3XRNDxoXNoLJ@{*6!;x#La6d9Y!leRs zy$A~@Dp-OB*zBp$`9cm>&&bsE=`r!9f>wz@y1-K*xP2s)!ey0^q+8cBT_|cFcsU3) ztG>Tj7ODASL}ROa8TiX}JueF#u)F1ww$A@a#S{?mO6XLQ>hGfnQ8uNNRFqVEWWZ!*IB8F*|4OG%^cD`7d| zyNx2d-%CjTZ?p;7o{ouQmFY@I;VQv52E^@KI}%6J<$;b%1$?`!oXBiVVv|M(QWX{S zC7|R4-+-tBHy8DcO?Y)bHmX8s$|#QOeg+~{ah(!X*}Ht&hF3@6>3KNUh26BM6hkri z$s*jZ=|1mQ^q#kK5{+yg$RPHoIo&@a+P~gD(0$3);FBZR3c$bU=SP~&@QW1u=QP}3 zhp8s~v<>G1YJT5r>p8hGsf@;>g7(0WA3`>;mtnl1&-@Eb`K{gVs>rU};m2zdPQ+~z zTnzU;&K6Vf;U4^SS&0(y0hk&W;%l?Qaaw4swF94IHToNj>UnWIiHLbpNd2Z?m4+6r zI}C-6HjQo%uEfFagpVFdDsHlZF9N4*@El-s=zCobc=w@5ojZpjYA+|@?Z@!*MJ1g3 zT_R4}eehMIEDu^{ShB(+n-1`<4eC{mtekc@GX(#!slqAU6Cu@X5vS%j38PVYI5my( zQ97=R+`U~AcO%zT4p*)Ok5hVYpC8Htsprs~eqmPJjM0F)sj>kYMfoiS2cTJpgJb#D z?BwAu_cZ!2nBYoOYL_<_rF04y)kW`}sHlt$X_QlTLdgw_#|l7Awc+_my`H6t_*rEy zd~qD!Ho%A5kVu2sC5S!T(Iy(PYRr-9lX-(}g%`VR>L#5AaV@qo8jrSm;zHTY;0#D* zwNe)6EF6)^Va)=2{Sk_<44cz?89=)j~=-SYbzQj>mO)92IZ zs*~+F;NdcCnc!ztnWpCJ^2l0lh`$ny%ERY&T7>(qNo#9sNAm75uVRY|Cb_J)O%V0N z&r*;d(g9($!n0>I68fyqZ`<%IAog+;&c-1Zg16RS!3*y%_s3CwS!i6I6haX)>b`}- zFztb4Nd}(RToP-zs_^V6%!Gv0RE)5a*Jt;$$MD$_Y!_fKAf8((r2Bq*1x}UVFIV7) zW^ndkdq>%L>ozqeFU;V%@Cde>@LEqr;E4&m5v2pPTQFl+^wWd3!~%yRIH&vJ(B1}I3CS)XXA;7;UK1~;(uHBCKHFRYmJ5>F z=UWOnH=E$ZD1v^*Em6nw!*Ib1;|3Uv!87ABi~PcjijZ?bxV;F|R*lg!6|h&7T{#ql z;Gq7t7ZLQ&nMKxaH5Bo);D_($#lagf>HRG9L%Q1rIsIQ8f(e@hD%GygakEkTpR)&t zN3wOm0qHJ8BJhQfcyx`neDB7*8h<|JSK-wN%SC>|rhE0;0K^92qq-E$OLZY?7o+kk zYZ&2;10jGtgZ7|2LV5SyBK7|$t95nNyDSdrJi0M@W z#REM&(Ao3kdQwRJMqh@u#v=}!;OkThc29(A*vyIzDOKU} zoXE4c+v0{qLy$3QGpe@0-&AO2*scv_Y#eMMFnP7vwL5TmT>KZGOWrvytFrV~Pt^I@ z(1!A86Yj4;Xj&WccoVu+INTS)Q>~~wZ`73oH9H7bi!#@{in_Vu68x|Z0gpIJL#{rN z)Fyt-(;ise6G<|WSFTi{0oTVQ8NM(CFNNS65h!FePP}+lw7dt)g5Rws_^qH~Sl(U* zR|xhp+PGFALQUx3U-t5YFIoQLmL7n*zU@o%U{=_ zkSJ=*wpxetgCeZ@{e8ufS=nG!mk_Uf6ONB%o;T1EjPJ5(L*6ed7G|#B`KR47zcV`F znqP(JMBhWI(-T+1&>x?gyWr2seLn65cUAsQ4zrMokV&tr(SprA5!LHmajY&)$yNE| zO%dLm9Z2QiK~Wp%NmZP-r~|$hRxxnKrC5uO0V0D!%7%wU(m(Hn-z%&5ey*u*{;hra zbS;ErNN6xhp)@_H&D?B)*`SK3Tt()trmhYvgG(;&0gH-7k5va!vLH00fK-~JS+G>} zx6aSPUKyS~4MnpE!PDa!0Up;y+C9$0bR1Ta5_o)^)u>}20JpkAi*9d<_ZN1FcR12V z7``~AV)-BiXS0yDsbGzd!zaM_6j;M>w<4GQxlxVqnq6>MsFcHMimVQJ&aC%!wQq7d zIW8f>N&=>Q8q-~lz>CB1@sieHx2i(_unSjab+3I*sV5CxHAQx}e1Qyh{hc9`##@;p zT=i(o)V4szBBFZR1lKGY3lBRajCh>U{WX}N*%v{iLPD%;4)8W*$>Fdnr|qZ-4^QBJ zS;*$JNo46#O+3A^{%aeGC`KulRj78MZ@*$O!R8U{8(}&N4~}8E3h%ARrTdE|Spf`= zDCBv<(dSX-|R9gKasE`z-La8SxP9CK-4xrQoG;#qwAzV68%<2A||)p|H6ppSo}jCQslO zySis>i%{P8m*JaExLFa3RWm`g1}?;PTj`2xHp0`o~?mX5$b$BG@V56-)+~?9L zvQT_-PCqH6e#5V$oLsaM2U0Xe2K3Mzp1@v0`AP;8998;Vy;b9#t$ldB3LidH*VZ*j zdFO1yYzNMnppcP*#BS~rPBW0`3Ry@MwRxJ`lIe!+f`HdE%AbowAaw$>PWUV>!$`MZ z%9&i7MFRQ&QU{x_u@`}eZ@0VRb$e{;$D<=de;}h2*Wq~VGB6E&MU^1=Oi&L>B zUIT<%B4&PbP->EZ4dPKj(j6n5@yo$DRRfDp2kr|oh-6_Xq@pEK1H-6B9UWlW59YcK z)E6gUWn1d8YF%Ah%?J4&1PfrVsv{gWDBk0^u7d2isP3(06~ykgiZB|?%U88=f48iW_-AY+ng$X+>#M$wGKD4keSpd z4Iu|nm&VNl4Lxh7nhK+;5k~DA$y<6tL)S|RBaho)$0RgkyQw0+kXN|#<37r0K?m

    GbLB>csR_@MO~oSzXQ_LC~$60gtUfMQ2TY9_6{AZpP4zImuZ&u$c&dnx4}R?K#aRTn(wQs5aDi9qdbRG2&2WsK*39+Z4hxUXrSBtf``OE2|M{$NGwY!RxajeTQFr8kL-s9 z$c&5AH&^Nh#8=b=4d&q8Jls8kkN4ok8m#zKkna@5Nt_&1lX&X{#va3ZLx}n(%TiL0 z#Pr%1CgjVtFaZ-W81sR(r!}2A(fY92o}AN93aNjb%kB2?=+O>;`lo;Pw_iJd{u00V z#V2fSWw>)^jqiNtU;gdC1%n~J_q`vozMf=hX`es%gZ}}e@$b1^S4UN#+?Y|PKIxGR zy_i*y@o0bC;4_GrJK=(NSG5s*bx<o<@_nV5TJ}>#nhn6!lTD!wl4IkoUt@50X7a;iNLKFs4n~U=jCYI|chqSV_Ty zu12RIGJ4PoHK$OtiAZ08)e?u{q^Kg+=a&UW$1JkH+owad(S|@)NL05gza=mF`-e@) z+aTtF8wT-8Zsp`f<8F&gdeD?tlEomS#?`%km)llA^lnYrf*2Hf?e9s=X7#I^E)<0# zOq(@|%vOXDEFUSZXnI6W>f3n;bTG|4lV}W-vux1gV`c@qrUVYBT4LI+W z-_p4Wn5`>g&E|#+A$dM|9B}cp3J-dUAi8`E^JyukrtC7GwOioQj0o^oMkxO;Ay_;S zX?`XMM`+yEnT%?ZY7N;gJZ$N86j~}$g99*N*Xs&8A?k&> zX(>MU_9fUTb)Z$3O3!6fmf95b20-_}qDT z(W5vVn-zj}jpkGR`;&6Q&8D(4ZGOFeLl>TkgEt57Z%HwFHw~GBgf8(Y%;(|an7D>7 z_PI*ghRkQjJ#hY5^n#(jp=_oCvss8)h1eOaV6ADyb|(v;uRzZ!Uf35$B&7M_wuA29ZsC6{f-Oa#!Py6*; zP~+z(*ONl(ALIJB|Mq{R)A?2Aki}x*d*AyZ-~ax1nVO35umAPG;1B=sf9BMwGXVV2 zAN?uSYL)r<5ng!VGB3UKD!=!8|Lw2;;^mY^I;Fl^u4Yt*)YJ2Vq+M1)$E!#1^L2IZ z1B3FdX@@0cFSO)+5{s)qS>9LjdJ7S$^93nE0zo*t0)tNdzrPB>Cd?VsiA)(_peYyZ zN>jY4VhO^2*efc(>2eHuLkg-L395jpImLVM8iaH`XA>#ZW`%7V)P3+uOxao|CvdR` zF@qvIY*s1RJPmb*VGEpdz$aT;USnOrqmb9D)6i|nsk_ruLAaiWT3ITknGub33?>Du zwv2GEt!y@*LnG4toW=)ND)5snd0Y8CLULA;B4Ur3g(?)RlD7{Gs4zLOXrl>O!~@Is z3HK9Ws9S{GU{J8>Vp&~Zwxz@WAgztc8-SCV2_xITo zNu7Hpp=lknJe!UXYWIv&BfrtEGWM>a?>(@=^`4x|du9dSUOEGw7NpV&_B~3%TRF(* z!P=(V@JRe{#-m+iyeIR_}RWIIQtWp)TcRD5}rwp;-k{$p(&)iV&9(CK?i6 zoEm`hHYmhkI{;UEkm+gcVl_)0RxyIZO=o6K{Hm-E_UdqXLbCNUV;V)?-;(=%t*uPL zgOto9?_`8P?H+1emhbxot>%Tk)Z1DoVUtke^|V62?Isbe^If@&Cu~A{E=E*nEDUN5 zJA1MTFxz0E1T!UBBRDKVuR(`uy``{fw?iYJyj3dJa6mP?^F(o1tIS9{lYg>>jz$Yz*mxL_G!0FEr-ou4nW%s zrj`tSTMfkt#jQf!Znof!I;@rkxh6z$%n4QPjGc>sv`U!r*B`rzCW#VS)h{ zREluef}My)sh_RDY)r^+z@i4H5RfQjzX3Z@Wma0uFk^-L+fetzPF9AS(SY{;GH{fI z|HdL4m(?zO$q#325z3=^p zzx~?v>o@52di>d+{X2TS9(V8F;>|bzif?`EJJ{`ZzVVIU;Sc`czo*%3l1``i(?9)V zzWwe0`qzKC(+4vzxR* z!>0p3&=v~PYzd+_`y?;-grvwb8gvLW+fulUdQ?bUK?iL(EdJA0PNdvIU7cs#r_IZ2 zQ=~|>EX1(d7RM)O0;flCJ7@#jDyg8_>wnLfU0kk;P4N0zM6x}o=%75x!>CK#jw;Zw z=@8s%OO04;ifHQTk7q`LU=2#$XtAnLt{LF;ILxJ>8iikM!Do+Pd>B6MYLk!sGQ2qh zBUN~qgA0Dxvcj1#%pAax8J;SOXguB($}{4Ep+WdOr8p2r1kOfbvrF&eKp$N*$s*zh zNgddS4S4eivH?Y19G^&h5i`TBUHJI8uf#ONen;xMFFECHWOBi`>X7Kj8Q#={c`ug9 zZOPGxy2__ph`_mqj3Ngd@(!!iVJrk!2ZTs=j4~wjHiRZk#HEtE-IJ0jSBKA&U~hrf zq-Sh@54Iah9rz>zFV^Aa8qA%j*d5FYIX-FXeYvc%GdVvbQ_si%T(H4&5%Fxs0x;ZA zvFmn->-B03{63BFeI7~uj~fs%z>GsCv9>l;oQibHw;*QJ{aVgQJs38sF{{+zFsWy# z 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 literal 57910 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

    obB^lrtu=czFl#x^3Ww9G{tt=T{q`4{G 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 H6TklfU^0ji diff --git a/doc/img/MainSpectrum.xcf b/doc/img/MainSpectrum.xcf index 2cb3915b691269b6064f581fa7bff5457af31ffc..e856b9748ad6eb2134066b3d40d09e6af731e49b 100644 GIT binary patch literal 343815 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%0A3i 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 diff --git a/doc/img/Workspace_top.png b/doc/img/Workspace_top.png index 4bfd0177049001e71524d34787dac356abc04502..3b3e823e09b052ae64feb8cb0e74a0d6f359f9bc 100644 GIT binary patch literal 10867 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 diff --git a/doc/img/Workspace_top.xcf b/doc/img/Workspace_top.xcf index 6b6009514e4045ff928fe4e34e0f37b75d5486a1..126038b6ae22fabe1661614d43334c1fea9367b6 100644 GIT binary patch delta 8232 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 OCTal8WBoPF!2bYGd8!5g delta 2971 zcmbuBdu&rx9LH}@+c0Pu>mH@+Ht5`gZP`Yfu}3F$)d8}N+HF8t_Hto^G9B5Xr~{n^ z1sySjwx{Zd5=oQPZ4+)YmMR|ljAVC8@5{x4OHAXVeJ-;vEO8iT2a(;9ae;2oPjp(E5$FMjh*lFH%n9&oO*VDo+*~8yQIgF6a>dL+QSIQj><_Ecxuph{ ze1V^J47 z0UU0 zU@tgG)T~~0K^?^lv_%(B!IL1`7gms8_&agQ9B?7H1gw^{C`&%4K+)quwO5_+W|~a;_b98Aew2hg4mTE#lctojuy;ARf=VM9u0|mm^+&jOxpA%#`DJaAQ38 za;kT)0Xx9~@Fhu$;_0J+XPZ#%Q&Z}1m{`S>R9$gJs;;=L%{AZGK2L30eTlKEn^JkT zi#paG0kJu2r^wf=UU@Y#t3E{a)sw`UZA5Pkh(o}OL%@qez>7nmmYP|VTAT*8V?uec zI@45aX3MWjx@`S?Gh1bqidPj$#jE^sU+-(b!&pN=s&2qV5y+*^filob z)T~~0!!3$8YG=228cz|M_JRk%qu{6D8R8mz^=oJxixR*<0h>^r$xn4Zs=m^lVrIcE zsXVw{Di6lw-u}M#3S+H%rSjI3G-3#fLr@&T_pa6Im9Is+wLa>%?jSf0V!UaK8(nYHyx)opvE>b8Soz8&h#flg*_6^)l^hd&=3Tu{}q zk@`woE^N9eI95Ae{+But>Uc|?9^9V9`fM`)KI9p`LCwQhnK0>-L4E#xI43u4r}mqD zU;yj_w}KdoP9BOm|p5k9kgfNwY!?C!_!fNb%ru$iJDzkYmU zQM8FVX`Fww6;A(C6n`d(ID!O5e^gE5rwa{He)jwiwg>(*V#V^#j3Jr$N$MNN_KRO6 zU$goCSmGE0e%Ag~WS2#$-*vM@3mNl_GC`TxF{ z+{pbh8(H92bmalR)&D@?KlsLTO5}c-<(<|-Ce~!)(kBuAo^E#Wee?4)0f{%jx54*` zn$@pJLi+^}Phtbu0pcP}-~vvB5q}ySROre~Jd3~!Abx=p5%{lyuM-u8doE`(lfs)X MXPQK6T1xqU1Dv+X-~a#s diff --git a/doc/img/Workspaces.png b/doc/img/Workspaces.png index 69529319791698c6bda3e352148ee6de29b91861..a796693e16b50cecdea22cc699f31d627b371827 100644 GIT binary patch literal 54627 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\Yqrjwa9M>Cx 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�&37f#YOG(t-mLW#qxnkH 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 Urw%P|u;7o9yt-VejCt^X0m>|LApigX literal 54229 zcmbTe2Q-)e|39onN~ttNXc8(Vd$uStN>*ms*(-ZgND?ZWP$AhOdu3%MI1{rG#Xd!ps_T8jpo*z}s-MrHBrN!mpwd^CRlrMeuxtUWJ!$zDbJ`hNEG zTP}=hI9={C;FOoUlx0u0+iTB*+8;b0^#k5Xm6jPA%S||mker%7{o;y8@Tv-nzw5|y zj=N8?^_B1;$I1G}Dod=0 zr(r>(lcD6=(xrxk&c&r~M%>aQTUSk^(>Gd4FVBhDw|1A-Qyh__NI>TG$FNl39vLijWQOJVbSl<&v4n99}wZDWYh3 z)5OR~+t`xirn&ZGOKq)_)=w<;PTm%iyr=Md|1lDhlO$rduFKl|p6IrAINZ9qHPaSw zHZ+x7Z1-E&27?>qLEE>#J}eTYeC^t$p*;b6&wo~b8?W;1lX?LQ%l7SiWOha=jD2i+ zfA8k@vj>{)oXj^|Wn5cx>8Y!Ie`EXg-y7rSfgh#P|usb0j#lPi3#Qd`~`vih)Kc$CyA z;<>0#k%CKa)#DB+;v-yH@1GOlHRYu)ro1*!{`1a@I<{+V&d&>?jr>_QQOYyi``^3IIwQ4EWO8y)=f~6* zQ|C>51s=!bc}2-?v&aW)0c~eU6Yq zcdm7yi5&4x5;_5!=}(H3vDyE5T>o!%DR(ZE+eBw;b7P`2v%bIQ?Wvoi-MQSt>(e`) zm2LIx6A}`d_?^BtUrl{Df3iC_&1u6f+;%uPH-|!Q;ct90yKdbH&$b+^e^t+SkWEid zr`R}dtS+<`6!jXLjIVXvW^gPma9nH8x985ap3>>eFscc@>R#?e+w*H(MOF1-jwSPW zTXKw47@yC?l|R`QBkak^YWX4bf_6VQ&%0)^(ytu+ty>dR)EIfYI?ve1sP>y?#b$u^ z?!9}@T)O1_C*MBpaS5sCQn>vjuhm3*5w3y9eDJ4Wk6MK{-9Xtf_0sb4moHvyCs|)# z|M~OhkDi{0+EDHt4G9mge)#ZVH#zyc$OM{SiOLo0D|6dY($X}$vdkP-$70^laPEn0 zR7|wBwYQIK)U(-K8PP65?YqX?PgOESXifJP<;|4S2GB~xYvZ3-{`-A|m6i4MnKLW1 z)ohh@bp?^L8OGgJKc4TnSS;6_^K`7?(+-Q_8s;>CeETJx(pLu+A3l71nR>b3lkL^( z*BY&Hay8+Cp=rAHpPR?pbDpw#9=)jX?{_-Mg@>uGBA!RPtxPF!$HKx4>gwv=48r@b zUAso2oMHHFx5DP;<_CW3{Vqk9I3essr47U_OzuNw52{BmtUP9yNQ=y zd3u&KH&Zb&F>#svsR_8K>^IH*xOnG@6DKYQOC+ddzyH^n$=bObX8q?Au9KRAR6a?i zvM!9DwY!|f|26j?8<$I}Irn5_Zr}oX9GB=mefq?iLpeM=oT}G&nv|0MO<>?LQkUm) z@$xeRKEmt{%chJHLC+fXw3?#CIE;QDHZU+)ob0adFY|oTQy}QeBwgOv_)gtv#Vz(h zj66?br54Mapu|>*eI*s6IoXx%>gHBS{$Tgx>#Hhq&3D=IU6Yg9s2C+4wWmBj%g8u8 znLG7o#$joq<7U3yf?jt{`m9nkvyn$8L>_wEpVLtT`rT^)2) zcfV3%KyU9O2S>+kdTmK`$Jo?lH?1r!OPia0AH+y?FPaDV`5icMLIRJGBs@I4y}do< zNi!p#<>)i{c)9I?ygwu(gu>z#l4q+}lc(#QH>oHoDMh_ckYBrh|Dd$M%+9I8Eg>vt zn8?F{3ZIIOj$mn_m7R(5)%;U=C2x*jwHfkPuvz<;X}dmC@$K8UPgz+(R{wr~vz}7> zA`x7S9T?2<d!SzCGZFw{|{$p*bu)fUm*l_`y z%gtXTHTz3F@I1aZ8BL6j-wnK^N-b!|aptzS)=YnywUGm7x{{Z8bg>?XjI6Aye0j&l@lbly-(cN_q}oB2JyH^==dxj;q4qxns)k&i{SrwttUS;eX!2;rsMx& zc*fb;8SlI!EzNZ0%0Uwo6GJ1TC(U2>q2-hm6>Y;7jUG<;`t?e8i4%WG?C zR{xan7wii|ly_Ibt;~?c;i*rl3vBr(NJJywp%7&Zg!Ec@ zzxbTsy&*4ul*D;+g^8Y?J}W!B(*M@($9?Zijyh~@ELH!rXhqvMk7*lUSf7j7(n1+j z8Q9v|W@Tl)4GOZkU5)K>bhs|8da1`bd1usY$ke=@AX-|2TK@0;8x#Hg#p=!*W%g6~ z?xK;OG%LIfyRzOfML4`4t_>YP9p1awKxdNn@#9IXofLNG^wN@oXbWeX9xo5i+mH}( zOG^&4Lz0tc&fHvaz;)Ku)m8Nb2L~UWGims^ZEJl*nAd*s!ph1@Rhcsy4h0>bAFstQ z+EsCK;bxqxv`f)F#soed9+HN51va~-NiWgJA}#jEnwl<3iRG1*l(!Zmbwtx;eg0*Bk-djxrd2_m zdFlxVjk&pbRkyA&r_tDd*WBxFo8F(~&iPK0!l63Q9S)c;}N-``ngkxifSPY<)#bk3!>sio~sKJqU! zGbCd^RUu<@m_#mo%`B9Lf>b+O+`TC7L^Imex8>Y;`x z2*vuwMlg?=hF5J**LD7rC!b+B%4SiZi=LgFoc!S>)xqF6f6&UxN_<}vYjWP_^}oU> z6b$l`KYzkUM@MNM)}b1&Jo-VBq?#*gXO|-*6)Ea9+Wsx_P?EWY1@F_phuQL`x07&j zaZylTekQ!No>pud6BFYb5>l1$FpX%Q7nL)j?njE&gz+D{efu^^l1ldH7X2bGo#Y=s zu8}BZ7<$CSFvUnml()9}CnQ`*QZMutkJi9Q6893rLgFXG(Sd3=66FUbC+mvc_QonC zt3`Kaq5I{{)$xmOMWRV}`#N!>4Z5N@El2N>Xm5l&E_U#TWU#C$iqMxDDTQhN?&(o~ zLOKDwXJ%$5ZhGX#?%3=cNrPVrMMXu=&)p3m;SR|q_B>7K7Eqkc#zGsf?QE3HxJ6n@ z%H^XM6n~6FI47yRK}ke&JlIJ_c2%c_dZ;en^B7xGo~;EYx3Xrn?L_;Pa&J0=#?l9h zia%H8MxJzLB#OkW)C+HY$oymHiD@qKumJmfAwXLt+uV)8dHo%w@cLT?bw~fVZx2NC z#-RWEvnm`qazq4^DBEV{^z-M>^EOw<6_u6e7TVN(KYXacKe`U!sQ67|*TaVo6I62p zRCBF}-F8*G>aftp{DaLA=S_FXP%Z*>%~UXWym_?Dle}n|%E>&+exBCfeX9Y`q_ICO#V(nI^`${B&+1+05qkRvQ zr5oYM$Ru*=)^U~}^OqAFdY-z!%W|FvV(xl;)%B~`RKeOMbe?q#gw!jpS$8Kz14Hev z|33kI)WdrOvR`s@kA67)e4@cPZr<#vbI-B4mX^sACmee>cl+OscpoW)H@!7oc*H7r z_}K+df&$Pln?}#Ft|^V_6s@m2y!P~DzH}+6(Oi1n<`7Ak)KZW6)cx9b*~1C*sX=Y| zQBi{#Hv^?&ZYIhH2AkM^ntaSW@;lvtq$OTK`q#=SHC^J6`%26~BHR^%r-;#Q*i{SO2?n;&t~~JIluA^YiDO zJ$v@BI2iZmv)qnv?H?NxlaY}L_DsRQ>gYI5OG}Hb^RiN_Sc_dQ_QW${kFWjwRP}2` z{%>1y{m1JrirMDpo;-PynQvrL_WO4TE)Wk|;_>4?LDi8^pWxi|y37aO1R2edEGsL+ zMETgNUoCKWxvr#jD>?D+AJwtGr)+#p3N16qz{5JVA)gxc{GQ(dHk8CLYybYlx6Gm; zAt7P3EBk5~zqM$hyv0nv2w22(;wD2lAJDTsXf-D$a6|+{D3?*HO{t`$KX$M<2K{kf z^Rs<@eJ6w*f^eg+U%ze`UszsUogMU*{wyEAvN96k@4l0tkMB?Fhq&`5VYQzohc`+R z0Dm})yQl%y(VP7PhODZvZRvdk7pZBv3^An!l9DZ+%Ib#U+u{U#Q@?k0sVq+QNc+$W zR?#tUf};YET_ZJ~{vcOt<$TA+NDsZf6xO zyEACKNCA?Q&#oygVA^5p==7zY;y=;}e^7#mT`8IA8uo3mulF- zKJ< zv3vIpfC(hb7#aL=x~TO1Bo|izuB!%Sm3ARiune%4b4H9=SKsLjEoSp+v@ zScZZa7_MU2l(|(wMLuvLUwIhV4Jl!~K?UFRG8d!VlaKFzt(*eSC0I!j|rrNGqpd``ej;3X7Fl zZM5?;Fbra(*?Im@Tt^F)iY+faG_L>Lql6zZYUK`R}vfs4{yjvc=HFA zo1BW#w6A#AuOwCbilO$7j+u5%pT2>CoAL6A?b#Mg?vxCS=g;rv=jZ33*(V|*GB`Mh z8S@07#Va?LA1~Yz5ov9=;&cxi9Mr;Iybe}tYHdBzm2Gi?$MjjCnwoZ1?DyBJ{nhdclX)5>r(pi7|v2amw*QYAlP`F>fpia1La;%05b32zkfp5=>!7< zgVJ}s=iEDGA=m6%qk1>Ax4|2PqkioX@v=*OS0mK>v&Z+H0c zVN*0wK%n^V7lt>+hLygCss)?%vUu8|F$F21ZR{F{n+zE~`_>;$r|4K_s@8Og2Emf`Y3cL=>mqjDLiB?sD znGRL?f8a6OE-WleZ$ELxZsD)CnL;o+)~Qp^9oH7kW(UPkSbm>)^#6Vy@9ll&>A4r6 z4jlU>zRY5}_c}2k@L2iyXk3b(<>uPEJ^#p3EfZ#r_O*gyd<94ja)_4DqSugR+p)GV zej8dFD}{m%pjm?Aw-BT<&S%g7z%Q7)`(X9p`$YOH@QY-6ga=!JYm6{pVR& zJ_9Y@5*20XW8vWm!Z>5@i^B((mX#4Z6RU|jkH*g3NlN;wz3ei23us#tdJCAHhld9Q zr@sFF&k|HrR5!)M$gicBys`yjqmPP?1|*uEpD(Sg^}?cRUZ(aN)Bk+JqW5Q2l}6?J zb9h7F;GnBi%o%?E(Bk47G~C8-uBY)HN9T4&b_4q!o3P)-7mbgLaCn~^7->= zlxG!(Gb8E?3LX2^tMNyY;vUqEHOKh>JAUxsbEU+j8B@8{tx!&b@=9N(U}0yc=!i9- zqnX*+IQds~m6e2Q0aWlXFAyUJH22&&H|S4IDE6WJ{(cRxCPMe5_cC4hb=hXoOt0zHL7Q@}*)PggAXyx(GQ3NWnbR$~f}`85tS$OZSK# zFRRfvZzyn0`^uL_$=b?!2R}eb7!(onY5>Ts;c$Dt^Rl!U%dTJt!u;Xc82W_L|@3y=^3`8 z@~`MMoYZfPBM1V<6&cYG#3KlxWo|yJns|L~ditZ&#;W)hBS!HX(a5G!nLoQybB;0j zR^CFBGwRGJtE=+{%YFa;{q)St_2f2Re}B0%L!%3;?2Zm6=;-dp$_7a=&f7T#UkOS` zO#GIX7T>rxsp!!osEz~`k$_%@PW~<^=v8Fo8Axl|*yI-!Q=W16IQU|$K2TAq@t|Ug z+o1kZ82_O&u_&&}TCw{8Dfc9WTzu>LVy8)!!Jj{W#G5lhLuoPI6}*|BVbFamF9aCR zVk?nzU~JhmiIDyB#=(-y5MnBUmmr35=(i9$6HDLU*qHF*sw(j(hw_vG&VU?lM$4GZ zkKTu%0+fVmdq~lsNOk)3>Cu*0DlZz&en=esSSEqRc1_FiUtT@VTlu1wmJc30h=IZ4 z!+9lG0>bEhMaA9MAfo{D0AZhx03!Q3+d$R|wW$`Y6Gnh80TBo=hw6c(<;y4;B)a1Yf%qCt&S1*xd4szT>$N>&#}Gipya2t0I0pG5_ofhZpgL=o_h<&CADU~Ut7W@hH9 z*`6o7_$iEp=k4u^@KJco`gfv0jy9uU4${)zMe*@Eu5w`NWVUk1`9I}yju_58So_=# zV(}XW;c)DSG@BU>aFx>Wa}%r%=?Pn z^xfj)Tw|(=0|~bhE{qlmOCpTVzoDUled;L~y!9X35$}>#xEEE9JMWAYiPUZ`4^ZNd z4^3h=d$0tqcALL=nI~Jp9YE3Lxq4wJeG*=O%5>R-e^ys(wk4^4*Kd8jxv@qCok3Ai zG5T0tCv(hasToeWx_>*pLVNa7QtJN6vy})J2qrc)gbd&J?{5Hc`ENdM-KSuk_M!9P z2<#B5!-uu5lq=feDPXl))ke_T=LoGY8GKO5wG0@x@-uS^+L4UKIf7^>^YZftW-o_^ z@mhQlKcm-EZ@m8qh=YxdO$rDjdwnP?GxI6HPIw_hNyUF((q}bB7_Q6?LAkX+`(ckr z_AqDJBPF*|({nJXX6p8>ThpkP86W3$7eZsn%M;)-Z2ve#l z&sJ7Dq@m#+ESrbJf^+v6a4)P&pK1Ugec@r_%Gd~*-n91`mvNWka6UE;=E&TSq)_ZP zOfVikK9+$4Zh*U>bzRxkbG!b*YC_w27amSbQ|Qnbedw0)a#E(Im(kVOGMUpDtH2_A z+rMdI@5(RR1VZAGdGG*wjBNSCIC0+0TqD*!@-ZShiG`$<~ zt?ZBmmFot2i(D?-4Lfgb2|*ShT(0Ws_!^sQLtd1!A!%u8GItNq(kkodxx@un#EfYZJ^?U}-oQRm86U;`cYp>R-t0 z7TIlvtQd8zG3{9LOnWo?hU(a{f9J>7O(&&q>^74wY2DBVyR`h<;sH*tW;=8Q*yOek z{m^J*Bm_Lx0U5$uztN@8S2do!W>FRy8R_Bet%JS_2;1=KCfW8KJC0w~p@4o&ka`Gs zcgGH>zLQT=u2wrsxZ8znDuTPA{J>oO^zq{}Q~*Kde}8{+hE|l&whkr5MWXhhQ6HeD zz61UF4oE&a8}<{dyI|#CO8>clJ6Wq+^2usmYRMxgVy&q^`Gi9{KX2J_hOo}frb_%* zCJV_b!Q96%(7@%(%gad<u0KN30aK9bz=3PP*o0Fm zCMLFX*DeAUq9JKh9wa>Y9@->&OEfOSKY76rx(M%gwvy@i6^)%GFZT_=`i;uUQd~Sl zEYY2;IF>M}3QZqD(MOL?Zriqv_;ebYDs<>gu-5&{!OL5+kt3hTD+vxp~; z(>f2S%k@Qy|M?hisDu{l%VtL}sr+=?dn_v8Kgp3pNt30R7DJuKaACK+9{m4^mt1bH zVqCT-WIe7Ic&6@9+!#mZ*Glmv^sg7*hM(@I4edaIx390RW>4|a!+Q164v$L<> zyomyuprs9^*=u#gjVn0WM%H=a=ao|xL{n-T8XDTxJ2qxpJ$z#cjlzV4HJ()dLRI8b;=F^Zs%qnyC2#{lWdAEGR6ylMdjx;4TNrN> zlaP=tXp5^^GQMbHJJ&kC@ZG300CoP>*Y`ovSmA(FK=Z)0scC7IH8m)S zW$&eL{2kW9o6DNm$kNp;}ZvzV2PsjHbtr+qp?hYEPl0;}w&`<0b z0GQuD8zsNkc&* ztWj=mZjd5^p1_1c4}))X`^Jr8x__G1<_RlnYXk6nfPz7?PxA0k=d0C2m#KwXj&WN8 zv<-te#i)}DYe>F(_eVIhm?v-z<_e8$-QJqY-Gr!`lAiAGB+p4ry|s|++&4OU2Pz9V zYGI`M7++Z3>Gmh-O7P6DLF55!&HQj%M@J{PLEf$aB^3jmSC4}RDiaAXm7kyANBQ_h zJr1aN?4SxtnZ~tE(RV#OJ#&W~?i#eGoWdgk1=x=n+tuCu@VnkSneiz3cw%XxPa6~r zt8bxX2+aH$5DbkHUWQ`yzmxPeA*^!+kC3B4e4!3`52%HwpqHW_!4z;$OuPtG4@%w- z4HgOvule9^%nScXO<2qyO?nEB3STxx7N8U$5?v>@>01WWnAN2zACRrB_QkMERGtieBda8Qc_aD@b3cx4;}w0zBf0xosi~Z#8rdeXIYLNO4n~Cz(!4t1fdwF z>C{s5S)NBYVP`LzoH&9d05RPmX?<)Pk6(`*hbn-!a9O|Q zJSb!tR5rh99idG0A~1D4_2T+^N(>e|JHG0Gi)T>Ey1FBKGF`A5FrSFM40UPxUuMrC z28Lvb2Zc_KjFKVHONeNRN`b>etCI-wb@lYcId5&|%{Sd0Du|JfA8U*x1DQ?@<1i;Fgn&oxZ|CnhzgwQ%93B63 z`0%mw$wRbry#6+?$JV}BT-a31j-YL{+Q}faaw`3wz6JBYLnemKE>s>&ajKixcA)EN zJe1HU{o!?|*e@AFJ`;hfSGCjV8PxG5~ATJSuyA zL_G7QkI?EXj8GXB6$w}B1|9SKN0cB)%`_ zV$hAH=^ z<|U;>`6o)%5OE;*nG|jWKtRyY)s5jOJWlbFWmh8_(YHrM+rLPLmcwM{fwv7`^oQ5* z9?Tp*tBE^M2}oT&b=@|AtJBrh1^~bO|L~|Nh?IA0W+WRgV$nOHeNihu%Nk@<95rk&&d0 z4Uf9R%r4w10s&;04gqZHb=abl;}*{l-vA}Wg#gn@8X8_egBkHxP&>S=l=@{EcioFjHSL<_-WTe8~V23uOJia;9b=lz81;X9{Ec36ZgJqSf zQMSkP(#fS}9@q531g}ri<-BEeRo={DF)5DARsx&#CH)SRz5j-5^G1dnbwwDwD;RXw zH3vE_Tkeo7G{WiLaR}-Zm!io zn*lFw8^qN-UcG`bhgzc)S{*%8{^odO3ty6QrZ+$^Am>;0LLqQ^ZSY2uiv$m_#%Hs1Po3uE^e0LJ;`dg-zkh^PZTj@7+?PVRIdxm@srGL!P}>OG0DHe9 z(?mVG-mj{*HjzW6qrLqel>2$d9RKNQQ>kzPF)&|*>GmfjC28sEDm+mRmS8L{DPg{F zVX(@d^}9(=I1n6bA5|}cVoW5EQZ8hc8!b=wVYs5_S5{XOx^Pw&4?=&puV3HB=<9*J zWrktLi_A=JG!${)7`0?3_`wlQt7D(|zexpMF8a)9Qsu`?(09neteB~*| za8q7Ikw3%%i{H`n9It|Zi6=2CA@SPxRO(Kywd zBQ@ahQaWX;YLlNO7fLtbXD~2G!_UxP?{Fj>%2TJVk6krj4<;N&pHG^kDc4=rka0 zj8Y#Izib&m6-Q94y|YunE`g9mpdKMy>dQ`f2QyU%aW8;Eg|RMEVbc_=Ugw-mN`JwmKbS+B}LqyCV|yGy8A7(5@~iYw=vpTIn*&YSK@)9BU%XwJv{tW|tQ){^QQ^ zS}@u#PW0i?X=!UeocE3YiI&R{#Blvk68-k2{%eMaD5Af~^JM@z`jN{=8#vZTx=9Nz z97enfd7{8HT@%4gg)yqHD>v2vQ383dz>cN;*i~?NDD2w5fBzcC7YNlC1Hg}WCJ>ub z`XMD5nLEqIYMUMh6nIyxZ7QZDhK3CgCU_C3{$Yr_s6X`Rs%Z+Svf9J;%hQj+8Hj`^ ztR_Zw_S7btYY^0-!5`<)J3!n6Rv6}{U<=bPNFH#opY2(S$K4*Fqf1;zPW&k`=+Nw7 zl2X>p7#r)esq!n|yLSzJL_eJdo+**2MNi~)TF*+YM`{?ZvzusS_E3TSoKr&c*l16o zFz#7fXaw8ForBC~LY@t5YY3#)2>R4ia^A&bb?%-f-2A?L4|h_MXd z__@Z?iJsPP}S=Eb_IE=W(-Vo1IrrF zNJ%{IVEFO~A&g*A{e_O6Id@Lv_{ntJx#5;Lx%ejj30%*UrYH~gYns|*>9&F}!(c0v zRaNb=x3>pB#a<%J*@=la`T2r4ijg#Ht%jf+rY=+iz~98dCPgk}CbpTa zXqKEy*|YQWT5xFL@kbUby$3?~Jc&gGe-MB0+vPMXt7lZySuA7;#DWV2>RzHKA5we) zBQrBv9KyrMXUh)pi``}LL7_mqe~g@P%HtBnR4sB$J;K979@rm`LxY2+dY=NaUPM9` zArcm0sa{dCIRcMfrp7}YL0G(^vGKlb55T>}h?En8g36aPVF=qyp^$N3U!Mh^ zMEV2qWkf}Sh%h%4#kJM7i!96K8$OBA(!4?QBgZmxW>-n(B-f^ z?;|~xwT=S@-+mH4@ax9<`W-Q``}*(ZW@p=R&;)Qc8Y}D{zGHRq zpYjl`?pYNNvi@s%yvqc-1n8P8r!{H7c){X>K;g#yR}y@De9UFKGfg7Oc4fwi`p`=v z&0)eD78)ALK6qWb$OK(n zEjx1L2;`8*2NP;IgNc#-^WB-Km>2~fa3SYURw%Z?a}Ll z2VPiMC?e^b{Y4z{shzCPFlAD~ivYea@Z|(r*W_a|AGM~ej}VSP$7gq3wLE8PYiBo0 zSY6;yvg009OU2$l=}vg?7jFq|6u$Y*^S9i#rIw7$fXrK6ur7JT>D+Sbqa2&3eR}Kn z|NNalnk|yOeq^nUE-)-SybdA=5lzu7f0G@lJ4p;TUWL__mAZfpeVdbXnMTIj}HZa_##;!$0Q{Fn)khOAP)ONsXOTB)h>RG^! z(f-z5>EgAWVLeITc9>z;{_xtLY?8ll6y~<}i&kZm{AdpHpqS)3@@|9&fQwBOu4;_+NBu_?5CvbBQbWM7+uGc$gYyjHn5v@gD2G;(}!om=!+EBP+ zcXxNNkRu-v>I05D5|9a!iV)2xVpG{ukkUipL6Jj3GBh-#<~BaDwlwA3OW&?0D_aVB z3t3ke_#QSUb5F9bFBKtEwEN`a8igEq0D*8CEWVggV{V?spFq(#{NV2lwKw)Dr>B>f zHe_;Ors(6}KCgUix;%&iuDVQ-sibD%1=htF;kbAluERORRKNk0U3J5x z+{L5geqAw3&f8e)q*yp!MYS)2 z(Uhfj`xkag?n(UhT(Lv!D}2_fEp&rWP>+BoU(P$;FmCvG-K}-jKkS;x(=}(C5vzzK|0VB?!c=CYMSUs8!1fp=<7rFw0~%baC3wCp0eQ3!`CJ^@J2U?^AHUo z&V0dY6M`-w8sHodat?5dXh8a)OGk4h#2tH&mPt@B0+KsnHCZAQ@75Pu`gYec{ep?3 zQ}-eEf;z4>o9P#ZP^3&OJagD5-Ls`32xh_62)g`uJ3`zF!3!HOi)S#KkXXfJGXwBa z-p^q#%-)t^)SmL3Xux<1spdoCNF(5^#7L+~!6_jj`gA2p&>!k68m@;VYOhD83Mts- ztC4GRUu~lgmH1-r^L%zVUM6-o1;rqut@7c#N;$SEd5@x7^^aqNLFp*wSFNqMB_neP zK2l#|<{u5z{!z{(s9_F3v4o(ctgMVu(ELW(P1*NXrHNOT!GFWd>B_=su88%e=S>f+ zm91LTX)^-S9~)ra!0!yOt<~dTiChij0-9y1k4$Ay1s&3b`Fcd^D!xX!P8TT$oJzT0 z-0EV8;|f*DVdmVg<=5^pyz+NrMY6%OFfk#4B@wdubXKBSk};95b3mKI#PG}3jQzZF z^zdO|vppm^mSe9W7X!HASpr&V%;XgzZUvp}VTNJwp{}Y>;s6lr6n;#mGwkf|;REam z+`_;iI643cuyGlT9pmFk@EOc3EY5%eV^%lCK47|}UQh$5sD~qTP^pMhfKgFV4anKz zo&Joav%_mCVT#t1UBt<~ZB-oSAORd@Q>($LJBHD@ced)6+v-5C=ZFr5XyO4H8gVr> zdLrzK9R-gz2-X9k7!q!~^8N%`Iyy-uB^re76I0ef)*<><8x>zNDZssfUECULTZZl5 zKQ$$drgc&OEz;^ZRdDwD_3NaQu>Lp-npEtaN?ARuRRWviyGi$-xPv4KBydcV6cj#= zF-joUN@OF^s;tkcgb6zrGz}%|6~x9qfy(6o{y2uvEUsQ8rukE=zW;S9O$`kmo9UBi zlbP*gcn;vePdYO$Le(Vl-_Vx!1ok*AspDe_H4R6}Vr8=8px+^wGmMbT1@!`z;GFMpf3n9j6Q!o!0Aqav95Xrb9 zu0<5)dAdSSlLxEsx@uZmp+P`+;dYY7U+Ml?CW4q4pBR22(6>$Ud}gVQzk5QwX*Uj1?&BhYlW` z$-Fq#-Lmn>TI|5*8!WxgJy*YMZI$kj>zJ7$4hI4;W=@>t#*t5}sXwa5`zpb8z(Wh0 z+D3=fZ;}n4*!gkcr@WlnaOmyh1qw|~O*RwXDvWtw*gh#JD5xUe#O9OR{D71flhlVF zWo5gv%$R~f1`$8#x?dfCnwwi;Y(RT*o@K7JGI(O!xwXj+GYP*Xj^|`UA2w6619udP zXXTWb4#8W4+vO(rbC@xuiE}sQ-7I!$3c{63l(+9JJP^Oi+N8&UBQx5K3XC{69NB2o z>BICbBLl)q0IzM8Lz#>CVz<4sM3+=n+}Ld~5BKUb9TzYXlyHa{?1=|klXY(NowAqY z{K;lL4$P9ip&_@>&`wp>igni6;m{qE^3-ovUX%GxT;C-16#=l%@*7X9ue*K!eYN{OjkZ{`j{vJe_Gs{v94JZKcC9N1e#UxRMUN=Js2EZ$JuDk&f+=&yS^9qka@ z0GTu5pbujGi8D?L!h%ls7P%0mqg?225a)UUA2|k7?idUbm?@XBwMU4A7dB1S_>i|? zljUP?eWAIyc~QNfpxB^qqNC?+xY~`*jDn*$DB*oU>HGVjAj?at z;{7g^LL$*J+wLWY^Fv?rz8m=d)tkJ!JX_&U%^d;CqHBwEz!>N6-)H{7bFe7oaq{`8 z?5r#dZ_(3dl7%^X8qYkm;6%6utPDLdk^R{yFxV}GFi2hcOq9J}@g3Ca)bdq*uc|rM z62HJR%-iCB-&QeP@|4z(nFdfA#Iw{afFx|IFM! zJjLLwUU--7+Kp!iPR^e`FS2u&^XtWrCN+pR)a|6R5ANo2+wM4DL7%MNM33Pv#2~ zvj^tBIg||Q&le2SXu2d*^YXgG{49<8++;4D6N$L~`O=PKUMFs+fA~&u#=cJQEF&|g zd-C79h(c{{qLsoT@D)&FY7FgLV%3RE#o z-iOeX6*%VN%+zD=jdY{63oskb-5uUxIxKO3-`W_bbP8(1Hw3$L7CviNua@QLzdE3B z?gtc&8jYy$cVy0iNcyNSjyN?r=B_N5PJi@vrh75q!Z0p+hsFQ2Z{>xIbAKr&co{@| zY3gLn2O)|`0 zTH-33ByyIDK9Tj_%4v-U9ZwfedBySf4aF5VKNNXcbVpW$<`ZT5PT@O6XWzZ=j^=dM z_y6!W{8z~ai(AWS$q3)g4{OGCBjRds~TKJ;F zh4mlEQ$v#PM}7etn62Y?8EZ2+00S21oZKRu!}t9N;Vj%>VjMJAv+2_Y#Rw{q&bQl_ z<+Kr3mJ!OGJU8f@I6W8dRrKiOg$v(rgt2C4soWJ)IqqvJ%lBJOaa*arX6#>;)+AN0 zq1obqYKsdr``&)g@4sdG`N&?7&fo%xYobX)d(|@XHoCKt2g)78S~Xd|7M{(2=PU1X zOpCp9qs+i7VPoz3^S^~0|GA%uWvbm!eO#iVfX!za3fK+Xk5SMrx|L-h?>O@>Q{8N+ zN+fh~WO&#+Gm{cAyMT-1aM5pO81Y}z8@-4~O+0UeOsv+Qyy&icdw03`fT8Ju-riSg zCLLeKES1o~6Tgs~ojZRh`{}N@A$?hMxq`*m-=I;+FxO54#+ z8cuy6(qq|<{5zj1rT;xZV|Vuz>-CwB@8k89BQLVG+B2)0?MfN=yZ=;Df@-_f-xpQ$ z+TY`)`=pmy-v~dymfUvLxGS*c!^%~dI6rx~@ z&Q}V|fh-5&XgU6c7Q{D?~531W@ zaCLsZtyj!jW(s%85oF76ahob+kcJ+poROg0l~qYWw|dFWvCJl%+)R%2-JP?+!n31) zd|yo#=JhQ8JD&aiH}9cS>*UV6mY5EZB&cNj1qJDezFB6_tdKe@C>Z8>tp1AWBa{7f zzjb|mHaEzE3y(Ss2amMp*-8&r=ZlwZZZP3-I?Ps{%OwhdbW6dv~B&dTZQ?Vt;{Du9bAd_c1Eu`hDQoL*^W9OD0RxW$PVJX2NfO zafJ6Hh}lj41Je74YzybUrfB}J7mza0%cpn$6)u59P_Q%SEKu9@@V7~_x5 z&QMfRCr8+Ir&o6rSB@xl+-KFA|98)KBvk%WNDOHt`3?HBX#*{SR^bJKQ`s3yE|=Jx zrv%hJDf!#_B6<}?*Ui5)Y+?&O>fM{J_poh;uGfp8C|Pe+51pZ+dm6q?dj{+8UmW?j zpL47sYLA>C2elXDWZ7RHyK}Ewo!#Dju0P+pnzpoZ(O`3@4;us#pE5J}#n)fOr@j)6 zBybnb)!EtEWebq{R{C}P6gT{N=I6qvfPtbfelr7P(oHEZ_r{KqCfz=JDo{-CUbkC$ zK@!J|h4%0AcX=4jm)J;og=sP?fGYlsnjS`ALR<$w?*XF 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 diff --git a/doc/img/Workspaces.xcf b/doc/img/Workspaces.xcf index f55a7928e3ae4a90d84180f8187e07af532fe655..eb17155fbc981eeb5575d8db4b9c40931ce847e7 100644 GIT binary patch literal 129997 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

  • T7&4OD>MJqo5OP|X=>+;I>$7*!m^S!D=uQ;DTI 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 B9l!tp literal 0 HcmV?d00001 diff --git a/doc/img/Features_basic_dialog.png b/doc/img/Features_basic_dialog.png index be775266ad50ec8e3810a0dfafc989e7bc67e331..4a12b9a81512422d6bedbd0b39c240b24da90826 100644 GIT binary patch literal 17698 zcmbt+1yGh<*Y1M~NQrbvi2~9kC@m6Ff+F2rBHe9Jf+8TL2-4Es-3`(p-O}B4*7Lsq zne)y0XZ|@ehnaU?xp}zneeb>YTGzU+YZLfF{uvG?IVJ*uz>$^`S41GLu_6#CWH-^^ zismYj5`6Q)TukhRw3rx;jh(fLxur1z@gUSQR7k4v2}QH+z(;B&{!!6nF~3!#mr6}9 zGhckbOi?i#s$b}5H|zhBT=p0<2Nhj}IP(@;PX&J&=DQj6fTZy7#GtRowaLF}Ti(xm zRaG99xuf6wg!!tw?(t8H$WZyFW=kvObuS6T-F@13oPjZ?T1*iimX9m^Lvo!t;}$(P zd;4-1o}(R$ 0A>KjT*-uTepJKRG7?ylHo*;zWL3Hj}PA6NfEgL_}c2{1gbT5H_ zV6y>wCuNt6pNyacg|gD8UEi!u%w^UdikEa*G4=W8FpHdgnRNZ*-vd{XpW_MrYdBO|CRPW#K zc6D{Eze|@6hyS#$T>p}g6ZWXC{w{}N^15S)^O3{h;Z}@_w}_3rU8>O?tOHmU3>ztR zdjx{*9`ZjFmlP2vxQOl`{agZl=>|Rq6WxPlDJ-}|<{+WwAZGpMjj@#jLd?!s-@(|3 z#@XD#jOLm2^B1abZ;>DnGze*NQ6-l@>yyqNghQvTJH3%#pG!Txnf&B0jp7Y(Y7`w`or-4}wh`zB>4lp3>0L(#mKczx!Gg0AFFmc<@EX z3YY)ux^&S0bv-dz0{Qv>xc;&q`SE|R|9hJU=E&Rpd;LFe|L^txy#0TB?EifGe;@mw zxBq{6?ElAGDe`wk7>l(8lO7lumnTO3S*=*_Dr$TE|_(wB+XxZe*rO?zdl#n%*Y!qnwC~f?hkg0-VlMFPcY@eTUlO`$Z`|TaT71segP(bC zSUL`MS851DnWBpx|B%;W8=;Hp%oxV!a+-V1xp@A=xOJ0A((RNiTan7Vs~+UM5NEKO_&*(#2BD)e%Az6c&;_vYH@@ zQ2t7)eZ0jiydB+bWNI3f>GoxsmzXy<`7k9wsXjU7XJn+@YvNl`7}w=Js=7(>o#$*S zq`LFoV_~DA6Tf0XvkuRR%y{so_fMOO#Y-$zA}w70iY2)TVVA4tU`A!%(2 zBrqS%yL;!6mC^1(JAxD&&29g$Xb6?iFV$Qs`LytXGzIS0!&x5IN9W5TR|$@5!<*q( zi4s8w(uZcZS5{W`Hz&VS3Aw$fbb13X8~E{~K~F3ng|Nq{w&cqaQ+VV<%hB6D8pRg* zE;|N@RE7AS{uJ35p4Y@Y7K3^#{V8(s!jwLvF6a~IYlecZ9 zbVh9dO$ssKApKZWb5gF96{P5-KR#ORe)P0dq}DS=b84ykJD=D2W9JE5^r@+-^@;NE z5aH8+qN0Zg7Gf+hKkNj<*3Y9GPAXYyZ%a!bIZry0FfuXwCil8xX&-=rP z&dX5bYW?(j!$fE7N3DRn`U1r4@6S3I8%=12Os<*o(yyyk4gFCZ($JtuOX;!XeU&7M zb5sRWQ@J)+Jfaii?~i_Vw8bDG@Eyi8P~+*IQ@%v*hfVfw$bycB#)s3er7TKTPVWBw z`yXH}u3ftp5E$6LcT#RFve(7EzdD5F10$ZBpU1$(We^mM{UPA2Q#8e(5SMH;=R7b3AxYNJPrsBzR$t&1?~rv+x+Wg(qp>?snF8p(R{-jsHn4B zTMAB2yw5_YP-)Ud$&TW9{B%v%P`=@_oXSmp`FP=OcuUzd`5#g;GIQYy!YFoD0*)*Fs6K;(N^S?M zzV&)-uXJqJ!j&kvO}g5gPhTkrDX(+cP20~-%V;pY=%2HBI)8_{Rqx@Vdg`I0=i>?k zZYnAhEjALJ1GoPDZ#k}!e8=-xF7AxB=`vN-)hCx{TYGZf`Rq;Nh`+XnGlYkQy{oMi z5f&C6%+`#uoo~S-C6(%oWF4#V6sWJShgbRX<45=L&U|1=!2+!DmV>nsl8TEHIzuMXN)aLurj9OBGBL7WoI|Zqs>>gLl7(NZo{uVy3-Pj zqjk&EJp>& z+2YThg-1tA>FLoFn)cGepd0;h_x5^4_F#nbHjgJ5cp$?MS)@P!Z8ERt)?WUZ^lz~! zu24`s;j0yqvZ}WJ{qt>Q7KyG$DE!CS>rT#Nv7l4(VBFp-Sc@&n{f{rh#>5*&vy+L% z%j0?6qYd87p$?o!6EFUqJb$@P`_a@Rszz4@)FF#0|LV-CdgC_p=jp_MzWu#dYuFpmn(XhN z@pa<*han3`-3b2=AM)%LWdwzUR)#W3m{qcVLN2Y^Zoq=%sgkYHtRQmn0g{b%%?UjM z_FgQ%Bc{*UW@Sk2)x}nk`G8-`r#l3Ac%tIsH(*1r45S&Bj-#}7buA30zk*s-F|xcq zR=8=$M#sZ5+0%4v4n9u zD!`7W|H1EQ=Nw!8J4d_Hrd$hVjLQ4m7M5_aMu?D*uxV$$HK?kl(s|42>I^cOtGq?` zHy-=a3_?eDJB2m`qySQEKfzM(o*x3wq@-@b#&RzGU1U97Q+H6gjqyD($J&dHXl=1G z>ff-*bB$W7dwYG;bE?wW+G%}M6e78F!mg8Ogn`j>Z8*Er*-dmhp+GAH>RQB!+XHh( zeKb>gch#z9YPNgyxYV&@F{D~!Fv=c@T!W3vh-g-xUn5I82hzLF-4xo8c~-Z&I-+MZ989_LjMAW}Z|yZFHO-tOW)IB|-9+ixWIaT$*m65O4~%uDyr^ zjKgivbQ3-#qo{~~usW0kfWu?AoxyUfAf;{my3O{CC<69H&+cNUw49t#S2QQo;^=J6 z(*6QtSwIL~=O=sc=v$YUmvlA%mef~JQq@MQI*AAf8gWI=>n5G2yJ61CH%i8D-n{uB z6e2_9gfVC0eoAu3tA0h5_Q3itX-5|lcPwn)-lGgh;UKkrtW7F(!7=vgE_2{~EqikP zfwLi(JF&)RrNj@1y%&}b$<6;1rJr@UL3U)(C<=gAHRK?D?6&`az4n}|SLlcWVDa(U z*#|%Q`!yYSIg!ibd)XSrb8xN6?4ccCu1GfRj$krwwhYyB@7j~4?~t7AuP%-T z%4`i+VJQI!J@z=Z30<8#=?uQ}==0ydW~YZ6vU^M2wvQ>AnwyaT#MyK((F(T?Uh|3n zEy^;uNjktBm`A7#B=76%puEaJSRoTJ4*5g9*#_Sm=;-z^Lc(LuDjSWL!;eHH0v%s= zvRU52`mQ?aq;2u1%y!;}|A)iY6s5Jb^-|9dSqRwjt(vp8j*g0~)BDErB>-<1j`CPK z+dO-Hy;c9r^S)q|lsFxqzH}0ek8I%M>|0DnH8M5+66Zi(`wgdlMX!-0#L!`Eznd^r zk${{WRywRwHzGcsS~im9F4VN**CSk(qa3KxGD@P2=)Y1@-f_(7ZS13O?`Qk^mx_!X zVq3PS4aQ4|%S>*RPWQm@pD8I3@_zpWm4<+nG+@YLF)2h?(Fd9vtu7;*xwe4jP`vMZ zrTD~njMw}6`j8P5$*Ku>M9jc|7V7N$fP%;(gaqanDU^)$jk|bwp&3t0{X58#`hI;j zhbAXNEu}(8DYowhvImmxRDMY$d5sVcFTG>%KnboeeUNc<{{`XvCkZ}G0^lc|a6BOr z*;J+9?!tT<5YVRU;pxe|f4Y3h?`oX<&rkkslN1#l+<$#l)5NANl)r=eR6O}z`$L_} zCn+_Ym3b6e$uvyn>3R`0@^^0O3`o4ajhB9HD%~kKz@m@zRRKqdh_mvr{)oOV@tgdA zdZW;H5=uc!humWH%~RAe+3I(+ZP)orccMc(<^Hrvuh!q;z~?kd?roJ$}75E7s1 z=)?RFAcgv3{aKtqNax;idzl*r|DrDa+MhCc%-Qcci;~>phf3js)4z=VyKR@~RZojA~)t#xU*DK9UzjF%cSP?s| zcj+q@I}< z>t{-cvj?|n6?0-f9^ZSs%s>s>aNgSc>`q`{VA1Q5&)eHJT}!p{jevHc+0QfXBv#4M zlD4xexTeh(9%*&;VOS`^>-Ei>sISSf4+f?n#4$S;?G~jqzCAQ==zcUE+-}rh)Zu!w zan@$WfuEE7Q?wjCbT?x0!k%VRReXge-E+`Nq0)=eEq=bWZA4v`KsShaqYxu(V5ZcQ z!;9GR{JC_e?z=nnVy~1cCB%~zUyl{g0scZ585`q~k+mm?`R9#q0Cs#)>*aZQSXw)~ zvGE-C9eoM) zZ*oLlT2$}#2u!%GWg|9qCdgh_@?%ur{Oeqlg-KqjZK|vu)g&7*Z*@F>X23FQj&eNVzA3oe@38B_Ch<7bF?uq7n zFR)cb;NOU{Fj4v`ICueC1jFeXPXI6I-dC|b=JeqhX#*>t*P=NMNx4nr680p-nG9uv z($W})GF6?gHXQiJDjb==^S-fD*xT7jkO(5irWU3MYJLiH`S(vTGZqP(1N$p-R@N1= zJWt%L$y)-4Pbc`wu5P*Pv;88wc@K@z{6uzF+U2lH)Wz-dgYXc~m6WK7E3b{>k-RZW zxoD1hp=@YQlGD>8THCEhr$!b^tBMtRE-tQ{7#OqA)99Zc?;!gC zC>^)b2Gp%;0-%?f`SfTCVaFd?eZJe-^^GS;)2iGTQe#Ebc*&bP^Q|GK?O}AA=Jrcn zj1HGr>{UPS{RmL%NM1ae_HOO$#7Y|oY@DUJfB#0|n4F4Aj~E&B%a+qMKj6pl(DKeh zf(1N^>|&r#V=Ycj=^P+Y&?LbSp4vVZx{a^oWsvc?3^f{16D(XJU~FWw&8A)PJ5OH> z8vI?BkwQ}iBw!a56qJ&corl5B%+$YcKFuO#)!=yZ=OMfuBH+`f#dd~x2j}19kE~)V zm62^ez~pP!ubcn=iDFV119fw^i9q2+sg-trf4|H5POG$nf+@UUSG-949qTGQZ*OnK zSFgx~Jstyd(!F;AEmsv(&dt=$<{%Po{e};;bTa1V=2Zv7nu37hA6iXB1GrA#4G4&B zZEZz|vI3I+_D+l=} zXs~aSINLsN@90Q;J(3el&hrt1JUk|*{R0Mp%VzoVUjXc0US9vS-0SO>e@d-;#c+kh zx2CH3+z%~lO7&9Zf5>=v2tJzj;0BzCwJ~10ywgg}?ncK&&Pj?5OoB2gUMQA^hE>&$ zBz(35#+I&LnCk8(n!CU6aEp>(Tt$T#CKWiYg&1AphU+s^Y+t@&Zg~NwRzyA>;(ZQ) zEy8j%4<&O*2BtYkxd%D}Yv>A|0reFv;w}906W;MxHGl<&C$Egg3XGwH+(}dcXjFk*aHuLVF)>{+-2TFzr-bH{o}QkrCyP-) z%gn(@GYFyj-e(=kP;Tt)4FTo=Age=rGM)4O_{8w0#ZX3sc|SfCLc|NR*rHCLQ19$= zm*CYTRmC+xiw?lDDa2z_Q&Yct_pVN->SHy`zBAv5jyr48vk;TZ^OFgu@i!!_@#7pCSRk7ObK+qWV#tp6-iX&qGJ;W4s9hhq!v!KW5l+*<2=wTaf<0*c&4KNF7SH^1&&v+nW1DC|^ai73)~ zTUPcsl3lk4a*_Q`*QWMIB}p3_4(KxGDz|Fv6~;@gsgYR^AYK&D>grB zHaCY&n5QT5nSJH<%O91N;G0QGTgc4Yk&)5Sq>%l9NrQ5S9Qspg!^4Av6u{b$p!G_} z`CPX3(uIY-uGO$@&&u9@fz#r3W2=YRn{#L*ffmyEW~)PQsoQ}C5~pwcDMq=8g=N_A z0ps}elsNM|kbnUR-;T~;B1yT7Zq{C1R-WYbQK;w@X1*)(~h+ zY)XE@HrI3a(L8;@?h`b3*~o)GP70Ltlv{>oMMZij>+zs{gr{H z*4C)g?1Y4)`sLiW@wmZWwI1{=G8!Mvpe$lj2}VnY(=W!ixtD~ z$aII-vdzA7NpWs&?(ax0HN?Luv`OpJwedW!hk<3$rZucTKi}&;KdQYlK0DlSadTT5 zXWH-ez6^)1r4Y)h*X4=npOQD78OoVO^MFBrrYu59$kMH)${hL#L|KT)<>SdpX9gM? z8ifL6{pWXDZv$F$=iq>jnK=l+tsP`y#f^d8RXvxj=2&l$M|i@QCFd%8 z_6lqSi8#yIVGB~LWKdt7uH`O|KMj4oGuOVRAAgWaivq?z$hEVR-M>8UG4q| zNI|o=d%7Mw5=XVVu8R@ZmQe)H)(c%Pr=T}`l`6+r)HOP)7U#B_PMiJuWz_Yuot8Vf zW;Fp)#`#_sZr^w+{Q$krFD^Di#F|gn2%+DipkZajf!zRvMiZ3CkY)pv8DPN;VRNNS zojB=P=eaCbnN-K^@lVIS|Gm2P58uy}y{9P&43PbD59&cK=SGLhF{8bnmVWltz_u;+y)n_iI&ZjIth|Q z(NXY1Kh~|h>tbQJxAm(zPVG8saXCa!SX|ua+FI{GoLeNfZ$EK!;|EL>b#R$_pf}V? zM-r5Jz+~T#uf)Dg=TXEMH2dg`Yr|HAw&D3dLn+t`4id^o6CPKw$@FhGLIN-(K?rJotqwrfqKL`HP=5O#jm9|D_6K1L4pU{XVm~b%Q?ZKS zU*VU?<^#bMbmAZ`)7?@hd^8Qs0S2xLMMRO(i~P5afjEN*H)xbbgngeNH6p2#(tFvA zdH9Ah_gfa&b2|9f`PVg9=ChJhzN*Pf^o#4m?QE*p4Y#v$eR8+!9<>m3)_=oH42#56 zSxH)&qvTOVK2Y!DVOyaV4l3?41B?(ADA|;!xmOjAYyav;TF)hYpTLs;r~33is!abc z;@1DHGBJ`Df_kRd>^aL+T|mwKpav;KC3EEH%H~>%Rmza&9naq;(6(Fj<@NpPrkt2_ zplo(;QE--h42B}6<_ZkJI2?>pgUM7P>T69H&kJL=9%_wvY#z!S=GNa58P)q$&6Sej1Ecvron%ys zvO<|V_J`7ILDWX64f)gh?JlFWk~{yXdMUGj*$4bjdOP#pS(`RL*fPG-b6F6|yK&Cb z{phxagD=bXu*im|NAho;UaDl*os6k4h)meOcnj+fsW|a!jSoZC4m&Fzqn71E-s{=n zXEe-WJLT8R=|*uSVSrL}CB`HU zE30(2e5t_@6r$lH@|wnFug0pSRI!}M@LxE;YRlzt!+B_qYoZ_}gzm1=xbIJxWMFY> zhKNHkk6Yt!x8kXGxPI# z@%Q0P$!w+IkBn8}B5WiSI#juOgr)pUH)s`y9#h_SB(2fK%`AOeOTE8-6jN7~V(vo-jYV?pbn&r3aZ3h>`w`nw1H?cv*=8T)y1l(qHr@s%rQt5e9 zz)XWolbEG;0!1D)TSdOF^|th*1+Qu7CXtpK-(NiU?j=87jj2t2#_BuM@_RwDEGIK8 zlGfr6^Ue2tPbNMqq0V#tJ^T{T7C-d^UEOKB_yMsMr^NBbohVsbCQOlEV+|zFQo}v% z@riJe+aPMI`7PfMy8&(@tkO@qqE5s`>zKIS*FIG)1b-GVqI&odVamI%QX83QCj1L6 z&WpZhqmxDx7e||Tk_tbR$#jE3&$(`OV|&l)*9va6)w|0{`=za6M2h!VB}EDAJ9%&a zasQ>No})8dH^Pd52CFVIvZE&c;sWFbVx;vJF>_~i*s|HF;x?WAwDR#K)d2bv6a1p; zJLgv(H~8s@X`|{%6c~jiUSwvHTsl@shjh;^aPcfMB7ZyaQEL6~OB+w_U$eN9*Y77U z1P|=yGLom!>FNrFm)^@ElnvkHsYBQb456qZ88TPiRr)qAmXMGD*;v&b7@&n~fWvbC z59h<=_)P0{kf_nHc~Z63El7Bf&`7opZ&i^SG1Dl2-S|be#-8f2jI(Nx@;p3aDzH=Y ze|rJAt6UdEvFt}&*N#=&cAh57(6Poi{;}Eq#`dRk@d-hlfCZZlPC@$cp9~)*^(j<( z);Hgb`swEMB2HDln)EA%hIp5^24yOWF>m`S>yx1VTa{xzdg?D`C#MwLlootDJtlT4 z8JRGs@hK38rOe)8Se;wl^y?#4r#SsFmtm%>mVw-ol ztC$i-TwCaWQ;Sc-y^6&#Ud^T7u0?M5+NV>sF;qdR{P0LIh6~aQb-k3J<|c-6CTUsU z$Cui3F19>MAFwhz%OAYxw`0`(I(O_y-kz@r73nd>t%AZK=~6`swJsAmdIG3O$`xK; z9VnPyQFLlrVpimRyy#cXS)KB=t&9!2M#n0F=S^XdD4SFDJN5KOu*}yy5XVD9Llg9r zXn{B9Ls!Oz-(P5%wTO@HG^FU1wZxpWF%FN6RNq|HaShR2l+rLr)K@HpJUYk((&&fA z**c42gNGKQKRY6WHCQ5p^BDuMOv|&}7lo`+q)jo(R~=-f&PobK zLU!|T7I{7|x?WXi$q%L5Ev+7L>|*K{eDFlK9Wr)?!Xhi<`~~8=1t9f$+?&bAty9ENsZqrwD+&Vs+@LxQMDGkv_^2|k zd3%rFz{}gk#EGR1?GMThRsQbY7<_|4iS|{d{~8}F6h&G^POS@J)+=|M%OU(FL9E)Y zw~%vJ8S~CBVo!9)Ta#D>L4wWFss4u4AY!HfQI)(IZvpf@kqh1J-tl}$Wq8eJN`V5q z#SVP!c$DjCw}HI}fiuF?4HTSd?<*nT!;nxIc_Rd{*!KH>`vh}Acg}~7-J=s18`}t) zi}gRnL6ibc+aSv8sF+rUF`@s|u5<$0-dDZQB>gFIA}Ay*+7(|v-FZ}C(oIg~bv#Sk zjfIPAdbmD@)WdP^@NO@Cx1Ixq!3QW5>*@nyXeABiTRwU0G?NSh8tf`bH|hRX1;`b7 zp9p5vUFejzbG&Kk%B!J#Oysf8}zuJUtYDTxt`r7BuUOggsb}bKlg5 zZpd=7A_C-=Ohw|l)>i)*ZqwbtR|@&I^O8U(s#Q9%f+)WX{l){EDOgmOt?DD_ywO7h zcW*+U#iEwy0}I6IxT@rbb4L=IL?r2?uC9)xz(69|t-Frb7&7CiVD-_twHe{CQ0t?d*|88&E5(Lila3R-e?b?pNgZpw9;a@|P)m@mUWMdr;l8m#Gdh{U9% za?nWm9DZ3`3^qZ-NXDV}Bs2;aIAK^>*c8`nmb%Cex2Ai6X4L#E=mT*EJRvy(x+%>P z%XV&Sh*%?IW0X9rNq(S~R7`Bm`jaufKeDFh=3Z9K0RgyGmc6pbPkke{Dlb@=D?eLA|nT5oTpnWaW^pgm0hkvfWH`6-e{18Stvs zyaT2h*ka9H7E=yV7ob&L*0M{1-5`m01spX{O|2o+&I$q!e_!M)DAFn4sspZVPkW?e z6U+hZ!$kX`_ntj=-Az9GIR$(rvQLIJ!}@`q=d_^#dt4Yw8l(ddV_BhL9Tv(34ivIy zr6!gAa)d83eRVMBa)2U=lpy-d?lUq59v{0PfLMc$<_2;T!(y;QC>pGf<^wSY{MoYb zFyUZ<8nLvhgTqw482eG>3kr}vO%~chgQ@UGFsS^A9TD;<;*KpW-A#DnFiC41?+Z_P08p-{Au=9;fEYX zZsoWJn@I!U*=U9BK}aYLs$#T&Gn>3=&y08w341EM6sRa*KN2mq;=8^vl%*aH7J+<& zW=xeo^DPjtA(WoUa0{E9Q;8V?qGKg;ZzrO4In^I5^lqq(?__QOUUW^7M7Sd=W4LuxPtLrsaVjgx7Mku9e!m z3#19mALx9gyGVx_khtFGJLuq-w4GK27T6lpJbf@`h)YP=?sY#x(!pFojSKDJNHz&3 zf9XB0u-;-vMAhkP=3s_0mQUqoMG$cPp>c7y>PB?EyID&o-l_7f>_tsG-{s~mY^uRR zhBxq{oqMaJ`S*Z*GZ;vH4gxr~57a=tVh!{05>^Zkgny+)2~t} zOFMp@ADHu12rU;I;Csa0JkU^$)}yDRL*ese_Yo|mhsGY5Y6OdBNifufXdrC91F@42 zv{hJm_|D#55(oycix+ay7}(i|b9KG@Oc6+48=L%LfcM3k?tcmRNCkl~Jyz>z(wQ3R zV*}F1wMGi$OxM#A@YCRKdbhT;a6tj!s>TUP)R#yAEBhCcABu|n>Vu(63ATLZ(@1q3;t{St>^ z3wBjiRkWZh7nos;ngZ~Uj6CcDNPT|YCiD5NRP&I^7D0Pv4i~Kgrxn;#^qM~sJ&{L0 zhV?|gz&22Ll)&vn^=lze5DaAyy@iR%L~=$aBy^F8ry;vr$zjkG;Gwy&S#xH^@9_7_ zvf4687N0wRI8R9fg5ZpigV2UibPWA1Nuhj1$7Klb~|TxAcBPAy)XHCMXo$*&^h6CtZGh7 zBtKKA>yeeK8iR;G^S3YQiF~}@oKqWfx?S>LB;5${GFszVDezXO&;&=j!lB-Xi|FxI zl&)8T>)sMfF>X|+?~sLYU!nvUFvxr$M0Ow?c^0 zzSsxpK`}%ADhODXmMl;gdhBgOnORuYGq$4O{W(02w*?2jJ+k(CX?oiKc$iJMCKfXP za-u6zV*vNkQr|P`MPP18KS_d4ubQ*EH}p|c!s*~uPt7>q z)s5rg%fmDS$+g|dUc3EU3>Ss3R0&X{K615=xI`}Y363W0aUmVp$9p(~^X17C#G^?E zf-if9YKASrNV$!~(+^0qAod!Y>9gR#z`Mf2RPXCwEZ8AgYb~t_iSE5j)!d~>O)Exc zg?txx_dQ@9!R5qGIEtHS+!<+PWOSQ=Al%dqQr2V$K9aix9U}y0k&K<49T1qy=}HCasnO8RF{99`+}Qp~Jg&fSXn3z#cZ= zbAW1-A!+`;zMT3EH^7kq(hfb?Wt*Cs^q@a!ou7!rshkA5@=7@ySraVG%kd)-W?ooD zHG6ZeW9oS(8ot=;fcbhrY*DXr$rnfxzM?t-FAf>M!viP>PR?~*5bVh93KO|WwE-ek z_oFqZ#}r6$5sZ!HxDL=Mo5Ds1EJKtY3;COUdPf-_A0HYKkuul`Y^w_z#CExs-6q^1 zq%B3L>ih#Tf%YBD)r}u3G+kEr+MFmydZZCxC|geaK=&xd6*BV)z(uS&0KhSjG$7wI zgXn^aMZ67YQpavmCL?xYoB+B-fFL`-XAi*K1WVqNOzIOFmWyK3W;#Im8-I1q0C<3T z-)ZWE_TD`*0Vfv7%=YogQJ}v-f|GP|a!S~v^4x#f@|m&|uxCiUo*pC$sJ;Ble?Xi) z2AQA{S}QPqfj+UgPc6*IV+2gHIF!R2q0@nEFNd1tZwx_Zb+toj0|-&Uc>acwaao6C&hM zmswvDGq6<&^eEF=5yha`M4YC1%)S%K+`Ecc<~^!^qagZzEkWZ&4{Jgf;bQ`v*J+JQ zy?gBTgG^?w?()mpc5(O1w{2^iHVOD#p7)5>w!d|}kwdW&7X=LWR`&M>q_KBxEmPx_ z_(`+KjOd*qjpc{+X&bxru5XbZZ4#j=mWcO&_7419bc=fTxOdvcE5sKZGIUClFp@+% zUH5Ca26071M8HmrInh#Zd`Wh7=1?ANXl02hpc9p{7xFStV7Bh-pq+Nwu0ZM%!%<1P zhDMIk!d$GXy6I~)v4r_h!hI^F2PXq8JBiOa09P>)|9kj>NOY{Y=mT%}ioasmqV(dg z4Q^ZgsIroDHmQ+q=7VA^Xe`;4xQ?n;y-` zI7mvKdqFm@rep9*um4@X)^PPMRq4n0BEi&KS&DfR`3xQ&9vw#eFB!jQJ_&E%U#?-j zM5(oPVIqh>U$c(t#E7<;{q@3@L+@HFDR#^g`cI9yJ>d`1i^&@&x8@znIm+v1?X(X@ zqp{Iq(b^u8wsmQ@?mCGYbIdOT-v9jEwN?|@osM8rHC`0lrE>v9WBkxdSu&OL z_nyGptok)EdC=YG&0_lZ9ee;&58xFnoDNy{ws^0Sb~ zCi{$ea`CaLOVx$ZJpC8bZiJ-q1PNkTS5D2FlaWR-$Cane3HK8lT^xRY3;V!@J({9j zZ2ohfj*C~^5&Y&mpT7k;4{8k=!P+8u&IrwaIIOM-;=j^D)C32t@~9~{KO2+teVf(u zG$4-CM|Cz6!qW_T&){iW{!sqaOHypUQg3YUxSBiuX)5Uq8%X_tFtR9?b8lhn$twA| z`tCFB#Of+hkvNUVQCOMXGV$945Ba`#EZ--LE~4O9eqTP0l(zPhilVSHgCHY=SpiH* zpxVC=R7eCGkuAIDctMf*Y>*iR(}u5~tE+20X%-AM`1o@~Uub_VE%(k5%nJ6;APD=@ z#ySgAxshKS{=_HjFrwxg7IqZ&*~CF^fz)Cr+hazN*Is9LqoT z2wz%QV7`6JV@9<8VE~og^r2f=SXiPD(MuT_Oi@u$BNG##Qqo~8A4B2A!mEBRP;p|r zfWWgDok@Z1om(cOoC-fpHZvWLjUr?>iPC^WHerG=pNm0^qY~TP>7+v1M(ankwk2qz znE?zRGu;X}NIBh4=Io5-^GsFZ!q!7tWpGY$tZ^lPlrH&X|Ew$`b(uaS$$h z@FmIF;ii7yqdn%X_91YN0oZ`|xz(zhH1bW3{`~wLIn5@_$9SuNAMHo9o%?=@UI+BY zARmi7Y=Km6RgJH}rtlS<`%67TWd^WEa=7Bz#$0^Iq^D-Y4sup7r6itfl<_@Htfc~hU1y51v4^jx!fHkKAJix4dkA>@H*vrggjWB4&%$wcup#P;7o~1*b89zXbIhi32Hap@Ux*IxT1c(cYtE@?7D40zykA z&Seh61S}>ncBF#?Krc2K7dYS>KuhlMRLlZjAR|!=`HZ(cefl`-Xs;OXggP1HW6Hy->|4ZGM~54|vFyU8G&ja1?1m*A%YEn zGStiMQru6Wl1a(QIc&{;`EnZ$dpuQBBZb!jIJbAQ96xVY?y?kP3Op_l49N2ea5M)D zMS6gL5nw+Ff)@M^up76i1ly3NQ!u=OD+&oreu}=orfSJwF>FIK?$^ZNREeBJB8 zUwGj6u$(B{NpuCK<1P~urVm5BM;J639N=33Y%L94?q4`e)#H^nzVbWaI@0_Gm|+=S z!~kFzLJQ8>Ah|)Lvj_+|=mFY*Fh#9H9-V-2MH+qquM2|NCk-4$rv~XM63HHU4m~Y} zB|pk~F9@-ViwHdy&35a|xWeC*E|p&y9d5TYGB3Aot(Q2gmjruEZ)tXK_0~|H#QzEp z!{5nPVKu#Yi+*|eB1XHIBzwIxD09+^;qKkHpahqv(cQlfmJKWq1#m~SwYAZ37jA#< zf|qgF+WPvH5Eej6O)c78!^kMv-7*F|U9SpFp2Lt`YXOLzC zBz^jl@iaBjc9Xo~eYynt~*K(W|@H6~8Bu-jY=mCJJ^kaIoykWxh;8v$eHFEhz#71FhM6V3Sr>P+N9&jC76c+ptNQQ|$z*>L=;rJZ_$)H?fL-@P7 zRaoJI&{Te!F>TsNWliguVuh8SvWU-$R)SsSan9-P^0qmi8H<^TZ zcaBqpx;ihv!)jM5&f0F*Yq#!Vh^3}D5vL1vzaYbBw+su%j`ddW>v&$gfm31z-5Erx z`6(&K4B%>#v$3&>(|ighlO`iBJPHaqOH0d|z)nDQ(z3Einx$4A&}+Lc6~7seMEXoD z$G9#pw!FFFoI#28^sTzg+#0vO%UX^;fR=z@;q=p1+!7clU%dEo#()X?Si;WEE@n8l zL()@2T;^2fX9-q3J#SUR#vobzy4R2!4GtpSgm*Pqp=GOv1vT>>PjQ*dY=m)lU1ZFw zM_q9`$H(aU;B)6MXqS3kx8gv2>h;=${z{u89tBrOW?9oJNrOiOlG&Ra>rU=o;ocAt zmf4B1Lhm11S5=hZx2b#muO7u*dQ*#M4!@Bx9+z3O%>>%Ik{}XHCouoO{%s1g!T#?R z5iJ@&+3jRUx}uQu7E~%V7GkUNMPkWd(t1Gmz)>_>^4c%7;UT~p_Z+&Xox_^_Q5oPs z6M)(K;Oh(HB!%Bb5@cWnL!O8OD;R=L;lshofIn~o+SAO%MFCpIahRgU&oyM$?#D)i zV!}x*?bVU^@XFb)hV~(mJtUQBmxDc23T|$wY(Fad58Q<>2`3IZh|S zteOL>;|oL_mtGwLViPdBK5+eT0O?vD(#frTF0 zBS*amu2-fWT;`QkLFeOQac?LF-Dt9Z#$HC)Kt~;6)HQ&%3&xDA(Eh2%n_#go}>E-rG&S-hip|aNOzJ`g)$=y>Xd( z@R1@9AW9kzQRD**Y9rdl?xdfo4`Ws6h(8)J-F-^pa#%4?bW2(AVXr^AG5^Nz;khRg_E&6`&r)%nLz?C54)eF!W&oo)l3QX)vz(s&6GMHGHdHC zTAV3*fQSoR<}7e(7ztk&C8ejYWo2-(NX>66(F>=OYUi}frX!WxgBlq+T4P z{QpA9VC0a1oJs zR-pV8pYODafhY4eV>0_{Bfp8zTvS%(P4{i@O!`Gkgz)5?lCJu$#wG&-6~|O6 z3`g(Y%$0ZoG7Ry(F?F_rumZjlds_E2I_4*ejDJ_|f>}LBgPCCC*-(;7riF_Nw>}5i zb{^>t&R(*5?d?A`6j?Ode*SI^pZUI2piqp{xC&XGVpkNsw9FxfTo9mS^V}VMZuT`T z_f$`Pvu*6?w?Jx9&~74?v$e zkkZb3Y1mhuR!c0|1N++6~-O!&X~h-{m~-Il5YQ7BaD*But**?P&LLQA{J{DcyGp? z%=^Y~LC)ETFK{xnqlD2GQ<0Ml+xLn?*9OOI`~gE~Xh9OM7`W?xYP?~u%2=$v#p6c0 zld~dP%ukQ{D_XwErw_rc^LCl)!|+I5;f3C&fF9T4eVz>sW_jc1tPq|=T0!1QX_p&3 zZNZ(iZeG{&Yts~22gA`yRT;885syCq>hAClVS3TbVTdP3iQ2u|c=hS@wOcCA=U6|# z;`-iawoI>)dpUik;m_&y+kATu{cGj#de#Pw9`Jg(@qTv_B8^Pj?Bi&DlGBMu?rS*%sA&fJ zJN%>v-XBAdV+*xg@MYvL^QBMK^51%kM8BYea;tjVKD1m%Q@$_fStn%0FfnWWNeu$gu?c&)&CxPb?uL z5NWw;H8D`9Ju?f4iWD YA&>BklnQ@vfw)GGK%fxaM1xme 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 diff --git a/doc/img/Features_basic_dialog.xcf b/doc/img/Features_basic_dialog.xcf index 2425111971f6d5f177b78937f70d9f702e3c03b7..7bfd9134030d0813c025a28024833e75c5178412 100644 GIT binary patch delta 6227 zcmchb4^R}>9mn_X;7%KBfC!NUgqmt0>S>5lH2zuJ#z`9mL^%HrhJS}q#DC<(l=4o8 zpiT(EtYXF{mN*7t{8Jp%rfMxU5fg`zj_q2?#LaYug|rPDf0RC=8>*zOd_4A>gpz{N zlK9c_N%5l-zn5TLQ_MOGN^*+xbJpi8H8Y9ziOvKBBJ!G z4k_=Tmk+C*6A&EWV(GPkC(g8l&jiZiBso8$-Y_<>>qZYm@vkE5eE5W_NGPGPELGRxJXR zsD_o((4PZlXhpajwYoe?gv-Z*+TV#Jl|x>RznyX`@;0y%tOe@@?Nz(ID`I0@H*HIs)DZ!2SssHX&x3sc*jS<$7tiViXlrx zC)E)Xs~()%uh3!?G3-m0X4#2kr)Rj-l^tZ=7_YvTBZjY;4P+^jvW$CG!t<9i6GV*jifK8wdIT%^iP8vGaopQ*rTMvr zH@^SGs6;7T)O>daDmH?R_9{4+VkR;7^2(xPYgl^DhLVE(__;Zo3f9Lz@f3f)3~gbF zUnn=qo*@aW<+W_Zt*(XK)8Nr>>lQDeJI6v>_qgLk*C?B)+V-wVR#TG0&1!OrNj6S( zrcA7F2$h{FGxLfbUhOQ(n>p1eo1$IgpKc?evMV{SFnqDAFfZ9C4~bT%(NI}6uhK8- z$}d{H{?&%#2WxDLLl>#5%vyQ|(hK`vJ$>-+{d($jeR7n#5g?b;!SLPS9vX*|Wsy%O5va*(M@21WJtns2} zk}mU#%*>qMH=j9mV2L5KXImHjXGvz}3Xhr25Q-3wGZ9c*PL1 zptr7%zPDgOp=Vs5*_u&D`}YmW=-u5zk7Z<7eRI21*8!HRyGP&k$@Q8G=KrdjUd%Pl z?^$-1-rGEXet~Bs%ckdlO7HD9ruS^?q&JGw(^;1AQ_jz6w>dv8ZD$Yd-L+hw*0pSV z7w!8^dRkh(XM%6IBI^{TJ+1Y7_P2CXdVZ-sP4!Z!<~)6>*bY5@n%T;@-%dDxPBFBtkPHN+($a!S0 zn#0t_8O~>t>i0A2Otvfuf8MpkGMV{_`EI{acKvv&CHw`KB_(Af^WbFW!O1B^w@ucp zS+Blk^R}jGJaLU(v(b+=$0WY?9cNCc59-W`NC$GJ$PaQR@wVoGLbIknXL3bwXL3bQ zXL5nZnZ(DMJU3v?LCzF8|IT#h{5g~70y~o+?Z=wjnLHcZnOqUvnOqUjnVSEFVz4y_ zoQbqwXO2x^KJjwqM5j4njct%Kx$OctbHJLjhkBJvQ>^Rpq}kuKr7o6D<6AD{m@G)S zbf*KEk_f>dS9+UrKoHQBToK%qT;bo81oe_DJg)RHB~Jx1rO5d=r90=(lsu=IlK8Ec zNCz+_&jvRoR|r#{3{Wn)f}4`0I+maGTQ7woped2|XG-o8k0niUu2}9RpISLvm1k>? zjP_qFO^sBk-P%uTmb(Ai*~7#qtbLrV1`(@yUGNq(w+hkUAjr1^V_18~auNS^s$kup z1?#H?4_y#!>JU8krX)U-{qC#?e2^`8vPtj{iv|B^6l}&9ln*VUf38&U{M#V^@oZ^* zU$6}i`<*ur2Of)S&2KbD{#leWklDZynptZ zF-Wsn+56FUAqdn=JXhrw_-`IQf7LDU*Sw(Qe9|Zc{+oYQK5soL1cRH8>h`kpzi$c| z#3s2d)xb6lx~A$&7vuD|w`i}dx6g=kbq5#YP6Ya+z$dj=*7ju4ZJz_;TD9Z3v)zVx zC0GmAgD1ci@CtYn>=yji2u6WpH4^FDWCZ4b7SIaXz)G+dtOrkkE#MXKCfF_5VFaVV zv7GFcwIdmUIiLl!f;O-ctOe`A6JQH?1-uD%vsczfn-V(@8+y+q=9PF6*;=qS#hg0+smOAma(m4e2VPF_I0vrj(fbrlDIg8i{N8#rhwBy8T=Xe95@$D2Q$G%g6zY8W8Z3}uW#eO0aPXS9RL6T delta 6836 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 common controls + +The feature windows have common top and bottom bars + +![Feature window](../../doc/img/FeatureWindow.png) + +

    obB^lrtu=czFl#x^3Ww9G{tt=T{q`4{G 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 H6TklfU^0ji literal 0 HcmV?d00001 diff --git a/doc/img/MainSpectrum.xcf b/doc/img/MainSpectrum.xcf new file mode 100644 index 0000000000000000000000000000000000000000..2cb3915b691269b6064f581fa7bff5457af31ffc GIT binary patch literal 152027 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

    O`xem7m6&C?iH6~9t9 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 diff --git a/doc/img/BasicChannelSettings.xcf b/doc/img/BasicChannelSettings.xcf index 5fafecc352a9342d1882a7e79972ae33c3a62634..a68769e83ae41a7cdd8b61a1e497667e8ea26e14 100644 GIT binary patch literal 79906 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 diff --git a/plugins/channelmimo/beamsteeringcwmod/beamsteeringcwmodgui.cpp b/plugins/channelmimo/beamsteeringcwmod/beamsteeringcwmodgui.cpp index f3587d03e..5c35e4b8c 100644 --- a/plugins/channelmimo/beamsteeringcwmod/beamsteeringcwmodgui.cpp +++ b/plugins/channelmimo/beamsteeringcwmod/beamsteeringcwmodgui.cpp @@ -99,7 +99,6 @@ BeamSteeringCWModGUI::BeamSteeringCWModGUI(PluginAPI* pluginAPI, DeviceUISet *de ui->setupUi(getRollupContents()); getRollupContents()->arrangeRollups(); setAttribute(Qt::WA_DeleteOnClose, true); - setStreamIndicator("M"); connect(getRollupContents(), SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &))); @@ -114,7 +113,7 @@ BeamSteeringCWModGUI::BeamSteeringCWModGUI(PluginAPI* pluginAPI, DeviceUISet *de m_channelMarker.addStreamIndex(1); m_channelMarker.setColor(m_settings.m_rgbColor); m_channelMarker.setCenterFrequency(0); - m_channelMarker.setTitle("Beam Steering CW Source"); + m_channelMarker.setTitle("BeamSteeringCWMod"); m_channelMarker.setSourceOrSinkStream(false); m_channelMarker.blockSignals(false); m_channelMarker.setVisible(true); // activate signal on the last setting only @@ -228,6 +227,7 @@ void BeamSteeringCWModGUI::onMenuDialogCalled(const QPoint &p) dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); dialog.setReverseAPIChannelIndex(m_settings.m_reverseAPIChannelIndex); + dialog.setDefaultTitle(m_displayedName); dialog.move(p); dialog.exec(); diff --git a/plugins/channelmimo/beamsteeringcwmod/beamsteeringcwmodgui.h b/plugins/channelmimo/beamsteeringcwmod/beamsteeringcwmodgui.h index 093b9f245..984eb4dd4 100644 --- a/plugins/channelmimo/beamsteeringcwmod/beamsteeringcwmodgui.h +++ b/plugins/channelmimo/beamsteeringcwmod/beamsteeringcwmodgui.h @@ -57,6 +57,8 @@ public: virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } virtual bool getHidden() const { return m_settings.m_hidden; } virtual ChannelMarker& getChannelMarker() { return m_channelMarker; } + virtual int getStreamIndex() const { return -1; } + virtual void setStreamIndex(int streamIndex) { (void) streamIndex; } private: Ui::BeamSteeringCWModGUI* ui; diff --git a/plugins/channelmimo/interferometer/interferometergui.cpp b/plugins/channelmimo/interferometer/interferometergui.cpp index 6d57ff9da..8ab582f62 100644 --- a/plugins/channelmimo/interferometer/interferometergui.cpp +++ b/plugins/channelmimo/interferometer/interferometergui.cpp @@ -109,7 +109,6 @@ InterferometerGUI::InterferometerGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUI ui->setupUi(getRollupContents()); getRollupContents()->arrangeRollups(); setAttribute(Qt::WA_DeleteOnClose, true); - setStreamIndicator("M"); connect(getRollupContents(), SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &))); @@ -263,6 +262,7 @@ void InterferometerGUI::onMenuDialogCalled(const QPoint &p) dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); dialog.setReverseAPIChannelIndex(m_settings.m_reverseAPIChannelIndex); + dialog.setDefaultTitle(m_displayedName); dialog.move(p); dialog.exec(); diff --git a/plugins/channelmimo/interferometer/interferometergui.h b/plugins/channelmimo/interferometer/interferometergui.h index 47919d1e0..b4b2d6fa2 100644 --- a/plugins/channelmimo/interferometer/interferometergui.h +++ b/plugins/channelmimo/interferometer/interferometergui.h @@ -56,6 +56,8 @@ public: virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } virtual bool getHidden() const { return m_settings.m_hidden; } virtual ChannelMarker& getChannelMarker() { return m_channelMarker; } + virtual int getStreamIndex() const { return -1; } + virtual void setStreamIndex(int streamIndex) { (void) streamIndex; } private: Ui::InterferometerGUI* ui; diff --git a/plugins/channelrx/chanalyzer/chanalyzer.cpp b/plugins/channelrx/chanalyzer/chanalyzer.cpp index 9a00de8aa..985ae81f7 100644 --- a/plugins/channelrx/chanalyzer/chanalyzer.cpp +++ b/plugins/channelrx/chanalyzer/chanalyzer.cpp @@ -104,6 +104,11 @@ void ChannelAnalyzer::setDeviceAPI(DeviceAPI *deviceAPI) } } +uint32_t ChannelAnalyzer::getNumberOfDeviceStreams() const +{ + return m_deviceAPI->getNbSourceStreams(); +} + int ChannelAnalyzer::getChannelSampleRate() { DeviceSampleSource *source = m_deviceAPI->getSampleSource(); diff --git a/plugins/channelrx/chanalyzer/chanalyzer.h b/plugins/channelrx/chanalyzer/chanalyzer.h index 7bbb36f98..806594ca3 100644 --- a/plugins/channelrx/chanalyzer/chanalyzer.h +++ b/plugins/channelrx/chanalyzer/chanalyzer.h @@ -96,6 +96,7 @@ public: virtual int getNbSinkStreams() const { return 1; } virtual int getNbSourceStreams() const { return 0; } + uint32_t getNumberOfDeviceStreams() const; virtual qint64 getStreamCenterFrequency(int streamIndex, bool sinkElseSource) const { diff --git a/plugins/channelrx/chanalyzer/chanalyzergui.cpp b/plugins/channelrx/chanalyzer/chanalyzergui.cpp index 1e7cd0253..4be9991f5 100644 --- a/plugins/channelrx/chanalyzer/chanalyzergui.cpp +++ b/plugins/channelrx/chanalyzer/chanalyzergui.cpp @@ -472,6 +472,14 @@ void ChannelAnalyzerGUI::onMenuDialogCalled(const QPoint& p) dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); dialog.setReverseAPIChannelIndex(m_settings.m_reverseAPIChannelIndex); + dialog.setDefaultTitle(m_displayedName); + + if (m_deviceUISet->m_deviceMIMOEngine) + { + dialog.setNumberOfStreams(m_channelAnalyzer->getNumberOfDeviceStreams()); + dialog.setStreamIndex(m_settings.m_streamIndex); + } + dialog.move(p); dialog.exec(); @@ -487,6 +495,14 @@ void ChannelAnalyzerGUI::onMenuDialogCalled(const QPoint& p) setTitle(m_channelMarker.getTitle()); setTitleColor(m_settings.m_rgbColor); + if (m_deviceUISet->m_deviceMIMOEngine) + { + m_settings.m_streamIndex = dialog.getSelectedStreamIndex(); + m_channelMarker.clearStreamIndexes(); + m_channelMarker.addStreamIndex(m_settings.m_streamIndex); + updateIndexLabel(); + } + applySettings(); } diff --git a/plugins/channelrx/chanalyzer/chanalyzergui.h b/plugins/channelrx/chanalyzer/chanalyzergui.h index c44a9f8a9..1744544b6 100644 --- a/plugins/channelrx/chanalyzer/chanalyzergui.h +++ b/plugins/channelrx/chanalyzer/chanalyzergui.h @@ -58,6 +58,8 @@ public: virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } virtual bool getHidden() const { return m_settings.m_hidden; } virtual ChannelMarker& getChannelMarker() { return m_channelMarker; } + virtual int getStreamIndex() const { return m_settings.m_streamIndex; } + virtual void setStreamIndex(int streamIndex) { m_settings.m_streamIndex = streamIndex; } public slots: void channelMarkerChangedByCursor(); diff --git a/plugins/channelrx/demodadsb/adsbdemodgui.cpp b/plugins/channelrx/demodadsb/adsbdemodgui.cpp index 67c260912..e8dca5747 100644 --- a/plugins/channelrx/demodadsb/adsbdemodgui.cpp +++ b/plugins/channelrx/demodadsb/adsbdemodgui.cpp @@ -2779,6 +2779,14 @@ void ADSBDemodGUI::onMenuDialogCalled(const QPoint &p) dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); dialog.setReverseAPIChannelIndex(m_settings.m_reverseAPIChannelIndex); + dialog.setDefaultTitle(m_displayedName); + + if (m_deviceUISet->m_deviceMIMOEngine) + { + dialog.setNumberOfStreams(m_adsbDemod->getNumberOfDeviceStreams()); + dialog.setStreamIndex(m_settings.m_streamIndex); + } + dialog.move(p); dialog.exec(); @@ -2794,20 +2802,14 @@ void ADSBDemodGUI::onMenuDialogCalled(const QPoint &p) setTitle(m_channelMarker.getTitle()); setTitleColor(m_settings.m_rgbColor); - applySettings(); - } - else if ((m_contextMenuType == ContextMenuStreamSettings) && (m_deviceUISet->m_deviceMIMOEngine)) - { - DeviceStreamSelectionDialog dialog(this); - dialog.setNumberOfStreams(m_adsbDemod->getNumberOfDeviceStreams()); - dialog.setStreamIndex(m_settings.m_streamIndex); - dialog.move(p); - dialog.exec(); + if (m_deviceUISet->m_deviceMIMOEngine) + { + m_settings.m_streamIndex = dialog.getSelectedStreamIndex(); + m_channelMarker.clearStreamIndexes(); + m_channelMarker.addStreamIndex(m_settings.m_streamIndex); + updateIndexLabel(); + } - m_settings.m_streamIndex = dialog.getSelectedStreamIndex(); - m_channelMarker.clearStreamIndexes(); - m_channelMarker.addStreamIndex(m_settings.m_streamIndex); - displayStreamIndex(); applySettings(); } @@ -3950,7 +3952,7 @@ void ADSBDemodGUI::displaySettings() ui->logFilename->setToolTip(QString(".csv log filename: %1").arg(m_settings.m_logFilename)); ui->logEnable->setChecked(m_settings.m_logEnabled); - displayStreamIndex(); + updateIndexLabel(); QFont font(m_settings.m_tableFontName, m_settings.m_tableFontSize); ui->adsbData->setFont(font); @@ -4003,15 +4005,6 @@ void ADSBDemodGUI::displaySettings() blockApplySettings(false); } -void ADSBDemodGUI::displayStreamIndex() -{ - if (m_deviceUISet->m_deviceMIMOEngine) { - setStreamIndicator(tr("%1").arg(m_settings.m_streamIndex)); - } else { - setStreamIndicator("S"); // single channel indicator - } -} - void ADSBDemodGUI::leaveEvent(QEvent*) { m_channelMarker.setHighlighted(false); diff --git a/plugins/channelrx/demodadsb/adsbdemodgui.h b/plugins/channelrx/demodadsb/adsbdemodgui.h index 95553329f..f582e02b9 100644 --- a/plugins/channelrx/demodadsb/adsbdemodgui.h +++ b/plugins/channelrx/demodadsb/adsbdemodgui.h @@ -761,6 +761,8 @@ public: virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } virtual bool getHidden() const { return m_settings.m_hidden; } virtual ChannelMarker& getChannelMarker() { return m_channelMarker; } + virtual int getStreamIndex() const { return m_settings.m_streamIndex; } + virtual void setStreamIndex(int streamIndex) { m_settings.m_streamIndex = streamIndex; } void highlightAircraft(Aircraft *aircraft); void targetAircraft(Aircraft *aircraft); @@ -848,7 +850,6 @@ private: void blockApplySettings(bool block); void applySettings(bool force = false); void displaySettings(); - void displayStreamIndex(); bool handleMessage(const Message& message); void makeUIConnections(); void updateAbsoluteCenterFrequency(); diff --git a/plugins/channelrx/demodais/aisdemodgui.cpp b/plugins/channelrx/demodais/aisdemodgui.cpp index cc81405a2..2a70598f8 100644 --- a/plugins/channelrx/demodais/aisdemodgui.cpp +++ b/plugins/channelrx/demodais/aisdemodgui.cpp @@ -396,6 +396,14 @@ void AISDemodGUI::onMenuDialogCalled(const QPoint &p) dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); dialog.setReverseAPIChannelIndex(m_settings.m_reverseAPIChannelIndex); + dialog.setDefaultTitle(m_displayedName); + + if (m_deviceUISet->m_deviceMIMOEngine) + { + dialog.setNumberOfStreams(m_aisDemod->getNumberOfDeviceStreams()); + dialog.setStreamIndex(m_settings.m_streamIndex); + } + dialog.move(p); dialog.exec(); @@ -411,20 +419,14 @@ void AISDemodGUI::onMenuDialogCalled(const QPoint &p) setTitle(m_channelMarker.getTitle()); setTitleColor(m_settings.m_rgbColor); - applySettings(); - } - else if ((m_contextMenuType == ContextMenuStreamSettings) && (m_deviceUISet->m_deviceMIMOEngine)) - { - DeviceStreamSelectionDialog dialog(this); - dialog.setNumberOfStreams(m_aisDemod->getNumberOfDeviceStreams()); - dialog.setStreamIndex(m_settings.m_streamIndex); - dialog.move(p); - dialog.exec(); + if (m_deviceUISet->m_deviceMIMOEngine) + { + m_settings.m_streamIndex = dialog.getSelectedStreamIndex(); + m_channelMarker.clearStreamIndexes(); + m_channelMarker.addStreamIndex(m_settings.m_streamIndex); + updateIndexLabel(); + } - m_settings.m_streamIndex = dialog.getSelectedStreamIndex(); - m_channelMarker.clearStreamIndexes(); - m_channelMarker.addStreamIndex(m_settings.m_streamIndex); - displayStreamIndex(); applySettings(); } @@ -599,7 +601,7 @@ void AISDemodGUI::displaySettings() ui->thresholdText->setText(QString("%1").arg(m_settings.m_correlationThreshold)); ui->threshold->setValue(m_settings.m_correlationThreshold); - displayStreamIndex(); + updateIndexLabel(); ui->filterMMSI->setText(m_settings.m_filterMMSI); @@ -633,15 +635,6 @@ void AISDemodGUI::displaySettings() blockApplySettings(false); } -void AISDemodGUI::displayStreamIndex() -{ - if (m_deviceUISet->m_deviceMIMOEngine) { - setStreamIndicator(tr("%1").arg(m_settings.m_streamIndex)); - } else { - setStreamIndicator("S"); // single channel indicator - } -} - void AISDemodGUI::leaveEvent(QEvent*) { m_channelMarker.setHighlighted(false); diff --git a/plugins/channelrx/demodais/aisdemodgui.h b/plugins/channelrx/demodais/aisdemodgui.h index 5856f1852..7209daaf1 100644 --- a/plugins/channelrx/demodais/aisdemodgui.h +++ b/plugins/channelrx/demodais/aisdemodgui.h @@ -66,6 +66,8 @@ public: virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } virtual bool getHidden() const { return m_settings.m_hidden; } virtual ChannelMarker& getChannelMarker() { return m_channelMarker; } + virtual int getStreamIndex() const { return m_settings.m_streamIndex; } + virtual void setStreamIndex(int streamIndex) { m_settings.m_streamIndex = streamIndex; } public slots: void channelMarkerChangedByCursor(); @@ -96,7 +98,6 @@ private: void blockApplySettings(bool block); void applySettings(bool force = false); void displaySettings(); - void displayStreamIndex(); void messageReceived(const QByteArray& message, const QDateTime& dateTime); bool handleMessage(const Message& message); void makeUIConnections(); diff --git a/plugins/channelrx/demodam/amdemodgui.cpp b/plugins/channelrx/demodam/amdemodgui.cpp index 8a4020f6a..35641d3e9 100644 --- a/plugins/channelrx/demodam/amdemodgui.cpp +++ b/plugins/channelrx/demodam/amdemodgui.cpp @@ -208,6 +208,14 @@ void AMDemodGUI::onMenuDialogCalled(const QPoint &p) dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); dialog.setReverseAPIChannelIndex(m_settings.m_reverseAPIChannelIndex); + dialog.setDefaultTitle(m_displayedName); + + if (m_deviceUISet->m_deviceMIMOEngine) + { + dialog.setNumberOfStreams(m_amDemod->getNumberOfDeviceStreams()); + dialog.setStreamIndex(m_settings.m_streamIndex); + } + dialog.move(p); dialog.exec(); @@ -223,20 +231,14 @@ void AMDemodGUI::onMenuDialogCalled(const QPoint &p) setTitle(m_channelMarker.getTitle()); setTitleColor(m_settings.m_rgbColor); - applySettings(); - } - else if ((m_contextMenuType == ContextMenuStreamSettings) && (m_deviceUISet->m_deviceMIMOEngine)) - { - DeviceStreamSelectionDialog dialog(this); - dialog.setNumberOfStreams(m_amDemod->getNumberOfDeviceStreams()); - dialog.setStreamIndex(m_settings.m_streamIndex); - dialog.move(p); - dialog.exec(); + if (m_deviceUISet->m_deviceMIMOEngine) + { + m_settings.m_streamIndex = dialog.getSelectedStreamIndex(); + m_channelMarker.clearStreamIndexes(); + m_channelMarker.addStreamIndex(m_settings.m_streamIndex); + updateIndexLabel(); + } - m_settings.m_streamIndex = dialog.getSelectedStreamIndex(); - m_channelMarker.clearStreamIndexes(); - m_channelMarker.addStreamIndex(m_settings.m_streamIndex); - displayStreamIndex(); applySettings(); } @@ -387,22 +389,13 @@ void AMDemodGUI::displaySettings() ui->ssb->setIcon(m_iconDSBUSB); } - displayStreamIndex(); + updateIndexLabel(); getRollupContents()->restoreState(m_rollupState); updateAbsoluteCenterFrequency(); blockApplySettings(false); } -void AMDemodGUI::displayStreamIndex() -{ - if (m_deviceUISet->m_deviceMIMOEngine) { - setStreamIndicator(tr("%1").arg(m_settings.m_streamIndex)); - } else { - setStreamIndicator("S"); // single channel indicator - } -} - void AMDemodGUI::leaveEvent(QEvent*) { m_channelMarker.setHighlighted(false); diff --git a/plugins/channelrx/demodam/amdemodgui.h b/plugins/channelrx/demodam/amdemodgui.h index 7d2440134..cedcf4b0c 100644 --- a/plugins/channelrx/demodam/amdemodgui.h +++ b/plugins/channelrx/demodam/amdemodgui.h @@ -40,6 +40,8 @@ public: virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } virtual bool getHidden() const { return m_settings.m_hidden; } virtual ChannelMarker& getChannelMarker() { return m_channelMarker; } + virtual int getStreamIndex() const { return m_settings.m_streamIndex; } + virtual void setStreamIndex(int streamIndex) { m_settings.m_streamIndex = streamIndex; } public slots: void channelMarkerChangedByCursor(); @@ -72,7 +74,6 @@ private: void blockApplySettings(bool block); void applySettings(bool force = false); void displaySettings(); - void displayStreamIndex(); bool handleMessage(const Message& message); void makeUIConnections(); void updateAbsoluteCenterFrequency(); diff --git a/plugins/channelrx/demodapt/aptdemodgui.cpp b/plugins/channelrx/demodapt/aptdemodgui.cpp index bb1e42acb..19e0b08c7 100644 --- a/plugins/channelrx/demodapt/aptdemodgui.cpp +++ b/plugins/channelrx/demodapt/aptdemodgui.cpp @@ -556,6 +556,14 @@ void APTDemodGUI::onMenuDialogCalled(const QPoint &p) dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); dialog.setReverseAPIChannelIndex(m_settings.m_reverseAPIChannelIndex); + dialog.setDefaultTitle(m_displayedName); + + if (m_deviceUISet->m_deviceMIMOEngine) + { + dialog.setNumberOfStreams(m_aptDemod->getNumberOfDeviceStreams()); + dialog.setStreamIndex(m_settings.m_streamIndex); + } + dialog.move(p); dialog.exec(); @@ -571,20 +579,14 @@ void APTDemodGUI::onMenuDialogCalled(const QPoint &p) setTitle(m_channelMarker.getTitle()); setTitleColor(m_settings.m_rgbColor); - applySettings(); - } - else if ((m_contextMenuType == ContextMenuStreamSettings) && (m_deviceUISet->m_deviceMIMOEngine)) - { - DeviceStreamSelectionDialog dialog(this); - dialog.setNumberOfStreams(m_aptDemod->getNumberOfDeviceStreams()); - dialog.setStreamIndex(m_settings.m_streamIndex); - dialog.move(p); - dialog.exec(); + if (m_deviceUISet->m_deviceMIMOEngine) + { + m_settings.m_streamIndex = dialog.getSelectedStreamIndex(); + m_channelMarker.clearStreamIndexes(); + m_channelMarker.addStreamIndex(m_settings.m_streamIndex); + updateIndexLabel(); + } - m_settings.m_streamIndex = dialog.getSelectedStreamIndex(); - m_channelMarker.clearStreamIndexes(); - m_channelMarker.addStreamIndex(m_settings.m_streamIndex); - displayStreamIndex(); applySettings(); } @@ -781,7 +783,7 @@ void APTDemodGUI::displaySettings() displayPalettes(); displayLabels(); - displayStreamIndex(); + updateIndexLabel(); getRollupContents()->restoreState(m_rollupState); updateAbsoluteCenterFrequency(); @@ -812,15 +814,6 @@ void APTDemodGUI::displayPalettes() ui->channels->blockSignals(false); } -void APTDemodGUI::displayStreamIndex() -{ - if (m_deviceUISet->m_deviceMIMOEngine) { - setStreamIndicator(tr("%1").arg(m_settings.m_streamIndex)); - } else { - setStreamIndicator("S"); // single channel indicator - } -} - void APTDemodGUI::leaveEvent(QEvent*) { m_channelMarker.setHighlighted(false); diff --git a/plugins/channelrx/demodapt/aptdemodgui.h b/plugins/channelrx/demodapt/aptdemodgui.h index 1cb07b223..71fc5b2cf 100644 --- a/plugins/channelrx/demodapt/aptdemodgui.h +++ b/plugins/channelrx/demodapt/aptdemodgui.h @@ -84,6 +84,8 @@ public: virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } virtual bool getHidden() const { return m_settings.m_hidden; } virtual ChannelMarker& getChannelMarker() { return m_channelMarker; } + virtual int getStreamIndex() const { return m_settings.m_streamIndex; } + virtual void setStreamIndex(int streamIndex) { m_settings.m_streamIndex = streamIndex; } public slots: void channelMarkerChangedByCursor(); @@ -123,7 +125,6 @@ private: void displaySettings(); void displayPalettes(); void displayLabels(); - void displayStreamIndex(); bool handleMessage(const Message& message); void makeUIConnections(); void updateAbsoluteCenterFrequency(); diff --git a/plugins/channelrx/demodatv/atvdemod.cpp b/plugins/channelrx/demodatv/atvdemod.cpp index 0eeb224db..28bb15d7c 100644 --- a/plugins/channelrx/demodatv/atvdemod.cpp +++ b/plugins/channelrx/demodatv/atvdemod.cpp @@ -87,6 +87,11 @@ void ATVDemod::setDeviceAPI(DeviceAPI *deviceAPI) } } +uint32_t ATVDemod::getNumberOfDeviceStreams() const +{ + return m_deviceAPI->getNbSourceStreams(); +} + void ATVDemod::start() { qDebug("ATVDemod::start"); diff --git a/plugins/channelrx/demodatv/atvdemod.h b/plugins/channelrx/demodatv/atvdemod.h index f70342a17..cafcd7722 100644 --- a/plugins/channelrx/demodatv/atvdemod.h +++ b/plugins/channelrx/demodatv/atvdemod.h @@ -97,6 +97,7 @@ public: double getMagSq() const { return m_basebandSink->getMagSq(); } //!< Beware this is scaled to 2^30 bool getBFOLocked() { return m_basebandSink->getBFOLocked(); } void setVideoTabIndex(int videoTabIndex) { m_basebandSink->setVideoTabIndex(videoTabIndex); } + uint32_t getNumberOfDeviceStreams() const; static const char* const m_channelIdURI; static const char* const m_channelId; diff --git a/plugins/channelrx/demodatv/atvdemodgui.cpp b/plugins/channelrx/demodatv/atvdemodgui.cpp index a11a48317..21cda893d 100644 --- a/plugins/channelrx/demodatv/atvdemodgui.cpp +++ b/plugins/channelrx/demodatv/atvdemodgui.cpp @@ -24,6 +24,7 @@ #include "device/deviceuiset.h" #include "dsp/scopevis.h" #include "dsp/glscopesettings.h" +#include "gui/basicchannelsettingsdialog.h" #include "ui_atvdemodgui.h" #include "plugin/pluginapi.h" #include "util/simpleserializer.h" @@ -87,7 +88,7 @@ void ATVDemodGUI::displaySettings() setTitleColor(m_settings.m_rgbColor); setWindowTitle(m_channelMarker.getTitle()); setTitle(m_channelMarker.getTitle()); - displayStreamIndex(); + updateIndexLabel(); m_doApplySettings = false; @@ -126,15 +127,6 @@ void ATVDemodGUI::displaySettings() m_doApplySettings = true; } -void ATVDemodGUI::displayStreamIndex() -{ - if (m_deviceUISet->m_deviceMIMOEngine) { - setStreamIndicator(tr("%1").arg(m_settings.m_streamIndex)); - } else { - setStreamIndicator("S"); // single channel indicator - } -} - void ATVDemodGUI::displayRFBandwidths() { int sliderPosition = m_settings.m_fftBandwidth / m_rfSliderDivisor; @@ -209,6 +201,53 @@ void ATVDemodGUI::handleSourceMessages() } } +void ATVDemodGUI::onMenuDialogCalled(const QPoint &p) +{ + if (m_contextMenuType == ContextMenuChannelSettings) + { + BasicChannelSettingsDialog dialog(&m_channelMarker, this); + dialog.setUseReverseAPI(m_settings.m_useReverseAPI); + dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); + dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); + dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); + dialog.setReverseAPIChannelIndex(m_settings.m_reverseAPIChannelIndex); + dialog.setDefaultTitle(m_displayedName); + + if (m_deviceUISet->m_deviceMIMOEngine) + { + dialog.setNumberOfStreams(m_atvDemod->getNumberOfDeviceStreams()); + dialog.setStreamIndex(m_settings.m_streamIndex); + } + + dialog.move(p); + dialog.exec(); + + m_settings.m_rgbColor = m_channelMarker.getColor().rgb(); + m_settings.m_title = m_channelMarker.getTitle(); + m_settings.m_useReverseAPI = dialog.useReverseAPI(); + m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); + m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); + m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); + m_settings.m_reverseAPIChannelIndex = dialog.getReverseAPIChannelIndex(); + + setWindowTitle(m_settings.m_title); + setTitle(m_channelMarker.getTitle()); + setTitleColor(m_settings.m_rgbColor); + + if (m_deviceUISet->m_deviceMIMOEngine) + { + m_settings.m_streamIndex = dialog.getSelectedStreamIndex(); + m_channelMarker.clearStreamIndexes(); + m_channelMarker.addStreamIndex(m_settings.m_streamIndex); + updateIndexLabel(); + } + + applySettings(); + } + + resetContextMenuType(); +} + void ATVDemodGUI::onWidgetRolled(QWidget* widget, bool rollDown) { (void) widget; @@ -234,6 +273,7 @@ ATVDemodGUI::ATVDemodGUI(PluginAPI* objPluginAPI, DeviceUISet *deviceUISet, Base m_helpURL = "plugins/channelrx/demodatv/readme.md"; setAttribute(Qt::WA_DeleteOnClose, true); connect(getRollupContents(), SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &))); m_atvDemod = (ATVDemod*) rxChannel; m_atvDemod->setMessageQueueToGUI(getInputMessageQueue()); diff --git a/plugins/channelrx/demodatv/atvdemodgui.h b/plugins/channelrx/demodatv/atvdemodgui.h index 123bf011e..663d565a9 100644 --- a/plugins/channelrx/demodatv/atvdemodgui.h +++ b/plugins/channelrx/demodatv/atvdemodgui.h @@ -59,6 +59,8 @@ public: virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } virtual bool getHidden() const { return m_settings.m_hidden; } virtual ChannelMarker& getChannelMarker() { return m_channelMarker; } + virtual int getStreamIndex() const { return m_settings.m_streamIndex; } + virtual void setStreamIndex(int streamIndex) { m_settings.m_streamIndex = streamIndex; } public slots: void channelMarkerChangedByCursor(); @@ -90,7 +92,6 @@ private: void applySettings(bool force = false); void displaySettings(); - void displayStreamIndex(); void displayRFBandwidths(); void applySampleRate(); void setChannelMarkerBandwidth(); @@ -107,6 +108,7 @@ private: private slots: void handleSourceMessages(); void onWidgetRolled(QWidget* widget, bool rollDown); + void onMenuDialogCalled(const QPoint& p); void tick(); void on_synchLevel_valueChanged(int value); void on_blackLevel_valueChanged(int value); diff --git a/plugins/channelrx/demodbfm/bfmdemodgui.cpp b/plugins/channelrx/demodbfm/bfmdemodgui.cpp index 921a35a0d..02fc49f35 100644 --- a/plugins/channelrx/demodbfm/bfmdemodgui.cpp +++ b/plugins/channelrx/demodbfm/bfmdemodgui.cpp @@ -330,6 +330,13 @@ void BFMDemodGUI::onMenuDialogCalled(const QPoint &p) dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); dialog.setReverseAPIChannelIndex(m_settings.m_reverseAPIChannelIndex); + dialog.setDefaultTitle(m_displayedName); + + if (m_deviceUISet->m_deviceMIMOEngine) + { + dialog.setNumberOfStreams(m_bfmDemod->getNumberOfDeviceStreams()); + dialog.setStreamIndex(m_settings.m_streamIndex); + } dialog.move(p); dialog.exec(); @@ -346,20 +353,14 @@ void BFMDemodGUI::onMenuDialogCalled(const QPoint &p) setTitle(m_channelMarker.getTitle()); setTitleColor(m_settings.m_rgbColor); - applySettings(); - } - else if ((m_contextMenuType == ContextMenuStreamSettings) && (m_deviceUISet->m_deviceMIMOEngine)) - { - DeviceStreamSelectionDialog dialog(this); - dialog.setNumberOfStreams(m_bfmDemod->getNumberOfDeviceStreams()); - dialog.setStreamIndex(m_settings.m_streamIndex); - dialog.move(p); - dialog.exec(); + if (m_deviceUISet->m_deviceMIMOEngine) + { + m_settings.m_streamIndex = dialog.getSelectedStreamIndex(); + m_channelMarker.clearStreamIndexes(); + m_channelMarker.addStreamIndex(m_settings.m_streamIndex); + updateIndexLabel(); + } - m_settings.m_streamIndex = dialog.getSelectedStreamIndex(); - m_channelMarker.clearStreamIndexes(); - m_channelMarker.addStreamIndex(m_settings.m_streamIndex); - displayStreamIndex(); applySettings(); } @@ -492,22 +493,13 @@ void BFMDemodGUI::displaySettings() ui->showPilot->setChecked(m_settings.m_showPilot); ui->rds->setChecked(m_settings.m_rdsActive); - displayStreamIndex(); + updateIndexLabel(); getRollupContents()->restoreState(m_rollupState); updateAbsoluteCenterFrequency(); blockApplySettings(false); } -void BFMDemodGUI::displayStreamIndex() -{ - if (m_deviceUISet->m_deviceMIMOEngine) { - setStreamIndicator(tr("%1").arg(m_settings.m_streamIndex)); - } else { - setStreamIndicator("S"); // single channel indicator - } -} - void BFMDemodGUI::leaveEvent(QEvent*) { m_channelMarker.setHighlighted(false); diff --git a/plugins/channelrx/demodbfm/bfmdemodgui.h b/plugins/channelrx/demodbfm/bfmdemodgui.h index e89dabe31..7b3f441a3 100644 --- a/plugins/channelrx/demodbfm/bfmdemodgui.h +++ b/plugins/channelrx/demodbfm/bfmdemodgui.h @@ -57,6 +57,8 @@ public: virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } virtual bool getHidden() const { return m_settings.m_hidden; } virtual ChannelMarker& getChannelMarker() { return m_channelMarker; } + virtual int getStreamIndex() const { return m_settings.m_streamIndex; } + virtual void setStreamIndex(int streamIndex) { m_settings.m_streamIndex = streamIndex; } public slots: void channelMarkerChangedByCursor(); @@ -88,7 +90,6 @@ private: void blockApplySettings(bool block); void applySettings(bool force = false); void displaySettings(); - void displayStreamIndex(); void rdsUpdate(bool force); void rdsUpdateFixedFields(); bool handleMessage(const Message& message); diff --git a/plugins/channelrx/demodchirpchat/chirpchatdemodgui.cpp b/plugins/channelrx/demodchirpchat/chirpchatdemodgui.cpp index e7eefe4c9..1226ebbae 100644 --- a/plugins/channelrx/demodchirpchat/chirpchatdemodgui.cpp +++ b/plugins/channelrx/demodchirpchat/chirpchatdemodgui.cpp @@ -343,6 +343,14 @@ void ChirpChatDemodGUI::onMenuDialogCalled(const QPoint &p) dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); dialog.setReverseAPIChannelIndex(m_settings.m_reverseAPIChannelIndex); + dialog.setDefaultTitle(m_displayedName); + + if (m_deviceUISet->m_deviceMIMOEngine) + { + dialog.setNumberOfStreams(m_chirpChatDemod->getNumberOfDeviceStreams()); + dialog.setStreamIndex(m_settings.m_streamIndex); + } + dialog.move(p); dialog.exec(); @@ -358,20 +366,14 @@ void ChirpChatDemodGUI::onMenuDialogCalled(const QPoint &p) setTitle(m_channelMarker.getTitle()); setTitleColor(m_settings.m_rgbColor); - applySettings(); - } - else if ((m_contextMenuType == ContextMenuStreamSettings) && (m_deviceUISet->m_deviceMIMOEngine)) - { - DeviceStreamSelectionDialog dialog(this); - dialog.setNumberOfStreams(m_chirpChatDemod->getNumberOfDeviceStreams()); - dialog.setStreamIndex(m_settings.m_streamIndex); - dialog.move(p); - dialog.exec(); + if (m_deviceUISet->m_deviceMIMOEngine) + { + m_settings.m_streamIndex = dialog.getSelectedStreamIndex(); + m_channelMarker.clearStreamIndexes(); + m_channelMarker.addStreamIndex(m_settings.m_streamIndex); + updateIndexLabel(); + } - m_settings.m_streamIndex = dialog.getSelectedStreamIndex(); - m_channelMarker.clearStreamIndexes(); - m_channelMarker.addStreamIndex(m_settings.m_streamIndex); - displayStreamIndex(); applySettings(); } @@ -507,21 +509,13 @@ void ChirpChatDemodGUI::displaySettings() ui->messageLengthAuto->setChecked(m_settings.m_autoNbSymbolsMax); displaySquelch(); + updateIndexLabel(); getRollupContents()->restoreState(m_rollupState); updateAbsoluteCenterFrequency(); blockApplySettings(false); } -void ChirpChatDemodGUI::displayStreamIndex() -{ - if (m_deviceUISet->m_deviceMIMOEngine) { - setStreamIndicator(tr("%1").arg(m_settings.m_streamIndex)); - } else { - setStreamIndicator("S"); // single channel indicator - } -} - void ChirpChatDemodGUI::displaySquelch() { ui->eomSquelch->setValue(m_settings.m_eomSquelchTenths); diff --git a/plugins/channelrx/demodchirpchat/chirpchatdemodgui.h b/plugins/channelrx/demodchirpchat/chirpchatdemodgui.h index 123c613d8..4678e0868 100644 --- a/plugins/channelrx/demodchirpchat/chirpchatdemodgui.h +++ b/plugins/channelrx/demodchirpchat/chirpchatdemodgui.h @@ -55,6 +55,8 @@ public: virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } virtual bool getHidden() const { return m_settings.m_hidden; } virtual ChannelMarker& getChannelMarker() { return m_channelMarker; } + virtual int getStreamIndex() const { return m_settings.m_streamIndex; } + virtual void setStreamIndex(int streamIndex) { m_settings.m_streamIndex = streamIndex; } private slots: void channelMarkerChangedByCursor(); @@ -113,7 +115,6 @@ private: void blockApplySettings(bool block); void applySettings(bool force = false); void displaySettings(); - void displayStreamIndex(); void displaySquelch(); void setBandwidths(); void showLoRaMessage(const Message& message); diff --git a/plugins/channelrx/demoddab/dabdemodgui.cpp b/plugins/channelrx/demoddab/dabdemodgui.cpp index 8e1729f12..cee8f38a5 100644 --- a/plugins/channelrx/demoddab/dabdemodgui.cpp +++ b/plugins/channelrx/demoddab/dabdemodgui.cpp @@ -406,6 +406,14 @@ void DABDemodGUI::onMenuDialogCalled(const QPoint &p) dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); dialog.setReverseAPIChannelIndex(m_settings.m_reverseAPIChannelIndex); + dialog.setDefaultTitle(m_displayedName); + + if (m_deviceUISet->m_deviceMIMOEngine) + { + dialog.setNumberOfStreams(m_dabDemod->getNumberOfDeviceStreams()); + dialog.setStreamIndex(m_settings.m_streamIndex); + } + dialog.move(p); dialog.exec(); @@ -421,20 +429,14 @@ void DABDemodGUI::onMenuDialogCalled(const QPoint &p) setTitle(m_channelMarker.getTitle()); setTitleColor(m_settings.m_rgbColor); - applySettings(); - } - else if ((m_contextMenuType == ContextMenuStreamSettings) && (m_deviceUISet->m_deviceMIMOEngine)) - { - DeviceStreamSelectionDialog dialog(this); - dialog.setNumberOfStreams(m_dabDemod->getNumberOfDeviceStreams()); - dialog.setStreamIndex(m_settings.m_streamIndex); - dialog.move(p); - dialog.exec(); + if (m_deviceUISet->m_deviceMIMOEngine) + { + m_settings.m_streamIndex = dialog.getSelectedStreamIndex(); + m_channelMarker.clearStreamIndexes(); + m_channelMarker.addStreamIndex(m_settings.m_streamIndex); + updateIndexLabel(); + } - m_settings.m_streamIndex = dialog.getSelectedStreamIndex(); - m_channelMarker.clearStreamIndexes(); - m_channelMarker.addStreamIndex(m_settings.m_streamIndex); - displayStreamIndex(); applySettings(); } @@ -563,7 +565,7 @@ void DABDemodGUI::displaySettings() ui->rfBWText->setText(QString("%1k").arg(m_settings.m_rfBandwidth / 1000.0, 0, 'f', 1)); ui->rfBW->setValue(m_settings.m_rfBandwidth / 100.0); - displayStreamIndex(); + updateIndexLabel(); ui->filter->setText(m_settings.m_filter); @@ -586,15 +588,6 @@ void DABDemodGUI::displaySettings() blockApplySettings(false); } -void DABDemodGUI::displayStreamIndex() -{ - if (m_deviceUISet->m_deviceMIMOEngine) { - setStreamIndicator(tr("%1").arg(m_settings.m_streamIndex)); - } else { - setStreamIndicator("S"); // single channel indicator - } -} - void DABDemodGUI::leaveEvent(QEvent*) { m_channelMarker.setHighlighted(false); diff --git a/plugins/channelrx/demoddab/dabdemodgui.h b/plugins/channelrx/demoddab/dabdemodgui.h index 9ed800f16..b5b4e9681 100644 --- a/plugins/channelrx/demoddab/dabdemodgui.h +++ b/plugins/channelrx/demoddab/dabdemodgui.h @@ -62,6 +62,8 @@ public: virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } virtual bool getHidden() const { return m_settings.m_hidden; } virtual ChannelMarker& getChannelMarker() { return m_channelMarker; } + virtual int getStreamIndex() const { return m_settings.m_streamIndex; } + virtual void setStreamIndex(int streamIndex) { m_settings.m_streamIndex = streamIndex; } public slots: void channelMarkerChangedByCursor(); @@ -91,7 +93,6 @@ private: void blockApplySettings(bool block); void applySettings(bool force = false); void displaySettings(); - void displayStreamIndex(); void addProgramName(const DABDemod::MsgDABProgramName& program); bool handleMessage(const Message& message); void makeUIConnections(); diff --git a/plugins/channelrx/demoddatv/datvdemodgui.cpp b/plugins/channelrx/demoddatv/datvdemodgui.cpp index 73515445c..c2ed28dae 100644 --- a/plugins/channelrx/demoddatv/datvdemodgui.cpp +++ b/plugins/channelrx/demoddatv/datvdemodgui.cpp @@ -162,6 +162,14 @@ void DATVDemodGUI::onMenuDialogCalled(const QPoint &p) dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); dialog.setReverseAPIChannelIndex(m_settings.m_reverseAPIChannelIndex); + dialog.setDefaultTitle(m_displayedName); + + if (m_deviceUISet->m_deviceMIMOEngine) + { + dialog.setNumberOfStreams(m_datvDemod->getNumberOfDeviceStreams()); + dialog.setStreamIndex(m_settings.m_streamIndex); + } + dialog.move(p); dialog.exec(); @@ -177,20 +185,14 @@ void DATVDemodGUI::onMenuDialogCalled(const QPoint &p) setTitle(m_channelMarker.getTitle()); setTitleColor(m_settings.m_rgbColor); - applySettings(); - } - else if ((m_contextMenuType == ContextMenuStreamSettings) && (m_deviceUISet->m_deviceMIMOEngine)) - { - DeviceStreamSelectionDialog dialog(this); - dialog.setNumberOfStreams(m_datvDemod->getNumberOfDeviceStreams()); - dialog.setStreamIndex(m_settings.m_streamIndex); - dialog.move(p); - dialog.exec(); + if (m_deviceUISet->m_deviceMIMOEngine) + { + m_settings.m_streamIndex = dialog.getSelectedStreamIndex(); + m_channelMarker.clearStreamIndexes(); + m_channelMarker.addStreamIndex(m_settings.m_streamIndex); + updateIndexLabel(); + } - m_settings.m_streamIndex = dialog.getSelectedStreamIndex(); - m_channelMarker.clearStreamIndexes(); - m_channelMarker.addStreamIndex(m_settings.m_streamIndex); - displayStreamIndex(); applySettings(); } @@ -413,6 +415,7 @@ void DATVDemodGUI::displaySettings() getRollupContents()->restoreState(m_rollupState); updateAbsoluteCenterFrequency(); + updateIndexLabel(); blockApplySettings(false); } @@ -460,15 +463,6 @@ void DATVDemodGUI::displaySystemConfiguration() ui->cmbFEC->blockSignals(false); } -void DATVDemodGUI::displayStreamIndex() -{ - if (m_deviceUISet->m_deviceMIMOEngine) { - setStreamIndicator(tr("%1").arg(m_settings.m_streamIndex)); - } else { - setStreamIndicator("S"); // single channel indicator - } -} - void DATVDemodGUI::applySettings(bool force) { if (m_blnDoApplySettings) diff --git a/plugins/channelrx/demoddatv/datvdemodgui.h b/plugins/channelrx/demoddatv/datvdemodgui.h index 1b2511983..34b987c10 100644 --- a/plugins/channelrx/demoddatv/datvdemodgui.h +++ b/plugins/channelrx/demoddatv/datvdemodgui.h @@ -59,8 +59,9 @@ public: virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } virtual bool getHidden() const { return m_settings.m_hidden; } virtual ChannelMarker& getChannelMarker() { return m_channelMarker; } - virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } + virtual int getStreamIndex() const { return m_settings.m_streamIndex; } + virtual void setStreamIndex(int streamIndex) { m_settings.m_streamIndex = streamIndex; } static const char* const m_strChannelID; @@ -140,7 +141,6 @@ private: void applySettings(bool force = false); void displaySettings(); void displaySystemConfiguration(); - void displayStreamIndex(); QString formatBytes(qint64 intBytes); void displayRRCParameters(bool blnVisible); diff --git a/plugins/channelrx/demoddsd/dsddemodgui.cpp b/plugins/channelrx/demoddsd/dsddemodgui.cpp index 83fd0db09..201d95bd0 100644 --- a/plugins/channelrx/demoddsd/dsddemodgui.cpp +++ b/plugins/channelrx/demoddsd/dsddemodgui.cpp @@ -274,6 +274,13 @@ void DSDDemodGUI::onMenuDialogCalled(const QPoint &p) dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); dialog.setReverseAPIChannelIndex(m_settings.m_reverseAPIChannelIndex); + dialog.setDefaultTitle(m_displayedName); + + if (m_deviceUISet->m_deviceMIMOEngine) + { + dialog.setNumberOfStreams(m_dsdDemod->getNumberOfDeviceStreams()); + dialog.setStreamIndex(m_settings.m_streamIndex); + } dialog.move(p); dialog.exec(); @@ -290,20 +297,14 @@ void DSDDemodGUI::onMenuDialogCalled(const QPoint &p) setTitle(m_channelMarker.getTitle()); setTitleColor(m_settings.m_rgbColor); - applySettings(); - } - else if ((m_contextMenuType == ContextMenuStreamSettings) && (m_deviceUISet->m_deviceMIMOEngine)) - { - DeviceStreamSelectionDialog dialog(this); - dialog.setNumberOfStreams(m_dsdDemod->getNumberOfDeviceStreams()); - dialog.setStreamIndex(m_settings.m_streamIndex); - dialog.move(p); - dialog.exec(); + if (m_deviceUISet->m_deviceMIMOEngine) + { + m_settings.m_streamIndex = dialog.getSelectedStreamIndex(); + m_channelMarker.clearStreamIndexes(); + m_channelMarker.addStreamIndex(m_settings.m_streamIndex); + updateIndexLabel(); + } - m_settings.m_streamIndex = dialog.getSelectedStreamIndex(); - m_channelMarker.clearStreamIndexes(); - m_channelMarker.addStreamIndex(m_settings.m_streamIndex); - displayStreamIndex(); applySettings(); } @@ -478,22 +479,13 @@ void DSDDemodGUI::displaySettings() ui->traceDecayText->setText(QString("%1").arg(m_settings.m_traceDecay)); m_scopeVisXY->setDecay(m_settings.m_traceDecay); - displayStreamIndex(); + updateIndexLabel(); getRollupContents()->restoreState(m_rollupState); updateAbsoluteCenterFrequency(); blockApplySettings(false); } -void DSDDemodGUI::displayStreamIndex() -{ - if (m_deviceUISet->m_deviceMIMOEngine) { - setStreamIndicator(tr("%1").arg(m_settings.m_streamIndex)); - } else { - setStreamIndicator("S"); // single channel indicator - } -} - void DSDDemodGUI::applySettings(bool force) { if (m_doApplySettings) diff --git a/plugins/channelrx/demoddsd/dsddemodgui.h b/plugins/channelrx/demoddsd/dsddemodgui.h index 618a853fc..27589f019 100644 --- a/plugins/channelrx/demoddsd/dsddemodgui.h +++ b/plugins/channelrx/demoddsd/dsddemodgui.h @@ -61,6 +61,8 @@ public: virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } virtual bool getHidden() const { return m_settings.m_hidden; } virtual ChannelMarker& getChannelMarker() { return m_channelMarker; } + virtual int getStreamIndex() const { return m_settings.m_streamIndex; } + virtual void setStreamIndex(int streamIndex) { m_settings.m_streamIndex = streamIndex; } public slots: void channelMarkerChangedByCursor(); @@ -112,7 +114,6 @@ private: void blockApplySettings(bool block); void applySettings(bool force = false); void displaySettings(); - void displayStreamIndex(); void updateMyPosition(); bool handleMessage(const Message& message); void makeUIConnections(); diff --git a/plugins/channelrx/demodfreedv/freedvdemodgui.cpp b/plugins/channelrx/demodfreedv/freedvdemodgui.cpp index a1fed8784..0e46817b6 100644 --- a/plugins/channelrx/demodfreedv/freedvdemodgui.cpp +++ b/plugins/channelrx/demodfreedv/freedvdemodgui.cpp @@ -206,6 +206,13 @@ void FreeDVDemodGUI::onMenuDialogCalled(const QPoint &p) dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); dialog.setReverseAPIChannelIndex(m_settings.m_reverseAPIChannelIndex); + dialog.setDefaultTitle(m_displayedName); + + if (m_deviceUISet->m_deviceMIMOEngine) + { + dialog.setNumberOfStreams(m_freeDVDemod->getNumberOfDeviceStreams()); + dialog.setStreamIndex(m_settings.m_streamIndex); + } dialog.move(p); dialog.exec(); @@ -222,20 +229,14 @@ void FreeDVDemodGUI::onMenuDialogCalled(const QPoint &p) setTitle(m_channelMarker.getTitle()); setTitleColor(m_settings.m_rgbColor); - applySettings(); - } - else if ((m_contextMenuType == ContextMenuStreamSettings) && (m_deviceUISet->m_deviceMIMOEngine)) - { - DeviceStreamSelectionDialog dialog(this); - dialog.setNumberOfStreams(m_freeDVDemod->getNumberOfDeviceStreams()); - dialog.setStreamIndex(m_settings.m_streamIndex); - dialog.move(p); - dialog.exec(); + if (m_deviceUISet->m_deviceMIMOEngine) + { + m_settings.m_streamIndex = dialog.getSelectedStreamIndex(); + m_channelMarker.clearStreamIndexes(); + m_channelMarker.addStreamIndex(m_settings.m_streamIndex); + updateIndexLabel(); + } - m_settings.m_streamIndex = dialog.getSelectedStreamIndex(); - m_channelMarker.clearStreamIndexes(); - m_channelMarker.addStreamIndex(m_settings.m_streamIndex); - displayStreamIndex(); applySettings(); } @@ -406,22 +407,13 @@ void FreeDVDemodGUI::displaySettings() ui->volumeIn->setValue(m_settings.m_volumeIn * 10.0); ui->volumeInText->setText(QString("%1").arg(m_settings.m_volumeIn, 0, 'f', 1)); - displayStreamIndex(); + updateIndexLabel(); getRollupContents()->restoreState(m_rollupState); updateAbsoluteCenterFrequency(); blockApplySettings(false); } -void FreeDVDemodGUI::displayStreamIndex() -{ - if (m_deviceUISet->m_deviceMIMOEngine) { - setStreamIndicator(tr("%1").arg(m_settings.m_streamIndex)); - } else { - setStreamIndicator("S"); // single channel indicator - } -} - void FreeDVDemodGUI::leaveEvent(QEvent*) { m_channelMarker.setHighlighted(false); diff --git a/plugins/channelrx/demodfreedv/freedvdemodgui.h b/plugins/channelrx/demodfreedv/freedvdemodgui.h index 4edb7d8fd..8d9be693d 100644 --- a/plugins/channelrx/demodfreedv/freedvdemodgui.h +++ b/plugins/channelrx/demodfreedv/freedvdemodgui.h @@ -59,6 +59,8 @@ public: virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } virtual bool getHidden() const { return m_settings.m_hidden; } virtual ChannelMarker& getChannelMarker() { return m_channelMarker; } + virtual int getStreamIndex() const { return m_settings.m_streamIndex; } + virtual void setStreamIndex(int streamIndex) { m_settings.m_streamIndex = streamIndex; } public slots: void channelMarkerChangedByCursor(); @@ -97,7 +99,6 @@ private: void applyBandwidths(int spanLog2, bool force = false); void displayBandwidths(int spanLog2); void displaySettings(); - void displayStreamIndex(); bool handleMessage(const Message& message); void makeUIConnections(); void updateAbsoluteCenterFrequency(); diff --git a/plugins/channelrx/demodnfm/nfmdemodgui.cpp b/plugins/channelrx/demodnfm/nfmdemodgui.cpp index e975c1b3d..a6e670963 100644 --- a/plugins/channelrx/demodnfm/nfmdemodgui.cpp +++ b/plugins/channelrx/demodnfm/nfmdemodgui.cpp @@ -306,6 +306,14 @@ void NFMDemodGUI::onMenuDialogCalled(const QPoint &p) dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); dialog.setReverseAPIChannelIndex(m_settings.m_reverseAPIChannelIndex); + dialog.setDefaultTitle(m_displayedName); + + if (m_deviceUISet->m_deviceMIMOEngine) + { + dialog.setNumberOfStreams(m_nfmDemod->getNumberOfDeviceStreams()); + dialog.setStreamIndex(m_settings.m_streamIndex); + } + dialog.move(p); dialog.exec(); @@ -321,20 +329,14 @@ void NFMDemodGUI::onMenuDialogCalled(const QPoint &p) setTitle(m_channelMarker.getTitle()); setTitleColor(m_settings.m_rgbColor); - applySettings(); - } - else if ((m_contextMenuType == ContextMenuStreamSettings) && (m_deviceUISet->m_deviceMIMOEngine)) - { - DeviceStreamSelectionDialog dialog(this); - dialog.setNumberOfStreams(m_nfmDemod->getNumberOfDeviceStreams()); - dialog.setStreamIndex(m_settings.m_streamIndex); - dialog.move(p); - dialog.exec(); + if (m_deviceUISet->m_deviceMIMOEngine) + { + m_settings.m_streamIndex = dialog.getSelectedStreamIndex(); + m_channelMarker.clearStreamIndexes(); + m_channelMarker.addStreamIndex(m_settings.m_streamIndex); + updateIndexLabel(); + } - m_settings.m_streamIndex = dialog.getSelectedStreamIndex(); - m_channelMarker.clearStreamIndexes(); - m_channelMarker.addStreamIndex(m_settings.m_streamIndex); - displayStreamIndex(); applySettings(); } @@ -523,22 +525,13 @@ void NFMDemodGUI::displaySettings() } setDcsCode(m_reportedDcsCode); - displayStreamIndex(); + updateIndexLabel(); getRollupContents()->restoreState(m_rollupState); updateAbsoluteCenterFrequency(); blockApplySettings(false); } -void NFMDemodGUI::displayStreamIndex() -{ - if (m_deviceUISet->m_deviceMIMOEngine) { - setStreamIndicator(tr("%1").arg(m_settings.m_streamIndex)); - } else { - setStreamIndicator("S"); // single channel indicator - } -} - void NFMDemodGUI::leaveEvent(QEvent*) { m_channelMarker.setHighlighted(false); diff --git a/plugins/channelrx/demodnfm/nfmdemodgui.h b/plugins/channelrx/demodnfm/nfmdemodgui.h index e1681c039..e320900d6 100644 --- a/plugins/channelrx/demodnfm/nfmdemodgui.h +++ b/plugins/channelrx/demodnfm/nfmdemodgui.h @@ -39,6 +39,8 @@ public: virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } virtual bool getHidden() const { return m_settings.m_hidden; } virtual ChannelMarker& getChannelMarker() { return m_channelMarker; } + virtual int getStreamIndex() const { return m_settings.m_streamIndex; } + virtual void setStreamIndex(int streamIndex) { m_settings.m_streamIndex = streamIndex; } public slots: void channelMarkerChangedByCursor(); @@ -70,7 +72,6 @@ private: void blockApplySettings(bool block); void applySettings(bool force = false); void displaySettings(); - void displayStreamIndex(); void setCtcssFreq(Real ctcssFreq); void setDcsCode(unsigned int dcsCode); bool handleMessage(const Message& message); diff --git a/plugins/channelrx/demodpacket/packetdemodgui.cpp b/plugins/channelrx/demodpacket/packetdemodgui.cpp index 3490a5dc3..afae8ef80 100644 --- a/plugins/channelrx/demodpacket/packetdemodgui.cpp +++ b/plugins/channelrx/demodpacket/packetdemodgui.cpp @@ -388,6 +388,14 @@ void PacketDemodGUI::onMenuDialogCalled(const QPoint &p) dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); dialog.setReverseAPIChannelIndex(m_settings.m_reverseAPIChannelIndex); + dialog.setDefaultTitle(m_displayedName); + + if (m_deviceUISet->m_deviceMIMOEngine) + { + dialog.setNumberOfStreams(m_packetDemod->getNumberOfDeviceStreams()); + dialog.setStreamIndex(m_settings.m_streamIndex); + } + dialog.move(p); dialog.exec(); @@ -403,20 +411,14 @@ void PacketDemodGUI::onMenuDialogCalled(const QPoint &p) setTitle(m_channelMarker.getTitle()); setTitleColor(m_settings.m_rgbColor); - applySettings(); - } - else if ((m_contextMenuType == ContextMenuStreamSettings) && (m_deviceUISet->m_deviceMIMOEngine)) - { - DeviceStreamSelectionDialog dialog(this); - dialog.setNumberOfStreams(m_packetDemod->getNumberOfDeviceStreams()); - dialog.setStreamIndex(m_settings.m_streamIndex); - dialog.move(p); - dialog.exec(); + if (m_deviceUISet->m_deviceMIMOEngine) + { + m_settings.m_streamIndex = dialog.getSelectedStreamIndex(); + m_channelMarker.clearStreamIndexes(); + m_channelMarker.addStreamIndex(m_settings.m_streamIndex); + updateIndexLabel(); + } - m_settings.m_streamIndex = dialog.getSelectedStreamIndex(); - m_channelMarker.clearStreamIndexes(); - m_channelMarker.addStreamIndex(m_settings.m_streamIndex); - displayStreamIndex(); applySettings(); } @@ -535,7 +537,7 @@ void PacketDemodGUI::displaySettings() ui->fmDevText->setText(QString("%1k").arg(m_settings.m_fmDeviation / 1000.0, 0, 'f', 1)); ui->fmDev->setValue(m_settings.m_fmDeviation / 100.0); - displayStreamIndex(); + updateIndexLabel(); ui->filterFrom->setText(m_settings.m_filterFrom); ui->filterTo->setText(m_settings.m_filterTo); @@ -567,15 +569,6 @@ void PacketDemodGUI::displaySettings() blockApplySettings(false); } -void PacketDemodGUI::displayStreamIndex() -{ - if (m_deviceUISet->m_deviceMIMOEngine) { - setStreamIndicator(tr("%1").arg(m_settings.m_streamIndex)); - } else { - setStreamIndicator("S"); // single channel indicator - } -} - void PacketDemodGUI::leaveEvent(QEvent*) { m_channelMarker.setHighlighted(false); diff --git a/plugins/channelrx/demodpacket/packetdemodgui.h b/plugins/channelrx/demodpacket/packetdemodgui.h index 80f860986..613692a8b 100644 --- a/plugins/channelrx/demodpacket/packetdemodgui.h +++ b/plugins/channelrx/demodpacket/packetdemodgui.h @@ -67,6 +67,8 @@ public: virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } virtual bool getHidden() const { return m_settings.m_hidden; } virtual ChannelMarker& getChannelMarker() { return m_channelMarker; } + virtual int getStreamIndex() const { return m_settings.m_streamIndex; } + virtual void setStreamIndex(int streamIndex) { m_settings.m_streamIndex = streamIndex; } public slots: void channelMarkerChangedByCursor(); @@ -95,7 +97,6 @@ private: void blockApplySettings(bool block); void applySettings(bool force = false); void displaySettings(); - void displayStreamIndex(); void packetReceived(QByteArray packet); bool handleMessage(const Message& message); void makeUIConnections(); diff --git a/plugins/channelrx/demodpager/pagerdemodgui.cpp b/plugins/channelrx/demodpager/pagerdemodgui.cpp index 97ca224dd..9989ba94b 100644 --- a/plugins/channelrx/demodpager/pagerdemodgui.cpp +++ b/plugins/channelrx/demodpager/pagerdemodgui.cpp @@ -445,6 +445,14 @@ void PagerDemodGUI::onMenuDialogCalled(const QPoint &p) dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); dialog.setReverseAPIChannelIndex(m_settings.m_reverseAPIChannelIndex); + dialog.setDefaultTitle(m_displayedName); + + if (m_deviceUISet->m_deviceMIMOEngine) + { + dialog.setNumberOfStreams(m_pagerDemod->getNumberOfDeviceStreams()); + dialog.setStreamIndex(m_settings.m_streamIndex); + } + dialog.move(p); dialog.exec(); @@ -460,20 +468,14 @@ void PagerDemodGUI::onMenuDialogCalled(const QPoint &p) setTitle(m_channelMarker.getTitle()); setTitleColor(m_settings.m_rgbColor); - applySettings(); - } - else if ((m_contextMenuType == ContextMenuStreamSettings) && (m_deviceUISet->m_deviceMIMOEngine)) - { - DeviceStreamSelectionDialog dialog(this); - dialog.setNumberOfStreams(m_pagerDemod->getNumberOfDeviceStreams()); - dialog.setStreamIndex(m_settings.m_streamIndex); - dialog.move(p); - dialog.exec(); + if (m_deviceUISet->m_deviceMIMOEngine) + { + m_settings.m_streamIndex = dialog.getSelectedStreamIndex(); + m_channelMarker.clearStreamIndexes(); + m_channelMarker.addStreamIndex(m_settings.m_streamIndex); + updateIndexLabel(); + } - m_settings.m_streamIndex = dialog.getSelectedStreamIndex(); - m_channelMarker.clearStreamIndexes(); - m_channelMarker.addStreamIndex(m_settings.m_streamIndex); - displayStreamIndex(); applySettings(); } @@ -632,7 +634,7 @@ void PagerDemodGUI::displaySettings() ui->fmDevText->setText(QString("%1k").arg(m_settings.m_fmDeviation / 1000.0, 0, 'f', 1)); ui->fmDev->setValue(m_settings.m_fmDeviation / 100.0); - displayStreamIndex(); + updateIndexLabel(); ui->filterAddress->setText(m_settings.m_filterAddress); @@ -666,15 +668,6 @@ void PagerDemodGUI::displaySettings() blockApplySettings(false); } -void PagerDemodGUI::displayStreamIndex() -{ - if (m_deviceUISet->m_deviceMIMOEngine) { - setStreamIndicator(tr("%1").arg(m_settings.m_streamIndex)); - } else { - setStreamIndicator("S"); // single channel indicator - } -} - void PagerDemodGUI::leaveEvent(QEvent*) { m_channelMarker.setHighlighted(false); diff --git a/plugins/channelrx/demodpager/pagerdemodgui.h b/plugins/channelrx/demodpager/pagerdemodgui.h index b4b3b82e4..f42f136be 100644 --- a/plugins/channelrx/demodpager/pagerdemodgui.h +++ b/plugins/channelrx/demodpager/pagerdemodgui.h @@ -61,6 +61,8 @@ public: virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } virtual bool getHidden() const { return m_settings.m_hidden; } virtual ChannelMarker& getChannelMarker() { return m_channelMarker; } + virtual int getStreamIndex() const { return m_settings.m_streamIndex; } + virtual void setStreamIndex(int streamIndex) { m_settings.m_streamIndex = streamIndex; } public slots: void channelMarkerChangedByCursor(); @@ -90,7 +92,6 @@ private: void blockApplySettings(bool block); void applySettings(bool force = false); void displaySettings(); - void displayStreamIndex(); void messageReceived(const QDateTime dateTime, int address, int functionBits, const QString &numericMessage, const QString &alphaMessage, int evenParityErrors, int bchParityErrors); diff --git a/plugins/channelrx/demodradiosonde/radiosondedemodgui.cpp b/plugins/channelrx/demodradiosonde/radiosondedemodgui.cpp index 14c093e8f..1a76963fb 100644 --- a/plugins/channelrx/demodradiosonde/radiosondedemodgui.cpp +++ b/plugins/channelrx/demodradiosonde/radiosondedemodgui.cpp @@ -502,6 +502,14 @@ void RadiosondeDemodGUI::onMenuDialogCalled(const QPoint &p) dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); dialog.setReverseAPIChannelIndex(m_settings.m_reverseAPIChannelIndex); + dialog.setDefaultTitle(m_displayedName); + + if (m_deviceUISet->m_deviceMIMOEngine) + { + dialog.setNumberOfStreams(m_radiosondeDemod->getNumberOfDeviceStreams()); + dialog.setStreamIndex(m_settings.m_streamIndex); + } + dialog.move(p); dialog.exec(); @@ -517,20 +525,14 @@ void RadiosondeDemodGUI::onMenuDialogCalled(const QPoint &p) setTitle(m_channelMarker.getTitle()); setTitleColor(m_settings.m_rgbColor); - applySettings(); - } - else if ((m_contextMenuType == ContextMenuStreamSettings) && (m_deviceUISet->m_deviceMIMOEngine)) - { - DeviceStreamSelectionDialog dialog(this); - dialog.setNumberOfStreams(m_radiosondeDemod->getNumberOfDeviceStreams()); - dialog.setStreamIndex(m_settings.m_streamIndex); - dialog.move(p); - dialog.exec(); + if (m_deviceUISet->m_deviceMIMOEngine) + { + m_settings.m_streamIndex = dialog.getSelectedStreamIndex(); + m_channelMarker.clearStreamIndexes(); + m_channelMarker.addStreamIndex(m_settings.m_streamIndex); + updateIndexLabel(); + } - m_settings.m_streamIndex = dialog.getSelectedStreamIndex(); - m_channelMarker.clearStreamIndexes(); - m_channelMarker.addStreamIndex(m_settings.m_streamIndex); - displayStreamIndex(); applySettings(); } @@ -737,7 +739,7 @@ void RadiosondeDemodGUI::displaySettings() ui->thresholdText->setText(QString("%1").arg(m_settings.m_correlationThreshold)); ui->threshold->setValue(m_settings.m_correlationThreshold); - displayStreamIndex(); + updateIndexLabel(); ui->filterSerial->setText(m_settings.m_filterSerial); @@ -770,15 +772,6 @@ void RadiosondeDemodGUI::displaySettings() blockApplySettings(false); } -void RadiosondeDemodGUI::displayStreamIndex() -{ - if (m_deviceUISet->m_deviceMIMOEngine) { - setStreamIndicator(tr("%1").arg(m_settings.m_streamIndex)); - } else { - setStreamIndicator("S"); // single channel indicator - } -} - void RadiosondeDemodGUI::leaveEvent(QEvent*) { m_channelMarker.setHighlighted(false); diff --git a/plugins/channelrx/demodradiosonde/radiosondedemodgui.h b/plugins/channelrx/demodradiosonde/radiosondedemodgui.h index 1958c2503..ca3879c8b 100644 --- a/plugins/channelrx/demodradiosonde/radiosondedemodgui.h +++ b/plugins/channelrx/demodradiosonde/radiosondedemodgui.h @@ -62,6 +62,8 @@ public: virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } virtual bool getHidden() const { return m_settings.m_hidden; } virtual ChannelMarker& getChannelMarker() { return m_channelMarker; } + virtual int getStreamIndex() const { return m_settings.m_streamIndex; } + virtual void setStreamIndex(int streamIndex) { m_settings.m_streamIndex = streamIndex; } public slots: void channelMarkerChangedByCursor(); @@ -94,7 +96,6 @@ private: void blockApplySettings(bool block); void applySettings(bool force = false); void displaySettings(); - void displayStreamIndex(); void frameReceived(const QByteArray& frame, const QDateTime& dateTime, int errorsCorrected, int threshold); bool handleMessage(const Message& message); void makeUIConnections(); diff --git a/plugins/channelrx/demodssb/ssbdemodgui.cpp b/plugins/channelrx/demodssb/ssbdemodgui.cpp index f4b4392a9..05eba5be5 100644 --- a/plugins/channelrx/demodssb/ssbdemodgui.cpp +++ b/plugins/channelrx/demodssb/ssbdemodgui.cpp @@ -246,6 +246,13 @@ void SSBDemodGUI::onMenuDialogCalled(const QPoint &p) dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); dialog.setReverseAPIChannelIndex(m_settings.m_reverseAPIChannelIndex); + dialog.setDefaultTitle(m_displayedName); + + if (m_deviceUISet->m_deviceMIMOEngine) + { + dialog.setNumberOfStreams(m_ssbDemod->getNumberOfDeviceStreams()); + dialog.setStreamIndex(m_settings.m_streamIndex); + } dialog.move(p); dialog.exec(); @@ -262,20 +269,14 @@ void SSBDemodGUI::onMenuDialogCalled(const QPoint &p) setTitle(m_channelMarker.getTitle()); setTitleColor(m_settings.m_rgbColor); - applySettings(); - } - else if ((m_contextMenuType == ContextMenuStreamSettings) && (m_deviceUISet->m_deviceMIMOEngine)) - { - DeviceStreamSelectionDialog dialog(this); - dialog.setNumberOfStreams(m_ssbDemod->getNumberOfDeviceStreams()); - dialog.setStreamIndex(m_settings.m_streamIndex); - dialog.move(p); - dialog.exec(); + if (m_deviceUISet->m_deviceMIMOEngine) + { + m_settings.m_streamIndex = dialog.getSelectedStreamIndex(); + m_channelMarker.clearStreamIndexes(); + m_channelMarker.addStreamIndex(m_settings.m_streamIndex); + updateIndexLabel(); + } - m_settings.m_streamIndex = dialog.getSelectedStreamIndex(); - m_channelMarker.clearStreamIndexes(); - m_channelMarker.addStreamIndex(m_settings.m_streamIndex); - displayStreamIndex(); applySettings(); } @@ -588,22 +589,13 @@ void SSBDemodGUI::displaySettings() displayAGCPowerThreshold(ui->agcPowerThreshold->value()); displayAGCThresholdGate(m_settings.m_agcThresholdGate); - displayStreamIndex(); + updateIndexLabel(); getRollupContents()->restoreState(m_rollupState); updateAbsoluteCenterFrequency(); blockApplySettings(false); } -void SSBDemodGUI::displayStreamIndex() -{ - if (m_deviceUISet->m_deviceMIMOEngine) { - setStreamIndicator(tr("%1").arg(m_settings.m_streamIndex)); - } else { - setStreamIndicator("S"); // single channel indicator - } -} - void SSBDemodGUI::displayAGCPowerThreshold(int value) { if (value == SSBDemodSettings::m_minPowerThresholdDB) diff --git a/plugins/channelrx/demodssb/ssbdemodgui.h b/plugins/channelrx/demodssb/ssbdemodgui.h index 3134732f5..6a193f09e 100644 --- a/plugins/channelrx/demodssb/ssbdemodgui.h +++ b/plugins/channelrx/demodssb/ssbdemodgui.h @@ -42,6 +42,8 @@ public: virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } virtual bool getHidden() const { return m_settings.m_hidden; } virtual ChannelMarker& getChannelMarker() { return m_channelMarker; } + virtual int getStreamIndex() const { return m_settings.m_streamIndex; } + virtual void setStreamIndex(int streamIndex) { m_settings.m_streamIndex = streamIndex; } public slots: void channelMarkerChangedByCursor(); @@ -80,7 +82,6 @@ private: void applyBandwidths(unsigned int spanLog2, bool force = false); unsigned int spanLog2Max(); void displaySettings(); - void displayStreamIndex(); void displayAGCPowerThreshold(int value); void displayAGCThresholdGate(int value); bool handleMessage(const Message& message); diff --git a/plugins/channelrx/demodvor/vordemodgui.cpp b/plugins/channelrx/demodvor/vordemodgui.cpp index b91b77238..5c96af2d6 100644 --- a/plugins/channelrx/demodvor/vordemodgui.cpp +++ b/plugins/channelrx/demodvor/vordemodgui.cpp @@ -1121,6 +1121,14 @@ void VORDemodGUI::onMenuDialogCalled(const QPoint &p) dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); dialog.setReverseAPIChannelIndex(m_settings.m_reverseAPIChannelIndex); + dialog.setDefaultTitle(m_displayedName); + + if (m_deviceUISet->m_deviceMIMOEngine) + { + dialog.setNumberOfStreams(m_vorDemod->getNumberOfDeviceStreams()); + dialog.setStreamIndex(m_settings.m_streamIndex); + } + dialog.move(p); dialog.exec(); @@ -1136,20 +1144,14 @@ void VORDemodGUI::onMenuDialogCalled(const QPoint &p) setTitle(m_channelMarker.getTitle()); setTitleColor(m_settings.m_rgbColor); - applySettings(); - } - else if ((m_contextMenuType == ContextMenuStreamSettings) && (m_deviceUISet->m_deviceMIMOEngine)) - { - DeviceStreamSelectionDialog dialog(this); - dialog.setNumberOfStreams(m_vorDemod->getNumberOfDeviceStreams()); - dialog.setStreamIndex(m_settings.m_streamIndex); - dialog.move(p); - dialog.exec(); + if (m_deviceUISet->m_deviceMIMOEngine) + { + m_settings.m_streamIndex = dialog.getSelectedStreamIndex(); + m_channelMarker.clearStreamIndexes(); + m_channelMarker.addStreamIndex(m_settings.m_streamIndex); + updateIndexLabel(); + } - m_settings.m_streamIndex = dialog.getSelectedStreamIndex(); - m_channelMarker.clearStreamIndexes(); - m_channelMarker.addStreamIndex(m_settings.m_streamIndex); - displayStreamIndex(); applySettings(); } @@ -1325,7 +1327,7 @@ void VORDemodGUI::displaySettings() ui->audioMute->setChecked(m_settings.m_audioMute); - displayStreamIndex(); + updateIndexLabel(); // Order and size columns QHeaderView *header = ui->vorData->horizontalHeader(); @@ -1343,15 +1345,6 @@ void VORDemodGUI::displaySettings() blockApplySettings(false); } -void VORDemodGUI::displayStreamIndex() -{ - if (m_deviceUISet->m_deviceMIMOEngine) { - setStreamIndicator(tr("%1").arg(m_settings.m_streamIndex)); - } else { - setStreamIndicator("S"); // single channel indicator - } -} - void VORDemodGUI::leaveEvent(QEvent*) { m_channelMarker.setHighlighted(false); diff --git a/plugins/channelrx/demodvor/vordemodgui.h b/plugins/channelrx/demodvor/vordemodgui.h index f368a7b54..e1ee119da 100644 --- a/plugins/channelrx/demodvor/vordemodgui.h +++ b/plugins/channelrx/demodvor/vordemodgui.h @@ -220,6 +220,8 @@ public: virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } virtual bool getHidden() const { return m_settings.m_hidden; } virtual ChannelMarker& getChannelMarker() { return m_channelMarker; } + virtual int getStreamIndex() const { return m_settings.m_streamIndex; } + virtual void setStreamIndex(int streamIndex) { m_settings.m_streamIndex = streamIndex; } void selectVOR(VORGUI *vorGUI, bool selected); @@ -262,7 +264,6 @@ private: void blockApplySettings(bool block); void applySettings(bool force = false); void displaySettings(); - void displayStreamIndex(); bool handleMessage(const Message& message); void makeUIConnections(); void updateAbsoluteCenterFrequency(); diff --git a/plugins/channelrx/demodvorsc/vordemodscgui.cpp b/plugins/channelrx/demodvorsc/vordemodscgui.cpp index 9e741f7da..d0e7c0e0a 100644 --- a/plugins/channelrx/demodvorsc/vordemodscgui.cpp +++ b/plugins/channelrx/demodvorsc/vordemodscgui.cpp @@ -242,6 +242,14 @@ void VORDemodSCGUI::onMenuDialogCalled(const QPoint &p) dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); dialog.setReverseAPIChannelIndex(m_settings.m_reverseAPIChannelIndex); + dialog.setDefaultTitle(m_displayedName); + + if (m_deviceUISet->m_deviceMIMOEngine) + { + dialog.setNumberOfStreams(m_vorDemod->getNumberOfDeviceStreams()); + dialog.setStreamIndex(m_settings.m_streamIndex); + } + dialog.move(p); dialog.exec(); @@ -257,20 +265,14 @@ void VORDemodSCGUI::onMenuDialogCalled(const QPoint &p) setTitle(m_channelMarker.getTitle()); setTitleColor(m_settings.m_rgbColor); - applySettings(); - } - else if ((m_contextMenuType == ContextMenuStreamSettings) && (m_deviceUISet->m_deviceMIMOEngine)) - { - DeviceStreamSelectionDialog dialog(this); - dialog.setNumberOfStreams(m_vorDemod->getNumberOfDeviceStreams()); - dialog.setStreamIndex(m_settings.m_streamIndex); - dialog.move(p); - dialog.exec(); + if (m_deviceUISet->m_deviceMIMOEngine) + { + m_settings.m_streamIndex = dialog.getSelectedStreamIndex(); + m_channelMarker.clearStreamIndexes(); + m_channelMarker.addStreamIndex(m_settings.m_streamIndex); + updateIndexLabel(); + } - m_settings.m_streamIndex = dialog.getSelectedStreamIndex(); - m_channelMarker.clearStreamIndexes(); - m_channelMarker.addStreamIndex(m_settings.m_streamIndex); - displayStreamIndex(); applySettings(); } @@ -379,22 +381,13 @@ void VORDemodSCGUI::displaySettings() ui->audioMute->setChecked(m_settings.m_audioMute); - displayStreamIndex(); + updateIndexLabel(); getRollupContents()->restoreState(m_rollupState); updateAbsoluteCenterFrequency(); blockApplySettings(false); } -void VORDemodSCGUI::displayStreamIndex() -{ - if (m_deviceUISet->m_deviceMIMOEngine) { - setStreamIndicator(tr("%1").arg(m_settings.m_streamIndex)); - } else { - setStreamIndicator("S"); // single channel indicator - } -} - void VORDemodSCGUI::leaveEvent(QEvent*) { m_channelMarker.setHighlighted(false); diff --git a/plugins/channelrx/demodvorsc/vordemodscgui.h b/plugins/channelrx/demodvorsc/vordemodscgui.h index 557a2775d..058d36c20 100644 --- a/plugins/channelrx/demodvorsc/vordemodscgui.h +++ b/plugins/channelrx/demodvorsc/vordemodscgui.h @@ -57,6 +57,8 @@ public: virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } virtual bool getHidden() const { return m_settings.m_hidden; } virtual ChannelMarker& getChannelMarker() { return m_channelMarker; } + virtual int getStreamIndex() const { return m_settings.m_streamIndex; } + virtual void setStreamIndex(int streamIndex) { m_settings.m_streamIndex = streamIndex; } public slots: void channelMarkerChangedByCursor(); @@ -84,7 +86,6 @@ private: void blockApplySettings(bool block); void applySettings(bool force = false); void displaySettings(); - void displayStreamIndex(); bool handleMessage(const Message& message); void makeUIConnections(); void updateAbsoluteCenterFrequency(); diff --git a/plugins/channelrx/demodwfm/wfmdemodgui.cpp b/plugins/channelrx/demodwfm/wfmdemodgui.cpp index 56264e6ad..50a5b723f 100644 --- a/plugins/channelrx/demodwfm/wfmdemodgui.cpp +++ b/plugins/channelrx/demodwfm/wfmdemodgui.cpp @@ -169,6 +169,13 @@ void WFMDemodGUI::onMenuDialogCalled(const QPoint &p) dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); dialog.setReverseAPIChannelIndex(m_settings.m_reverseAPIChannelIndex); + dialog.setDefaultTitle(m_displayedName); + + if (m_deviceUISet->m_deviceMIMOEngine) + { + dialog.setNumberOfStreams(m_wfmDemod->getNumberOfDeviceStreams()); + dialog.setStreamIndex(m_settings.m_streamIndex); + } dialog.move(p); dialog.exec(); @@ -185,20 +192,14 @@ void WFMDemodGUI::onMenuDialogCalled(const QPoint &p) setTitle(m_channelMarker.getTitle()); setTitleColor(m_settings.m_rgbColor); - applySettings(); - } - else if ((m_contextMenuType == ContextMenuStreamSettings) && (m_deviceUISet->m_deviceMIMOEngine)) - { - DeviceStreamSelectionDialog dialog(this); - dialog.setNumberOfStreams(m_wfmDemod->getNumberOfDeviceStreams()); - dialog.setStreamIndex(m_settings.m_streamIndex); - dialog.move(p); - dialog.exec(); + if (m_deviceUISet->m_deviceMIMOEngine) + { + m_settings.m_streamIndex = dialog.getSelectedStreamIndex(); + m_channelMarker.clearStreamIndexes(); + m_channelMarker.addStreamIndex(m_settings.m_streamIndex); + updateIndexLabel(); + } - m_settings.m_streamIndex = dialog.getSelectedStreamIndex(); - m_channelMarker.clearStreamIndexes(); - m_channelMarker.addStreamIndex(m_settings.m_streamIndex); - displayStreamIndex(); applySettings(); } @@ -307,22 +308,13 @@ void WFMDemodGUI::displaySettings() ui->squelchText->setText(QString("%1 dB").arg(m_settings.m_squelch)); ui->audioMute->setChecked(m_settings.m_audioMute); - displayStreamIndex(); + updateIndexLabel(); getRollupContents()->restoreState(m_rollupState); updateAbsoluteCenterFrequency(); blockApplySettings(false); } -void WFMDemodGUI::displayStreamIndex() -{ - if (m_deviceUISet->m_deviceMIMOEngine) { - setStreamIndicator(tr("%1").arg(m_settings.m_streamIndex)); - } else { - setStreamIndicator("S"); // single channel indicator - } -} - void WFMDemodGUI::leaveEvent(QEvent*) { m_channelMarker.setHighlighted(false); diff --git a/plugins/channelrx/demodwfm/wfmdemodgui.h b/plugins/channelrx/demodwfm/wfmdemodgui.h index 772b4d6f0..e456cd5fc 100644 --- a/plugins/channelrx/demodwfm/wfmdemodgui.h +++ b/plugins/channelrx/demodwfm/wfmdemodgui.h @@ -37,6 +37,8 @@ public: virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } virtual bool getHidden() const { return m_settings.m_hidden; } virtual ChannelMarker& getChannelMarker() { return m_channelMarker; } + virtual int getStreamIndex() const { return m_settings.m_streamIndex; } + virtual void setStreamIndex(int streamIndex) { m_settings.m_streamIndex = streamIndex; } public slots: void channelMarkerChangedByCursor(); @@ -66,7 +68,6 @@ private: void blockApplySettings(bool block); void applySettings(bool force = false); void displaySettings(); - void displayStreamIndex(); bool handleMessage(const Message& message); void makeUIConnections(); void updateAbsoluteCenterFrequency(); diff --git a/plugins/channelrx/filesink/filesinkgui.cpp b/plugins/channelrx/filesink/filesinkgui.cpp index 42c0e0baa..04f51aa65 100644 --- a/plugins/channelrx/filesink/filesinkgui.cpp +++ b/plugins/channelrx/filesink/filesinkgui.cpp @@ -284,7 +284,7 @@ void FileSinkGUI::displaySettings() ui->squelchLevel->setStyleSheet("QDial { background:rgb(79,79,79); }"); } - displayStreamIndex(); + updateIndexLabel(); setPosFromFrequency(); getRollupContents()->restoreState(m_rollupState); @@ -292,15 +292,6 @@ void FileSinkGUI::displaySettings() blockApplySettings(false); } -void FileSinkGUI::displayStreamIndex() -{ - if (m_deviceUISet->m_deviceMIMOEngine) { - setStreamIndicator(tr("%1").arg(m_settings.m_streamIndex)); - } else { - setStreamIndicator("S"); // single channel indicator - } -} - void FileSinkGUI::displayRate() { double channelSampleRate = ((double) m_basebandSampleRate) / (1<m_deviceMIMOEngine) + { + dialog.setNumberOfStreams(m_fileSink->getNumberOfDeviceStreams()); + dialog.setStreamIndex(m_settings.m_streamIndex); + } dialog.move(p); dialog.exec(); @@ -389,20 +387,14 @@ void FileSinkGUI::onMenuDialogCalled(const QPoint &p) setTitle(m_channelMarker.getTitle()); setTitleColor(m_settings.m_rgbColor); - applySettings(); - } - else if ((m_contextMenuType == ContextMenuStreamSettings) && (m_deviceUISet->m_deviceMIMOEngine)) - { - DeviceStreamSelectionDialog dialog(this); - dialog.setNumberOfStreams(m_fileSink->getNumberOfDeviceStreams()); - dialog.setStreamIndex(m_settings.m_streamIndex); - dialog.move(p); - dialog.exec(); + if (m_deviceUISet->m_deviceMIMOEngine) + { + m_settings.m_streamIndex = dialog.getSelectedStreamIndex(); + m_channelMarker.clearStreamIndexes(); + m_channelMarker.addStreamIndex(m_settings.m_streamIndex); + updateIndexLabel(); + } - m_settings.m_streamIndex = dialog.getSelectedStreamIndex(); - m_channelMarker.clearStreamIndexes(); - m_channelMarker.addStreamIndex(m_settings.m_streamIndex); - displayStreamIndex(); applySettings(); } diff --git a/plugins/channelrx/filesink/filesinkgui.h b/plugins/channelrx/filesink/filesinkgui.h index d16d37e0f..f38d0cbda 100644 --- a/plugins/channelrx/filesink/filesinkgui.h +++ b/plugins/channelrx/filesink/filesinkgui.h @@ -58,6 +58,8 @@ public: virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } virtual bool getHidden() const { return m_settings.m_hidden; } virtual ChannelMarker& getChannelMarker() { return m_channelMarker; } + virtual int getStreamIndex() const { return m_settings.m_streamIndex; } + virtual void setStreamIndex(int streamIndex) { m_settings.m_streamIndex = streamIndex; } public slots: void channelMarkerChangedByCursor(); @@ -91,7 +93,6 @@ private: void applySettings(bool force = false); void applyDecimation(); void displaySettings(); - void displayStreamIndex(); void displayRate(); void displayPos(); void setFrequencyFromPos(); diff --git a/plugins/channelrx/freqtracker/freqtrackergui.cpp b/plugins/channelrx/freqtracker/freqtrackergui.cpp index 378c1c426..7aa2512c6 100644 --- a/plugins/channelrx/freqtracker/freqtrackergui.cpp +++ b/plugins/channelrx/freqtracker/freqtrackergui.cpp @@ -270,6 +270,14 @@ void FreqTrackerGUI::onMenuDialogCalled(const QPoint &p) dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); dialog.setReverseAPIChannelIndex(m_settings.m_reverseAPIChannelIndex); + dialog.setDefaultTitle(m_displayedName); + + if (m_deviceUISet->m_deviceMIMOEngine) + { + dialog.setNumberOfStreams(m_freqTracker->getNumberOfDeviceStreams()); + dialog.setStreamIndex(m_settings.m_streamIndex); + } + dialog.move(p); dialog.exec(); @@ -285,20 +293,14 @@ void FreqTrackerGUI::onMenuDialogCalled(const QPoint &p) setTitle(m_channelMarker.getTitle()); setTitleColor(m_settings.m_rgbColor); - applySettings(); - } - else if ((m_contextMenuType == ContextMenuStreamSettings) && (m_deviceUISet->m_deviceMIMOEngine)) - { - DeviceStreamSelectionDialog dialog(this); - dialog.setNumberOfStreams(m_freqTracker->getNumberOfDeviceStreams()); - dialog.setStreamIndex(m_settings.m_streamIndex); - dialog.move(p); - dialog.exec(); + if (m_deviceUISet->m_deviceMIMOEngine) + { + m_settings.m_streamIndex = dialog.getSelectedStreamIndex(); + m_channelMarker.clearStreamIndexes(); + m_channelMarker.addStreamIndex(m_settings.m_streamIndex); + updateIndexLabel(); + } - m_settings.m_streamIndex = dialog.getSelectedStreamIndex(); - m_channelMarker.clearStreamIndexes(); - m_channelMarker.addStreamIndex(m_settings.m_streamIndex); - displayStreamIndex(); applySettings(); } @@ -439,7 +441,7 @@ void FreqTrackerGUI::displaySettings() ui->squelchGate->setValue(m_settings.m_squelchGate); displaySpectrumBandwidth(m_settings.m_spanLog2); - displayStreamIndex(); + updateIndexLabel(); getRollupContents()->restoreState(m_rollupState); updateAbsoluteCenterFrequency(); @@ -463,15 +465,6 @@ void FreqTrackerGUI::displaySpectrumBandwidth(int spanLog2) ui->glSpectrum->setSampleRate(spectrumRate); } -void FreqTrackerGUI::displayStreamIndex() -{ - if (m_deviceUISet->m_deviceMIMOEngine) { - setStreamIndicator(tr("%1").arg(m_settings.m_streamIndex)); - } else { - setStreamIndicator("S"); // single channel indicator - } -} - void FreqTrackerGUI::leaveEvent(QEvent*) { m_channelMarker.setHighlighted(false); diff --git a/plugins/channelrx/freqtracker/freqtrackergui.h b/plugins/channelrx/freqtracker/freqtrackergui.h index 702b61c25..5f590ac81 100644 --- a/plugins/channelrx/freqtracker/freqtrackergui.h +++ b/plugins/channelrx/freqtracker/freqtrackergui.h @@ -58,6 +58,8 @@ public: virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } virtual bool getHidden() const { return m_settings.m_hidden; } virtual ChannelMarker& getChannelMarker() { return m_channelMarker; } + virtual int getStreamIndex() const { return m_settings.m_streamIndex; } + virtual void setStreamIndex(int streamIndex) { m_settings.m_streamIndex = streamIndex; } public slots: void channelMarkerChangedByCursor(); @@ -89,7 +91,6 @@ private: void applySpectrumBandwidth(int spanLog2, bool force = false); void displaySettings(); void displaySpectrumBandwidth(int spanLog2); - void displayStreamIndex(); bool handleMessage(const Message& message); void makeUIConnections(); void updateAbsoluteCenterFrequency(); diff --git a/plugins/channelrx/localsink/localsinkgui.cpp b/plugins/channelrx/localsink/localsinkgui.cpp index 33dcea644..89d85d450 100644 --- a/plugins/channelrx/localsink/localsinkgui.cpp +++ b/plugins/channelrx/localsink/localsinkgui.cpp @@ -180,21 +180,12 @@ void LocalSinkGUI::displaySettings() ui->localDevicePlay->setChecked(m_settings.m_play); ui->decimationFactor->setCurrentIndex(m_settings.m_log2Decim); applyDecimation(); - displayStreamIndex(); + updateIndexLabel(); getRollupContents()->restoreState(m_rollupState); blockApplySettings(false); } -void LocalSinkGUI::displayStreamIndex() -{ - if (m_deviceUISet->m_deviceMIMOEngine) { - setStreamIndicator(tr("%1").arg(m_settings.m_streamIndex)); - } else { - setStreamIndicator("S"); // single channel indicator - } -} - void LocalSinkGUI::displayRateAndShift() { int shift = m_shiftFrequencyFactor * m_basebandSampleRate; @@ -274,6 +265,13 @@ void LocalSinkGUI::onMenuDialogCalled(const QPoint &p) dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); dialog.setReverseAPIChannelIndex(m_settings.m_reverseAPIChannelIndex); + dialog.setDefaultTitle(m_displayedName); + + if (m_deviceUISet->m_deviceMIMOEngine) + { + dialog.setNumberOfStreams(m_localSink->getNumberOfDeviceStreams()); + dialog.setStreamIndex(m_settings.m_streamIndex); + } dialog.move(p); dialog.exec(); @@ -290,20 +288,14 @@ void LocalSinkGUI::onMenuDialogCalled(const QPoint &p) setTitle(m_channelMarker.getTitle()); setTitleColor(m_settings.m_rgbColor); - applySettings(); - } - else if ((m_contextMenuType == ContextMenuStreamSettings) && (m_deviceUISet->m_deviceMIMOEngine)) - { - DeviceStreamSelectionDialog dialog(this); - dialog.setNumberOfStreams(m_localSink->getNumberOfDeviceStreams()); - dialog.setStreamIndex(m_settings.m_streamIndex); - dialog.move(p); - dialog.exec(); + if (m_deviceUISet->m_deviceMIMOEngine) + { + m_settings.m_streamIndex = dialog.getSelectedStreamIndex(); + m_channelMarker.clearStreamIndexes(); + m_channelMarker.addStreamIndex(m_settings.m_streamIndex); + updateIndexLabel(); + } - m_settings.m_streamIndex = dialog.getSelectedStreamIndex(); - m_channelMarker.clearStreamIndexes(); - m_channelMarker.addStreamIndex(m_settings.m_streamIndex); - displayStreamIndex(); applySettings(); } diff --git a/plugins/channelrx/localsink/localsinkgui.h b/plugins/channelrx/localsink/localsinkgui.h index 55a7344cd..6d678d563 100644 --- a/plugins/channelrx/localsink/localsinkgui.h +++ b/plugins/channelrx/localsink/localsinkgui.h @@ -57,6 +57,8 @@ public: virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } virtual bool getHidden() const { return m_settings.m_hidden; } virtual ChannelMarker& getChannelMarker() { return m_channelMarker; } + virtual int getStreamIndex() const { return m_settings.m_streamIndex; } + virtual void setStreamIndex(int streamIndex) { m_settings.m_streamIndex = streamIndex; } private: Ui::LocalSinkGUI* ui; @@ -81,7 +83,6 @@ private: void blockApplySettings(bool block); void applySettings(bool force = false); void displaySettings(); - void displayStreamIndex(); void displayRateAndShift(); void updateLocalDevices(); int getLocalDeviceIndexInCombo(int localDeviceIndex); diff --git a/plugins/channelrx/noisefigure/noisefiguregui.cpp b/plugins/channelrx/noisefigure/noisefiguregui.cpp index 2728c2605..c2bc9a0db 100644 --- a/plugins/channelrx/noisefigure/noisefiguregui.cpp +++ b/plugins/channelrx/noisefigure/noisefiguregui.cpp @@ -551,6 +551,14 @@ void NoiseFigureGUI::onMenuDialogCalled(const QPoint &p) dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); dialog.setReverseAPIChannelIndex(m_settings.m_reverseAPIChannelIndex); + dialog.setDefaultTitle(m_displayedName); + + if (m_deviceUISet->m_deviceMIMOEngine) + { + dialog.setNumberOfStreams(m_noiseFigure->getNumberOfDeviceStreams()); + dialog.setStreamIndex(m_settings.m_streamIndex); + } + dialog.move(p); dialog.exec(); @@ -566,20 +574,14 @@ void NoiseFigureGUI::onMenuDialogCalled(const QPoint &p) setTitle(m_channelMarker.getTitle()); setTitleColor(m_settings.m_rgbColor); - applySettings(); - } - else if ((m_contextMenuType == ContextMenuStreamSettings) && (m_deviceUISet->m_deviceMIMOEngine)) - { - DeviceStreamSelectionDialog dialog(this); - dialog.setNumberOfStreams(m_noiseFigure->getNumberOfDeviceStreams()); - dialog.setStreamIndex(m_settings.m_streamIndex); - dialog.move(p); - dialog.exec(); + if (m_deviceUISet->m_deviceMIMOEngine) + { + m_settings.m_streamIndex = dialog.getSelectedStreamIndex(); + m_channelMarker.clearStreamIndexes(); + m_channelMarker.addStreamIndex(m_settings.m_streamIndex); + updateIndexLabel(); + } - m_settings.m_streamIndex = dialog.getSelectedStreamIndex(); - m_channelMarker.clearStreamIndexes(); - m_channelMarker.addStreamIndex(m_settings.m_streamIndex); - displayStreamIndex(); applySettings(); } @@ -736,7 +738,7 @@ void NoiseFigureGUI::displaySettings() ui->fftSize->setCurrentIndex(log2(m_settings.m_fftSize) - 6); updateBW(); - displayStreamIndex(); + updateIndexLabel(); // Order and size columns QHeaderView *header = ui->results->horizontalHeader(); @@ -756,15 +758,6 @@ void NoiseFigureGUI::displaySettings() blockApplySettings(false); } -void NoiseFigureGUI::displayStreamIndex() -{ - if (m_deviceUISet->m_deviceMIMOEngine) { - setStreamIndicator(tr("%1").arg(m_settings.m_streamIndex)); - } else { - setStreamIndicator("S"); // single channel indicator - } -} - void NoiseFigureGUI::leaveEvent(QEvent*) { m_channelMarker.setHighlighted(false); diff --git a/plugins/channelrx/noisefigure/noisefiguregui.h b/plugins/channelrx/noisefigure/noisefiguregui.h index 249697592..100350faa 100644 --- a/plugins/channelrx/noisefigure/noisefiguregui.h +++ b/plugins/channelrx/noisefigure/noisefiguregui.h @@ -64,6 +64,8 @@ public: virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } virtual bool getHidden() const { return m_settings.m_hidden; } virtual ChannelMarker& getChannelMarker() { return m_channelMarker; } + virtual int getStreamIndex() const { return m_settings.m_streamIndex; } + virtual void setStreamIndex(int streamIndex) { m_settings.m_streamIndex = streamIndex; } public slots: void channelMarkerChangedByCursor(); @@ -100,7 +102,6 @@ private: void blockApplySettings(bool block); void applySettings(bool force = false); void displaySettings(); - void displayStreamIndex(); bool handleMessage(const Message& message); void makeUIConnections(); void updateAbsoluteCenterFrequency(); diff --git a/plugins/channelrx/radioastronomy/radioastronomygui.cpp b/plugins/channelrx/radioastronomy/radioastronomygui.cpp index 7e74542e2..8a3996162 100644 --- a/plugins/channelrx/radioastronomy/radioastronomygui.cpp +++ b/plugins/channelrx/radioastronomy/radioastronomygui.cpp @@ -1963,6 +1963,14 @@ void RadioAstronomyGUI::onMenuDialogCalled(const QPoint &p) dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); dialog.setReverseAPIChannelIndex(m_settings.m_reverseAPIChannelIndex); + dialog.setDefaultTitle(m_displayedName); + + if (m_deviceUISet->m_deviceMIMOEngine) + { + dialog.setNumberOfStreams(m_radioAstronomy->getNumberOfDeviceStreams()); + dialog.setStreamIndex(m_settings.m_streamIndex); + } + dialog.move(p); dialog.exec(); @@ -1978,20 +1986,14 @@ void RadioAstronomyGUI::onMenuDialogCalled(const QPoint &p) setTitle(m_channelMarker.getTitle()); setTitleColor(m_settings.m_rgbColor); - applySettings(); - } - else if ((m_contextMenuType == ContextMenuStreamSettings) && (m_deviceUISet->m_deviceMIMOEngine)) - { - DeviceStreamSelectionDialog dialog(this); - dialog.setNumberOfStreams(m_radioAstronomy->getNumberOfDeviceStreams()); - dialog.setStreamIndex(m_settings.m_streamIndex); - dialog.move(p); - dialog.exec(); + if (m_deviceUISet->m_deviceMIMOEngine) + { + m_settings.m_streamIndex = dialog.getSelectedStreamIndex(); + m_channelMarker.clearStreamIndexes(); + m_channelMarker.addStreamIndex(m_settings.m_streamIndex); + updateIndexLabel(); + } - m_settings.m_streamIndex = dialog.getSelectedStreamIndex(); - m_channelMarker.clearStreamIndexes(); - m_channelMarker.addStreamIndex(m_settings.m_streamIndex); - displayStreamIndex(); applySettings(); } @@ -2547,7 +2549,7 @@ void RadioAstronomyGUI::displaySettings() ui->sweep2Delay->setValue(m_settings.m_sweep2Delay); displayRunModeSettings(); - displayStreamIndex(); + updateIndexLabel(); // Order and size columns QHeaderView *header = ui->powerTable->horizontalHeader(); @@ -2567,15 +2569,6 @@ void RadioAstronomyGUI::displaySettings() getRollupContents()->arrangeRollups(); } -void RadioAstronomyGUI::displayStreamIndex() -{ - if (m_deviceUISet->m_deviceMIMOEngine) { - setStreamIndicator(tr("%1").arg(m_settings.m_streamIndex)); - } else { - setStreamIndicator("S"); // single channel indicator - } -} - void RadioAstronomyGUI::leaveEvent(QEvent*) { m_channelMarker.setHighlighted(false); diff --git a/plugins/channelrx/radioastronomy/radioastronomygui.h b/plugins/channelrx/radioastronomy/radioastronomygui.h index 2ae11b487..b4a736ade 100644 --- a/plugins/channelrx/radioastronomy/radioastronomygui.h +++ b/plugins/channelrx/radioastronomy/radioastronomygui.h @@ -207,6 +207,8 @@ public: virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } virtual bool getHidden() const { return m_settings.m_hidden; } virtual ChannelMarker& getChannelMarker() { return m_channelMarker; } + virtual int getStreamIndex() const { return m_settings.m_streamIndex; } + virtual void setStreamIndex(int streamIndex) { m_settings.m_streamIndex = streamIndex; } public slots: void channelMarkerChangedByCursor(); @@ -328,7 +330,6 @@ private: void blockApplySettings(bool block); void applySettings(bool force = false); void displaySettings(); - void displayStreamIndex(); void displaySpectrumLineFrequency(); void displayRunModeSettings(); void updateAvailableFeatures(); diff --git a/plugins/channelrx/radioclock/radioclockgui.cpp b/plugins/channelrx/radioclock/radioclockgui.cpp index 7f92628a2..675463e1e 100644 --- a/plugins/channelrx/radioclock/radioclockgui.cpp +++ b/plugins/channelrx/radioclock/radioclockgui.cpp @@ -236,6 +236,14 @@ void RadioClockGUI::onMenuDialogCalled(const QPoint &p) dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); dialog.setReverseAPIChannelIndex(m_settings.m_reverseAPIChannelIndex); + dialog.setDefaultTitle(m_displayedName); + + if (m_deviceUISet->m_deviceMIMOEngine) + { + dialog.setNumberOfStreams(m_radioClock->getNumberOfDeviceStreams()); + dialog.setStreamIndex(m_settings.m_streamIndex); + } + dialog.move(p); dialog.exec(); @@ -251,20 +259,14 @@ void RadioClockGUI::onMenuDialogCalled(const QPoint &p) setTitle(m_channelMarker.getTitle()); setTitleColor(m_settings.m_rgbColor); - applySettings(); - } - else if ((m_contextMenuType == ContextMenuStreamSettings) && (m_deviceUISet->m_deviceMIMOEngine)) - { - DeviceStreamSelectionDialog dialog(this); - dialog.setNumberOfStreams(m_radioClock->getNumberOfDeviceStreams()); - dialog.setStreamIndex(m_settings.m_streamIndex); - dialog.move(p); - dialog.exec(); + if (m_deviceUISet->m_deviceMIMOEngine) + { + m_settings.m_streamIndex = dialog.getSelectedStreamIndex(); + m_channelMarker.clearStreamIndexes(); + m_channelMarker.addStreamIndex(m_settings.m_streamIndex); + updateIndexLabel(); + } - m_settings.m_streamIndex = dialog.getSelectedStreamIndex(); - m_channelMarker.clearStreamIndexes(); - m_channelMarker.addStreamIndex(m_settings.m_streamIndex); - displayStreamIndex(); applySettings(); } @@ -381,22 +383,13 @@ void RadioClockGUI::displaySettings() ui->modulation->setCurrentIndex((int)m_settings.m_modulation); ui->timezone->setCurrentIndex((int)m_settings.m_timezone); - displayStreamIndex(); + updateIndexLabel(); getRollupContents()->restoreState(m_rollupState); updateAbsoluteCenterFrequency(); blockApplySettings(false); } -void RadioClockGUI::displayStreamIndex() -{ - if (m_deviceUISet->m_deviceMIMOEngine) { - setStreamIndicator(tr("%1").arg(m_settings.m_streamIndex)); - } else { - setStreamIndicator("S"); // single channel indicator - } -} - void RadioClockGUI::leaveEvent(QEvent*) { m_channelMarker.setHighlighted(false); diff --git a/plugins/channelrx/radioclock/radioclockgui.h b/plugins/channelrx/radioclock/radioclockgui.h index 73b04177a..2d855e054 100644 --- a/plugins/channelrx/radioclock/radioclockgui.h +++ b/plugins/channelrx/radioclock/radioclockgui.h @@ -59,6 +59,8 @@ public: virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } virtual bool getHidden() const { return m_settings.m_hidden; } virtual ChannelMarker& getChannelMarker() { return m_channelMarker; } + virtual int getStreamIndex() const { return m_settings.m_streamIndex; } + virtual void setStreamIndex(int streamIndex) { m_settings.m_streamIndex = streamIndex; } public slots: void channelMarkerChangedByCursor(); @@ -88,7 +90,6 @@ private: void blockApplySettings(bool block); void applySettings(bool force = false); void displaySettings(); - void displayStreamIndex(); bool handleMessage(const Message& message); void makeUIConnections(); void updateAbsoluteCenterFrequency(); diff --git a/plugins/channelrx/remotesink/remotesinkgui.cpp b/plugins/channelrx/remotesink/remotesinkgui.cpp index 886d2248a..50f4cf9ad 100644 --- a/plugins/channelrx/remotesink/remotesinkgui.cpp +++ b/plugins/channelrx/remotesink/remotesinkgui.cpp @@ -175,21 +175,12 @@ void RemoteSinkGUI::displaySettings() ui->nominalNbBlocksText->setText(tr("%1/%2").arg(s).arg(s1)); ui->nbTxBytes->setCurrentIndex(log2(m_settings.m_nbTxBytes)); applyDecimation(); - displayStreamIndex(); + updateIndexLabel(); getRollupContents()->restoreState(m_rollupState); updateAbsoluteCenterFrequency(); blockApplySettings(false); } -void RemoteSinkGUI::displayStreamIndex() -{ - if (m_deviceUISet->m_deviceMIMOEngine) { - setStreamIndicator(tr("%1").arg(m_settings.m_streamIndex)); - } else { - setStreamIndicator("S"); // single channel indicator - } -} - void RemoteSinkGUI::displayRateAndShift() { int shift = m_shiftFrequencyFactor * m_basebandSampleRate; @@ -243,6 +234,13 @@ void RemoteSinkGUI::onMenuDialogCalled(const QPoint &p) dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); dialog.setReverseAPIChannelIndex(m_settings.m_reverseAPIChannelIndex); + dialog.setDefaultTitle(m_displayedName); + + if (m_deviceUISet->m_deviceMIMOEngine) + { + dialog.setNumberOfStreams(m_remoteSink->getNumberOfDeviceStreams()); + dialog.setStreamIndex(m_settings.m_streamIndex); + } dialog.move(p); dialog.exec(); @@ -259,20 +257,14 @@ void RemoteSinkGUI::onMenuDialogCalled(const QPoint &p) setTitle(m_channelMarker.getTitle()); setTitleColor(m_settings.m_rgbColor); - applySettings(); - } - else if ((m_contextMenuType == ContextMenuStreamSettings) && (m_deviceUISet->m_deviceMIMOEngine)) - { - DeviceStreamSelectionDialog dialog(this); - dialog.setNumberOfStreams(m_remoteSink->getNumberOfDeviceStreams()); - dialog.setStreamIndex(m_settings.m_streamIndex); - dialog.move(p); - dialog.exec(); + if (m_deviceUISet->m_deviceMIMOEngine) + { + m_settings.m_streamIndex = dialog.getSelectedStreamIndex(); + m_channelMarker.clearStreamIndexes(); + m_channelMarker.addStreamIndex(m_settings.m_streamIndex); + updateIndexLabel(); + } - m_settings.m_streamIndex = dialog.getSelectedStreamIndex(); - m_channelMarker.clearStreamIndexes(); - m_channelMarker.addStreamIndex(m_settings.m_streamIndex); - displayStreamIndex(); applySettings(); } diff --git a/plugins/channelrx/remotesink/remotesinkgui.h b/plugins/channelrx/remotesink/remotesinkgui.h index 60354c528..441ae61ec 100644 --- a/plugins/channelrx/remotesink/remotesinkgui.h +++ b/plugins/channelrx/remotesink/remotesinkgui.h @@ -57,6 +57,8 @@ public: virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } virtual bool getHidden() const { return m_settings.m_hidden; } virtual ChannelMarker& getChannelMarker() { return m_channelMarker; } + virtual int getStreamIndex() const { return m_settings.m_streamIndex; } + virtual void setStreamIndex(int streamIndex) { m_settings.m_streamIndex = streamIndex; } private: Ui::RemoteSinkGUI* ui; @@ -81,7 +83,6 @@ private: void blockApplySettings(bool block); void applySettings(bool force = false); void displaySettings(); - void displayStreamIndex(); void displayRateAndShift(); bool handleMessage(const Message& message); void makeUIConnections(); diff --git a/plugins/channelrx/sigmffilesink/sigmffilesinkgui.cpp b/plugins/channelrx/sigmffilesink/sigmffilesinkgui.cpp index 52113da64..28606ff7a 100644 --- a/plugins/channelrx/sigmffilesink/sigmffilesinkgui.cpp +++ b/plugins/channelrx/sigmffilesink/sigmffilesinkgui.cpp @@ -276,7 +276,7 @@ void SigMFFileSinkGUI::displaySettings() ui->squelchedRecording->blockSignals(false); } - displayStreamIndex(); + updateIndexLabel(); setPosFromFrequency(); getRollupContents()->restoreState(m_rollupState); @@ -284,15 +284,6 @@ void SigMFFileSinkGUI::displaySettings() blockApplySettings(false); } -void SigMFFileSinkGUI::displayStreamIndex() -{ - if (m_deviceUISet->m_deviceMIMOEngine) { - setStreamIndicator(tr("%1").arg(m_settings.m_streamIndex)); - } else { - setStreamIndicator("S"); // single channel indicator - } -} - void SigMFFileSinkGUI::displayRate() { double channelSampleRate = ((double) m_basebandSampleRate) / (1<m_deviceMIMOEngine) + { + dialog.setNumberOfStreams(m_sigMFFileSink->getNumberOfDeviceStreams()); + dialog.setStreamIndex(m_settings.m_streamIndex); + } dialog.move(p); dialog.exec(); @@ -381,20 +379,14 @@ void SigMFFileSinkGUI::onMenuDialogCalled(const QPoint &p) setTitle(m_channelMarker.getTitle()); setTitleColor(m_settings.m_rgbColor); - applySettings(); - } - else if ((m_contextMenuType == ContextMenuStreamSettings) && (m_deviceUISet->m_deviceMIMOEngine)) - { - DeviceStreamSelectionDialog dialog(this); - dialog.setNumberOfStreams(m_sigMFFileSink->getNumberOfDeviceStreams()); - dialog.setStreamIndex(m_settings.m_streamIndex); - dialog.move(p); - dialog.exec(); + if (m_deviceUISet->m_deviceMIMOEngine) + { + m_settings.m_streamIndex = dialog.getSelectedStreamIndex(); + m_channelMarker.clearStreamIndexes(); + m_channelMarker.addStreamIndex(m_settings.m_streamIndex); + updateIndexLabel(); + } - m_settings.m_streamIndex = dialog.getSelectedStreamIndex(); - m_channelMarker.clearStreamIndexes(); - m_channelMarker.addStreamIndex(m_settings.m_streamIndex); - displayStreamIndex(); applySettings(); } diff --git a/plugins/channelrx/sigmffilesink/sigmffilesinkgui.h b/plugins/channelrx/sigmffilesink/sigmffilesinkgui.h index e97e4d006..eb18bc049 100644 --- a/plugins/channelrx/sigmffilesink/sigmffilesinkgui.h +++ b/plugins/channelrx/sigmffilesink/sigmffilesinkgui.h @@ -58,6 +58,8 @@ public: virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } virtual bool getHidden() const { return m_settings.m_hidden; } virtual ChannelMarker& getChannelMarker() { return m_channelMarker; } + virtual int getStreamIndex() const { return m_settings.m_streamIndex; } + virtual void setStreamIndex(int streamIndex) { m_settings.m_streamIndex = streamIndex; } public slots: void channelMarkerChangedByCursor(); @@ -91,7 +93,6 @@ private: void applySettings(bool force = false); void applyDecimation(); void displaySettings(); - void displayStreamIndex(); void displayRate(); void displayPos(); void setFrequencyFromPos(); diff --git a/plugins/channelrx/udpsink/udpsinkgui.cpp b/plugins/channelrx/udpsink/udpsinkgui.cpp index 939cb5a5f..855c7751d 100644 --- a/plugins/channelrx/udpsink/udpsinkgui.cpp +++ b/plugins/channelrx/udpsink/udpsinkgui.cpp @@ -271,7 +271,7 @@ void UDPSinkGUI::displaySettings() ui->applyBtn->setEnabled(false); ui->applyBtn->setStyleSheet("QPushButton { background:rgb(79,79,79); }"); - displayStreamIndex(); + updateIndexLabel(); getRollupContents()->restoreState(m_rollupState); updateAbsoluteCenterFrequency(); @@ -280,15 +280,6 @@ void UDPSinkGUI::displaySettings() ui->glSpectrum->setSampleRate(m_settings.m_outputSampleRate); } -void UDPSinkGUI::displayStreamIndex() -{ - if (m_deviceUISet->m_deviceMIMOEngine) { - setStreamIndicator(tr("%1").arg(m_settings.m_streamIndex)); - } else { - setStreamIndicator("S"); // single channel indicator - } -} - void UDPSinkGUI::setSampleFormatIndex(const UDPSinkSettings::SampleFormat& sampleFormat) { switch(sampleFormat) @@ -621,6 +612,13 @@ void UDPSinkGUI::onMenuDialogCalled(const QPoint &p) dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); dialog.setReverseAPIChannelIndex(m_settings.m_reverseAPIChannelIndex); + dialog.setDefaultTitle(m_displayedName); + + if (m_deviceUISet->m_deviceMIMOEngine) + { + dialog.setNumberOfStreams(m_udpSink->getNumberOfDeviceStreams()); + dialog.setStreamIndex(m_settings.m_streamIndex); + } dialog.move(p); dialog.exec(); @@ -637,21 +635,15 @@ void UDPSinkGUI::onMenuDialogCalled(const QPoint &p) setTitle(m_channelMarker.getTitle()); setTitleColor(m_settings.m_rgbColor); - applySettingsImmediate(); - } - else if ((m_contextMenuType == ContextMenuStreamSettings) && (m_deviceUISet->m_deviceMIMOEngine)) - { - DeviceStreamSelectionDialog dialog(this); - dialog.setNumberOfStreams(m_udpSink->getNumberOfDeviceStreams()); - dialog.setStreamIndex(m_settings.m_streamIndex); - dialog.move(p); - dialog.exec(); + if (m_deviceUISet->m_deviceMIMOEngine) + { + m_settings.m_streamIndex = dialog.getSelectedStreamIndex(); + m_channelMarker.clearStreamIndexes(); + m_channelMarker.addStreamIndex(m_settings.m_streamIndex); + updateIndexLabel(); + } - m_settings.m_streamIndex = dialog.getSelectedStreamIndex(); - m_channelMarker.clearStreamIndexes(); - m_channelMarker.addStreamIndex(m_settings.m_streamIndex); - displayStreamIndex(); - applySettings(); + applySettingsImmediate(); } resetContextMenuType(); diff --git a/plugins/channelrx/udpsink/udpsinkgui.h b/plugins/channelrx/udpsink/udpsinkgui.h index fe476b533..d08e5dd4a 100644 --- a/plugins/channelrx/udpsink/udpsinkgui.h +++ b/plugins/channelrx/udpsink/udpsinkgui.h @@ -58,6 +58,8 @@ public: virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } virtual bool getHidden() const { return m_settings.m_hidden; } virtual ChannelMarker& getChannelMarker() { return m_channelMarker; } + virtual int getStreamIndex() const { return m_settings.m_streamIndex; } + virtual void setStreamIndex(int streamIndex) { m_settings.m_streamIndex = streamIndex; } public slots: void channelMarkerChangedByCursor(); @@ -92,7 +94,6 @@ private: void applySettings(bool force = false); void applySettingsImmediate(bool force = false); void displaySettings(); - void displayStreamIndex(); void setSampleFormat(int index); void setSampleFormatIndex(const UDPSinkSettings::SampleFormat& sampleFormat); bool handleMessage(const Message& message); diff --git a/plugins/channeltx/filesource/filesourcegui.cpp b/plugins/channeltx/filesource/filesourcegui.cpp index 63c2a3a3e..f0e0bdda7 100644 --- a/plugins/channeltx/filesource/filesourcegui.cpp +++ b/plugins/channeltx/filesource/filesourcegui.cpp @@ -309,7 +309,7 @@ void FileSourceGUI::displaySettings() setWindowTitle(m_channelMarker.getTitle()); setTitle(m_channelMarker.getTitle()); - displayStreamIndex(); + updateIndexLabel(); blockApplySettings(true); ui->fileNameText->setText(m_settings.m_fileName); @@ -333,15 +333,6 @@ void FileSourceGUI::displayRateAndShift() m_channelMarker.setBandwidth(channelSampleRate); } -void FileSourceGUI::displayStreamIndex() -{ - if (m_deviceUISet->m_deviceMIMOEngine) { - setStreamIndicator(tr("%1").arg(m_settings.m_streamIndex)); - } else { - setStreamIndicator("S"); // single channel indicator - } -} - void FileSourceGUI::leaveEvent(QEvent*) { m_channelMarker.setHighlighted(false); @@ -384,6 +375,13 @@ void FileSourceGUI::onMenuDialogCalled(const QPoint &p) dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); dialog.setReverseAPIChannelIndex(m_settings.m_reverseAPIChannelIndex); + dialog.setDefaultTitle(m_displayedName); + + if (m_deviceUISet->m_deviceMIMOEngine) + { + dialog.setNumberOfStreams(m_fileSource->getNumberOfDeviceStreams()); + dialog.setStreamIndex(m_settings.m_streamIndex); + } dialog.move(p); dialog.exec(); @@ -399,20 +397,14 @@ void FileSourceGUI::onMenuDialogCalled(const QPoint &p) setWindowTitle(m_settings.m_title); setTitleColor(m_settings.m_rgbColor); - applySettings(); - } - else if ((m_contextMenuType == ContextMenuStreamSettings) && (m_deviceUISet->m_deviceMIMOEngine)) - { - DeviceStreamSelectionDialog dialog(this); - dialog.setNumberOfStreams(m_fileSource->getNumberOfDeviceStreams()); - dialog.setStreamIndex(m_settings.m_streamIndex); - dialog.move(p); - dialog.exec(); + if (m_deviceUISet->m_deviceMIMOEngine) + { + m_settings.m_streamIndex = dialog.getSelectedStreamIndex(); + m_channelMarker.clearStreamIndexes(); + m_channelMarker.addStreamIndex(m_settings.m_streamIndex); + updateIndexLabel(); + } - m_settings.m_streamIndex = dialog.getSelectedStreamIndex(); - m_channelMarker.clearStreamIndexes(); - m_channelMarker.addStreamIndex(m_settings.m_streamIndex); - displayStreamIndex(); applySettings(); } diff --git a/plugins/channeltx/filesource/filesourcegui.h b/plugins/channeltx/filesource/filesourcegui.h index 8eec2f10f..92c86ec7f 100644 --- a/plugins/channeltx/filesource/filesourcegui.h +++ b/plugins/channeltx/filesource/filesourcegui.h @@ -54,6 +54,8 @@ public: virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } virtual bool getHidden() const { return m_settings.m_hidden; } virtual ChannelMarker& getChannelMarker() { return m_channelMarker; } + virtual int getStreamIndex() const { return m_settings.m_streamIndex; } + virtual void setStreamIndex(int streamIndex) { m_settings.m_streamIndex = streamIndex; } public slots: void channelMarkerChangedByCursor(); @@ -93,7 +95,6 @@ private: void updateWithStreamTime(); void displaySettings(); void displayRateAndShift(); - void displayStreamIndex(); bool handleMessage(const Message& message); void makeUIConnections(); void updateAbsoluteCenterFrequency(); diff --git a/plugins/channeltx/localsource/localsourcegui.cpp b/plugins/channeltx/localsource/localsourcegui.cpp index 316894394..1a1e346c1 100644 --- a/plugins/channeltx/localsource/localsourcegui.cpp +++ b/plugins/channeltx/localsource/localsourcegui.cpp @@ -164,7 +164,7 @@ void LocalSourceGUI::displaySettings() setTitleColor(m_settings.m_rgbColor); setWindowTitle(m_channelMarker.getTitle()); setTitle(m_channelMarker.getTitle()); - displayStreamIndex(); + updateIndexLabel(); blockApplySettings(true); ui->interpolationFactor->setCurrentIndex(m_settings.m_log2Interp); @@ -186,15 +186,6 @@ void LocalSourceGUI::displayRateAndShift() m_channelMarker.setBandwidth(channelSampleRate); } -void LocalSourceGUI::displayStreamIndex() -{ - if (m_deviceUISet->m_deviceMIMOEngine) { - setStreamIndicator(tr("%1").arg(m_settings.m_streamIndex)); - } else { - setStreamIndicator("S"); // single channel indicator - } -} - void LocalSourceGUI::updateLocalDevices() { std::vector localDevicesIndexes; @@ -249,6 +240,13 @@ void LocalSourceGUI::onMenuDialogCalled(const QPoint &p) dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); dialog.setReverseAPIChannelIndex(m_settings.m_reverseAPIChannelIndex); + dialog.setDefaultTitle(m_displayedName); + + if (m_deviceUISet->m_deviceMIMOEngine) + { + dialog.setNumberOfStreams(m_localSource->getNumberOfDeviceStreams()); + dialog.setStreamIndex(m_settings.m_streamIndex); + } dialog.move(p); dialog.exec(); @@ -265,20 +263,14 @@ void LocalSourceGUI::onMenuDialogCalled(const QPoint &p) setTitle(m_channelMarker.getTitle()); setTitleColor(m_settings.m_rgbColor); - applySettings(); - } - else if ((m_contextMenuType == ContextMenuStreamSettings) && (m_deviceUISet->m_deviceMIMOEngine)) - { - DeviceStreamSelectionDialog dialog(this); - dialog.setNumberOfStreams(m_localSource->getNumberOfDeviceStreams()); - dialog.setStreamIndex(m_settings.m_streamIndex); - dialog.move(p); - dialog.exec(); + if (m_deviceUISet->m_deviceMIMOEngine) + { + m_settings.m_streamIndex = dialog.getSelectedStreamIndex(); + m_channelMarker.clearStreamIndexes(); + m_channelMarker.addStreamIndex(m_settings.m_streamIndex); + updateIndexLabel(); + } - m_settings.m_streamIndex = dialog.getSelectedStreamIndex(); - m_channelMarker.clearStreamIndexes(); - m_channelMarker.addStreamIndex(m_settings.m_streamIndex); - displayStreamIndex(); applySettings(); } diff --git a/plugins/channeltx/localsource/localsourcegui.h b/plugins/channeltx/localsource/localsourcegui.h index 52f358eed..c7a58498e 100644 --- a/plugins/channeltx/localsource/localsourcegui.h +++ b/plugins/channeltx/localsource/localsourcegui.h @@ -57,6 +57,8 @@ public: virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } virtual bool getHidden() const { return m_settings.m_hidden; } virtual ChannelMarker& getChannelMarker() { return m_channelMarker; } + virtual int getStreamIndex() const { return m_settings.m_streamIndex; } + virtual void setStreamIndex(int streamIndex) { m_settings.m_streamIndex = streamIndex; } private: Ui::LocalSourceGUI* ui; @@ -82,7 +84,6 @@ private: void applySettings(bool force = false); void displaySettings(); void displayRateAndShift(); - void displayStreamIndex(); void updateLocalDevices(); bool handleMessage(const Message& message); void makeUIConnections(); diff --git a/plugins/channeltx/mod802.15.4/ieee_802_15_4_modgui.cpp b/plugins/channeltx/mod802.15.4/ieee_802_15_4_modgui.cpp index e9dfc2485..c5cbe8e36 100644 --- a/plugins/channeltx/mod802.15.4/ieee_802_15_4_modgui.cpp +++ b/plugins/channeltx/mod802.15.4/ieee_802_15_4_modgui.cpp @@ -334,6 +334,14 @@ void IEEE_802_15_4_ModGUI::onMenuDialogCalled(const QPoint &p) dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); dialog.setReverseAPIChannelIndex(m_settings.m_reverseAPIChannelIndex); + dialog.setDefaultTitle(m_displayedName); + + if (m_deviceUISet->m_deviceMIMOEngine) + { + dialog.setNumberOfStreams(m_IEEE_802_15_4_Mod->getNumberOfDeviceStreams()); + dialog.setStreamIndex(m_settings.m_streamIndex); + } + dialog.move(p); dialog.exec(); @@ -349,20 +357,14 @@ void IEEE_802_15_4_ModGUI::onMenuDialogCalled(const QPoint &p) setTitle(m_channelMarker.getTitle()); setTitleColor(m_settings.m_rgbColor); - applySettings(); - } - else if ((m_contextMenuType == ContextMenuStreamSettings) && (m_deviceUISet->m_deviceMIMOEngine)) - { - DeviceStreamSelectionDialog dialog(this); - dialog.setNumberOfStreams(m_IEEE_802_15_4_Mod->getNumberOfDeviceStreams()); - dialog.setStreamIndex(m_settings.m_streamIndex); - dialog.move(p); - dialog.exec(); + if (m_deviceUISet->m_deviceMIMOEngine) + { + m_settings.m_streamIndex = dialog.getSelectedStreamIndex(); + m_channelMarker.clearStreamIndexes(); + m_channelMarker.addStreamIndex(m_settings.m_streamIndex); + updateIndexLabel(); + } - m_settings.m_streamIndex = dialog.getSelectedStreamIndex(); - m_channelMarker.clearStreamIndexes(); - m_channelMarker.addStreamIndex(m_settings.m_streamIndex); - displayStreamIndex(); applySettings(); } @@ -509,7 +511,7 @@ void IEEE_802_15_4_ModGUI::displaySettings() setTitleColor(m_settings.m_rgbColor); setWindowTitle(m_channelMarker.getTitle()); setTitle(m_channelMarker.getTitle()); - displayStreamIndex(); + updateIndexLabel(); blockApplySettings(true); @@ -603,15 +605,6 @@ QString IEEE_802_15_4_ModGUI::getDisplayValueWithMultiplier(int value) } } -void IEEE_802_15_4_ModGUI::displayStreamIndex() -{ - if (m_deviceUISet->m_deviceMIMOEngine) { - setStreamIndicator(tr("%1").arg(m_settings.m_streamIndex)); - } else { - setStreamIndicator("S"); // single channel indicator - } -} - void IEEE_802_15_4_ModGUI::leaveEvent(QEvent*) { m_channelMarker.setHighlighted(false); diff --git a/plugins/channeltx/mod802.15.4/ieee_802_15_4_modgui.h b/plugins/channeltx/mod802.15.4/ieee_802_15_4_modgui.h index b8f7ac261..48ccf3d8c 100644 --- a/plugins/channeltx/mod802.15.4/ieee_802_15_4_modgui.h +++ b/plugins/channeltx/mod802.15.4/ieee_802_15_4_modgui.h @@ -63,6 +63,8 @@ public: virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } virtual bool getHidden() const { return m_settings.m_hidden; } virtual ChannelMarker& getChannelMarker() { return m_channelMarker; } + virtual int getStreamIndex() const { return m_settings.m_streamIndex; } + virtual void setStreamIndex(int streamIndex) { m_settings.m_streamIndex = streamIndex; } public slots: void channelMarkerChangedByCursor(); @@ -93,7 +95,6 @@ private: void blockApplySettings(bool block); void applySettings(bool force = false); void displaySettings(); - void displayStreamIndex(); void displayRFBandwidth(int bandwidth); void displayChipRate(const IEEE_802_15_4_ModSettings& settings); QString getDisplayValueWithMultiplier(int value); diff --git a/plugins/channeltx/modais/aismodgui.cpp b/plugins/channeltx/modais/aismodgui.cpp index cb4a2db98..2348d50c1 100644 --- a/plugins/channeltx/modais/aismodgui.cpp +++ b/plugins/channeltx/modais/aismodgui.cpp @@ -357,6 +357,14 @@ void AISModGUI::onMenuDialogCalled(const QPoint &p) dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); dialog.setReverseAPIChannelIndex(m_settings.m_reverseAPIChannelIndex); + dialog.setDefaultTitle(m_displayedName); + + if (m_deviceUISet->m_deviceMIMOEngine) + { + dialog.setNumberOfStreams(m_aisMod->getNumberOfDeviceStreams()); + dialog.setStreamIndex(m_settings.m_streamIndex); + } + dialog.move(p); dialog.exec(); @@ -372,20 +380,14 @@ void AISModGUI::onMenuDialogCalled(const QPoint &p) setTitle(m_channelMarker.getTitle()); setTitleColor(m_settings.m_rgbColor); - applySettings(); - } - else if ((m_contextMenuType == ContextMenuStreamSettings) && (m_deviceUISet->m_deviceMIMOEngine)) - { - DeviceStreamSelectionDialog dialog(this); - dialog.setNumberOfStreams(m_aisMod->getNumberOfDeviceStreams()); - dialog.setStreamIndex(m_settings.m_streamIndex); - dialog.move(p); - dialog.exec(); + if (m_deviceUISet->m_deviceMIMOEngine) + { + m_settings.m_streamIndex = dialog.getSelectedStreamIndex(); + m_channelMarker.clearStreamIndexes(); + m_channelMarker.addStreamIndex(m_settings.m_streamIndex); + updateIndexLabel(); + } - m_settings.m_streamIndex = dialog.getSelectedStreamIndex(); - m_channelMarker.clearStreamIndexes(); - m_channelMarker.addStreamIndex(m_settings.m_streamIndex); - displayStreamIndex(); applySettings(); } @@ -534,7 +536,7 @@ void AISModGUI::displaySettings() setTitleColor(m_settings.m_rgbColor); setWindowTitle(m_channelMarker.getTitle()); setTitle(m_channelMarker.getTitle()); - displayStreamIndex(); + updateIndexLabel(); blockApplySettings(true); @@ -573,15 +575,6 @@ void AISModGUI::displaySettings() blockApplySettings(false); } -void AISModGUI::displayStreamIndex() -{ - if (m_deviceUISet->m_deviceMIMOEngine) { - setStreamIndicator(tr("%1").arg(m_settings.m_streamIndex)); - } else { - setStreamIndicator("S"); // single channel indicator - } -} - void AISModGUI::leaveEvent(QEvent*) { m_channelMarker.setHighlighted(false); diff --git a/plugins/channeltx/modais/aismodgui.h b/plugins/channeltx/modais/aismodgui.h index c327f5cbb..7deb50c85 100644 --- a/plugins/channeltx/modais/aismodgui.h +++ b/plugins/channeltx/modais/aismodgui.h @@ -58,6 +58,8 @@ public: virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } virtual bool getHidden() const { return m_settings.m_hidden; } virtual ChannelMarker& getChannelMarker() { return m_channelMarker; } + virtual int getStreamIndex() const { return m_settings.m_streamIndex; } + virtual void setStreamIndex(int streamIndex) { m_settings.m_streamIndex = streamIndex; } public slots: void channelMarkerChangedByCursor(); @@ -87,7 +89,6 @@ private: void blockApplySettings(bool block); void applySettings(bool force = false); void displaySettings(); - void displayStreamIndex(); bool handleMessage(const Message& message); void makeUIConnections(); void updateAbsoluteCenterFrequency(); diff --git a/plugins/channeltx/modam/ammodgui.cpp b/plugins/channeltx/modam/ammodgui.cpp index 1c0b4003b..ba71b516c 100644 --- a/plugins/channeltx/modam/ammodgui.cpp +++ b/plugins/channeltx/modam/ammodgui.cpp @@ -297,6 +297,14 @@ void AMModGUI::onMenuDialogCalled(const QPoint &p) dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); dialog.setReverseAPIChannelIndex(m_settings.m_reverseAPIChannelIndex); + dialog.setDefaultTitle(m_displayedName); + + if (m_deviceUISet->m_deviceMIMOEngine) + { + dialog.setNumberOfStreams(m_amMod->getNumberOfDeviceStreams()); + dialog.setStreamIndex(m_settings.m_streamIndex); + } + dialog.move(p); dialog.exec(); @@ -312,20 +320,14 @@ void AMModGUI::onMenuDialogCalled(const QPoint &p) setTitle(m_channelMarker.getTitle()); setTitleColor(m_settings.m_rgbColor); - applySettings(); - } - else if ((m_contextMenuType == ContextMenuStreamSettings) && (m_deviceUISet->m_deviceMIMOEngine)) - { - DeviceStreamSelectionDialog dialog(this); - dialog.setNumberOfStreams(m_amMod->getNumberOfDeviceStreams()); - dialog.setStreamIndex(m_settings.m_streamIndex); - dialog.move(p); - dialog.exec(); + if (m_deviceUISet->m_deviceMIMOEngine) + { + m_settings.m_streamIndex = dialog.getSelectedStreamIndex(); + m_channelMarker.clearStreamIndexes(); + m_channelMarker.addStreamIndex(m_settings.m_streamIndex); + updateIndexLabel(); + } - m_settings.m_streamIndex = dialog.getSelectedStreamIndex(); - m_channelMarker.clearStreamIndexes(); - m_channelMarker.addStreamIndex(m_settings.m_streamIndex); - displayStreamIndex(); applySettings(); } @@ -471,22 +473,13 @@ void AMModGUI::displaySettings() ui->feedbackVolume->setValue(roundf(m_settings.m_feedbackVolumeFactor * 100.0)); ui->feedbackVolumeText->setText(QString("%1").arg(m_settings.m_feedbackVolumeFactor, 0, 'f', 2)); - displayStreamIndex(); + updateIndexLabel(); getRollupContents()->restoreState(m_rollupState); updateAbsoluteCenterFrequency(); blockApplySettings(false); } -void AMModGUI::displayStreamIndex() -{ - if (m_deviceUISet->m_deviceMIMOEngine) { - setStreamIndicator(tr("%1").arg(m_settings.m_streamIndex)); - } else { - setStreamIndicator("S"); // single channel indicator - } -} - void AMModGUI::leaveEvent(QEvent*) { m_channelMarker.setHighlighted(false); diff --git a/plugins/channeltx/modam/ammodgui.h b/plugins/channeltx/modam/ammodgui.h index 7e4c9595f..07985e85c 100644 --- a/plugins/channeltx/modam/ammodgui.h +++ b/plugins/channeltx/modam/ammodgui.h @@ -57,6 +57,8 @@ public: virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } virtual bool getHidden() const { return m_settings.m_hidden; } virtual ChannelMarker& getChannelMarker() { return m_channelMarker; } + virtual int getStreamIndex() const { return m_settings.m_streamIndex; } + virtual void setStreamIndex(int streamIndex) { m_settings.m_streamIndex = streamIndex; } public slots: void channelMarkerChangedByCursor(); @@ -91,7 +93,6 @@ private: void blockApplySettings(bool block); void applySettings(bool force = false); void displaySettings(); - void displayStreamIndex(); void updateWithStreamData(); void updateWithStreamTime(); bool handleMessage(const Message& message); diff --git a/plugins/channeltx/modatv/atvmodgui.cpp b/plugins/channeltx/modatv/atvmodgui.cpp index e9046d924..c3dc7d76e 100644 --- a/plugins/channeltx/modatv/atvmodgui.cpp +++ b/plugins/channeltx/modatv/atvmodgui.cpp @@ -700,6 +700,13 @@ void ATVModGUI::onMenuDialogCalled(const QPoint &p) dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); dialog.setReverseAPIChannelIndex(m_settings.m_reverseAPIChannelIndex); + dialog.setDefaultTitle(m_displayedName); + + if (m_deviceUISet->m_deviceMIMOEngine) + { + dialog.setNumberOfStreams(m_atvMod->getNumberOfDeviceStreams()); + dialog.setStreamIndex(m_settings.m_streamIndex); + } dialog.move(p); dialog.exec(); @@ -716,20 +723,14 @@ void ATVModGUI::onMenuDialogCalled(const QPoint &p) setTitle(m_channelMarker.getTitle()); setTitleColor(m_settings.m_rgbColor); - applySettings(); - } - else if ((m_contextMenuType == ContextMenuStreamSettings) && (m_deviceUISet->m_deviceMIMOEngine)) - { - DeviceStreamSelectionDialog dialog(this); - dialog.setNumberOfStreams(m_atvMod->getNumberOfDeviceStreams()); - dialog.setStreamIndex(m_settings.m_streamIndex); - dialog.move(p); - dialog.exec(); + if (m_deviceUISet->m_deviceMIMOEngine) + { + m_settings.m_streamIndex = dialog.getSelectedStreamIndex(); + m_channelMarker.clearStreamIndexes(); + m_channelMarker.addStreamIndex(m_settings.m_streamIndex); + updateIndexLabel(); + } - m_settings.m_streamIndex = dialog.getSelectedStreamIndex(); - m_channelMarker.clearStreamIndexes(); - m_channelMarker.addStreamIndex(m_settings.m_streamIndex); - displayStreamIndex(); applySettings(); } @@ -766,7 +767,7 @@ void ATVModGUI::displaySettings() setTitleColor(m_settings.m_rgbColor); setWindowTitle(m_channelMarker.getTitle()); setTitle(m_channelMarker.getTitle()); - displayStreamIndex(); + updateIndexLabel(); blockApplySettings(true); @@ -817,15 +818,6 @@ void ATVModGUI::displaySettings() blockApplySettings(false); } -void ATVModGUI::displayStreamIndex() -{ - if (m_deviceUISet->m_deviceMIMOEngine) { - setStreamIndicator(tr("%1").arg(m_settings.m_streamIndex)); - } else { - setStreamIndicator("S"); // single channel indicator - } -} - void ATVModGUI::leaveEvent(QEvent*) { m_channelMarker.setHighlighted(false); diff --git a/plugins/channeltx/modatv/atvmodgui.h b/plugins/channeltx/modatv/atvmodgui.h index a146ff4b4..cfbf04961 100644 --- a/plugins/channeltx/modatv/atvmodgui.h +++ b/plugins/channeltx/modatv/atvmodgui.h @@ -56,6 +56,8 @@ public: virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } virtual bool getHidden() const { return m_settings.m_hidden; } virtual ChannelMarker& getChannelMarker() { return m_channelMarker; } + virtual int getStreamIndex() const { return m_settings.m_streamIndex; } + virtual void setStreamIndex(int streamIndex) { m_settings.m_streamIndex = streamIndex; } public slots: void channelMarkerChangedByCursor(); @@ -91,7 +93,6 @@ private: void blockApplySettings(bool block); void applySettings(bool force = false); void displaySettings(); - void displayStreamIndex(); void updateWithStreamData(); void updateWithStreamTime(); void setRFFiltersSlidersRange(int sampleRate); diff --git a/plugins/channeltx/modchirpchat/chirpchatmodgui.cpp b/plugins/channeltx/modchirpchat/chirpchatmodgui.cpp index 289c4ba21..b8af4b4ec 100644 --- a/plugins/channeltx/modchirpchat/chirpchatmodgui.cpp +++ b/plugins/channeltx/modchirpchat/chirpchatmodgui.cpp @@ -377,6 +377,14 @@ void ChirpChatModGUI::onMenuDialogCalled(const QPoint &p) dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); dialog.setReverseAPIChannelIndex(m_settings.m_reverseAPIChannelIndex); + dialog.setDefaultTitle(m_displayedName); + + if (m_deviceUISet->m_deviceMIMOEngine) + { + dialog.setNumberOfStreams(m_chirpChatMod->getNumberOfDeviceStreams()); + dialog.setStreamIndex(m_settings.m_streamIndex); + } + dialog.move(p); dialog.exec(); @@ -392,20 +400,14 @@ void ChirpChatModGUI::onMenuDialogCalled(const QPoint &p) setTitle(m_channelMarker.getTitle()); setTitleColor(m_settings.m_rgbColor); - applySettings(); - } - else if ((m_contextMenuType == ContextMenuStreamSettings) && (m_deviceUISet->m_deviceMIMOEngine)) - { - DeviceStreamSelectionDialog dialog(this); - dialog.setNumberOfStreams(m_chirpChatMod->getNumberOfDeviceStreams()); - dialog.setStreamIndex(m_settings.m_streamIndex); - dialog.move(p); - dialog.exec(); + if (m_deviceUISet->m_deviceMIMOEngine) + { + m_settings.m_streamIndex = dialog.getSelectedStreamIndex(); + m_channelMarker.clearStreamIndexes(); + m_channelMarker.addStreamIndex(m_settings.m_streamIndex); + updateIndexLabel(); + } - m_settings.m_streamIndex = dialog.getSelectedStreamIndex(); - m_channelMarker.clearStreamIndexes(); - m_channelMarker.addStreamIndex(m_settings.m_streamIndex); - displayStreamIndex(); applySettings(); } @@ -497,7 +499,7 @@ void ChirpChatModGUI::displaySettings() setWindowTitle(m_channelMarker.getTitle()); setTitle(m_channelMarker.getTitle()); - displayStreamIndex(); + updateIndexLabel(); displayCurrentPayloadMessage(); displayBinaryMessage(); @@ -539,15 +541,6 @@ void ChirpChatModGUI::displaySettings() blockApplySettings(false); } -void ChirpChatModGUI::displayStreamIndex() -{ - if (m_deviceUISet->m_deviceMIMOEngine) { - setStreamIndicator(tr("%1").arg(m_settings.m_streamIndex)); - } else { - setStreamIndicator("S"); // single channel indicator - } -} - void ChirpChatModGUI::displayCurrentPayloadMessage() { ui->messageText->blockSignals(true); diff --git a/plugins/channeltx/modchirpchat/chirpchatmodgui.h b/plugins/channeltx/modchirpchat/chirpchatmodgui.h index bfb017afb..98d3affb7 100644 --- a/plugins/channeltx/modchirpchat/chirpchatmodgui.h +++ b/plugins/channeltx/modchirpchat/chirpchatmodgui.h @@ -55,6 +55,8 @@ public: virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } virtual bool getHidden() const { return m_settings.m_hidden; } virtual ChannelMarker& getChannelMarker() { return m_channelMarker; } + virtual int getStreamIndex() const { return m_settings.m_streamIndex; } + virtual void setStreamIndex(int streamIndex) { m_settings.m_streamIndex = streamIndex; } public slots: void channelMarkerChangedByCursor(); @@ -82,7 +84,6 @@ private: void blockApplySettings(bool block); void applySettings(bool force = false); void displaySettings(); - void displayStreamIndex(); void displayCurrentPayloadMessage(); void displayBinaryMessage(); void setBandwidths(); diff --git a/plugins/channeltx/moddatv/datvmodgui.cpp b/plugins/channeltx/moddatv/datvmodgui.cpp index 6443d7779..370c2945d 100644 --- a/plugins/channeltx/moddatv/datvmodgui.cpp +++ b/plugins/channeltx/moddatv/datvmodgui.cpp @@ -503,6 +503,13 @@ void DATVModGUI::onMenuDialogCalled(const QPoint &p) dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); dialog.setReverseAPIChannelIndex(m_settings.m_reverseAPIChannelIndex); + dialog.setDefaultTitle(m_displayedName); + + if (m_deviceUISet->m_deviceMIMOEngine) + { + dialog.setNumberOfStreams(m_datvMod->getNumberOfDeviceStreams()); + dialog.setStreamIndex(m_settings.m_streamIndex); + } dialog.move(p); dialog.exec(); @@ -519,20 +526,14 @@ void DATVModGUI::onMenuDialogCalled(const QPoint &p) setTitle(m_channelMarker.getTitle()); setTitleColor(m_settings.m_rgbColor); - applySettings(); - } - else if ((m_contextMenuType == ContextMenuStreamSettings) && (m_deviceUISet->m_deviceMIMOEngine)) - { - DeviceStreamSelectionDialog dialog(this); - dialog.setNumberOfStreams(m_datvMod->getNumberOfDeviceStreams()); - dialog.setStreamIndex(m_settings.m_streamIndex); - dialog.move(p); - dialog.exec(); + if (m_deviceUISet->m_deviceMIMOEngine) + { + m_settings.m_streamIndex = dialog.getSelectedStreamIndex(); + m_channelMarker.clearStreamIndexes(); + m_channelMarker.addStreamIndex(m_settings.m_streamIndex); + updateIndexLabel(); + } - m_settings.m_streamIndex = dialog.getSelectedStreamIndex(); - m_channelMarker.clearStreamIndexes(); - m_channelMarker.addStreamIndex(m_settings.m_streamIndex); - displayStreamIndex(); applySettings(); } @@ -569,7 +570,7 @@ void DATVModGUI::displaySettings() setTitleColor(m_settings.m_rgbColor); setWindowTitle(m_channelMarker.getTitle()); setTitle(m_channelMarker.getTitle()); - displayStreamIndex(); + updateIndexLabel(); blockApplySettings(true); @@ -603,15 +604,6 @@ void DATVModGUI::displaySettings() blockApplySettings(false); } -void DATVModGUI::displayStreamIndex() -{ - if (m_deviceUISet->m_deviceMIMOEngine) { - setStreamIndicator(tr("%1").arg(m_settings.m_streamIndex)); - } else { - setStreamIndicator("S"); // single channel indicator - } -} - void DATVModGUI::leaveEvent(QEvent*) { m_channelMarker.setHighlighted(false); diff --git a/plugins/channeltx/moddatv/datvmodgui.h b/plugins/channeltx/moddatv/datvmodgui.h index 4982fa555..561f44f44 100644 --- a/plugins/channeltx/moddatv/datvmodgui.h +++ b/plugins/channeltx/moddatv/datvmodgui.h @@ -57,6 +57,8 @@ public: virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } virtual bool getHidden() const { return m_settings.m_hidden; } virtual ChannelMarker& getChannelMarker() { return m_channelMarker; } + virtual int getStreamIndex() const { return m_settings.m_streamIndex; } + virtual void setStreamIndex(int streamIndex) { m_settings.m_streamIndex = streamIndex; } public slots: void channelMarkerChangedByCursor(); @@ -92,7 +94,6 @@ private: void blockApplySettings(bool block); void applySettings(bool force = false); void displaySettings(); - void displayStreamIndex(); void updateWithStreamData(); void updateWithStreamTime(); void setChannelMarkerBandwidth(); diff --git a/plugins/channeltx/modfreedv/freedvmodgui.cpp b/plugins/channeltx/modfreedv/freedvmodgui.cpp index 789cd8a59..406f65e2e 100644 --- a/plugins/channeltx/modfreedv/freedvmodgui.cpp +++ b/plugins/channeltx/modfreedv/freedvmodgui.cpp @@ -309,6 +309,13 @@ void FreeDVModGUI::onMenuDialogCalled(const QPoint &p) dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); dialog.setReverseAPIChannelIndex(m_settings.m_reverseAPIChannelIndex); + dialog.setDefaultTitle(m_displayedName); + + if (m_deviceUISet->m_deviceMIMOEngine) + { + dialog.setNumberOfStreams(m_freeDVMod->getNumberOfDeviceStreams()); + dialog.setStreamIndex(m_settings.m_streamIndex); + } dialog.move(p); dialog.exec(); @@ -325,20 +332,14 @@ void FreeDVModGUI::onMenuDialogCalled(const QPoint &p) setTitle(m_channelMarker.getTitle()); setTitleColor(m_settings.m_rgbColor); - applySettings(); - } - else if ((m_contextMenuType == ContextMenuStreamSettings) && (m_deviceUISet->m_deviceMIMOEngine)) - { - DeviceStreamSelectionDialog dialog(this); - dialog.setNumberOfStreams(m_freeDVMod->getNumberOfDeviceStreams()); - dialog.setStreamIndex(m_settings.m_streamIndex); - dialog.move(p); - dialog.exec(); + if (m_deviceUISet->m_deviceMIMOEngine) + { + m_settings.m_streamIndex = dialog.getSelectedStreamIndex(); + m_channelMarker.clearStreamIndexes(); + m_channelMarker.addStreamIndex(m_settings.m_streamIndex); + updateIndexLabel(); + } - m_settings.m_streamIndex = dialog.getSelectedStreamIndex(); - m_channelMarker.clearStreamIndexes(); - m_channelMarker.addStreamIndex(m_settings.m_streamIndex); - displayStreamIndex(); applySettings(); } @@ -475,7 +476,7 @@ void FreeDVModGUI::displaySettings() setTitleColor(m_settings.m_rgbColor); setWindowTitle(m_channelMarker.getTitle()); setTitle(m_channelMarker.getTitle()); - displayStreamIndex(); + updateIndexLabel(); blockApplySettings(true); @@ -521,15 +522,6 @@ void FreeDVModGUI::displaySettings() blockApplySettings(false); } -void FreeDVModGUI::displayStreamIndex() -{ - if (m_deviceUISet->m_deviceMIMOEngine) { - setStreamIndicator(tr("%1").arg(m_settings.m_streamIndex)); - } else { - setStreamIndicator("S"); // single channel indicator - } -} - void FreeDVModGUI::leaveEvent(QEvent*) { m_channelMarker.setHighlighted(false); diff --git a/plugins/channeltx/modfreedv/freedvmodgui.h b/plugins/channeltx/modfreedv/freedvmodgui.h index 0275e17ef..6bd48fc5d 100644 --- a/plugins/channeltx/modfreedv/freedvmodgui.h +++ b/plugins/channeltx/modfreedv/freedvmodgui.h @@ -58,6 +58,8 @@ public: virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } virtual bool getHidden() const { return m_settings.m_hidden; } virtual ChannelMarker& getChannelMarker() { return m_channelMarker; } + virtual int getStreamIndex() const { return m_settings.m_streamIndex; } + virtual void setStreamIndex(int streamIndex) { m_settings.m_streamIndex = streamIndex; } public slots: void channelMarkerChangedByCursor(); @@ -95,7 +97,6 @@ private: void applyBandwidths(int spanLog2, bool force = false); void displayBandwidths(int spanLog2); void displaySettings(); - void displayStreamIndex(); void updateWithStreamData(); void updateWithStreamTime(); void channelMarkerUpdate(); diff --git a/plugins/channeltx/modnfm/nfmmodgui.cpp b/plugins/channeltx/modnfm/nfmmodgui.cpp index 899a7878c..e1023b923 100644 --- a/plugins/channeltx/modnfm/nfmmodgui.cpp +++ b/plugins/channeltx/modnfm/nfmmodgui.cpp @@ -369,6 +369,14 @@ void NFMModGUI::onMenuDialogCalled(const QPoint &p) dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); dialog.setReverseAPIChannelIndex(m_settings.m_reverseAPIChannelIndex); + dialog.setDefaultTitle(m_displayedName); + + if (m_deviceUISet->m_deviceMIMOEngine) + { + dialog.setNumberOfStreams(m_nfmMod->getNumberOfDeviceStreams()); + dialog.setStreamIndex(m_settings.m_streamIndex); + } + dialog.move(p); dialog.exec(); @@ -384,20 +392,14 @@ void NFMModGUI::onMenuDialogCalled(const QPoint &p) setTitle(m_channelMarker.getTitle()); setTitleColor(m_settings.m_rgbColor); - applySettings(); - } - else if ((m_contextMenuType == ContextMenuStreamSettings) && (m_deviceUISet->m_deviceMIMOEngine)) - { - DeviceStreamSelectionDialog dialog(this); - dialog.setNumberOfStreams(m_nfmMod->getNumberOfDeviceStreams()); - dialog.setStreamIndex(m_settings.m_streamIndex); - dialog.move(p); - dialog.exec(); + if (m_deviceUISet->m_deviceMIMOEngine) + { + m_settings.m_streamIndex = dialog.getSelectedStreamIndex(); + m_channelMarker.clearStreamIndexes(); + m_channelMarker.addStreamIndex(m_settings.m_streamIndex); + updateIndexLabel(); + } - m_settings.m_streamIndex = dialog.getSelectedStreamIndex(); - m_channelMarker.clearStreamIndexes(); - m_channelMarker.addStreamIndex(m_settings.m_streamIndex); - displayStreamIndex(); applySettings(); } @@ -522,7 +524,7 @@ void NFMModGUI::displaySettings() setTitleColor(m_settings.m_rgbColor); setWindowTitle(m_channelMarker.getTitle()); setTitle(m_channelMarker.getTitle()); - displayStreamIndex(); + updateIndexLabel(); blockApplySettings(true); @@ -577,15 +579,6 @@ void NFMModGUI::displaySettings() blockApplySettings(false); } -void NFMModGUI::displayStreamIndex() -{ - if (m_deviceUISet->m_deviceMIMOEngine) { - setStreamIndicator(tr("%1").arg(m_settings.m_streamIndex)); - } else { - setStreamIndicator("S"); // single channel indicator - } -} - void NFMModGUI::leaveEvent(QEvent*) { m_channelMarker.setHighlighted(false); diff --git a/plugins/channeltx/modnfm/nfmmodgui.h b/plugins/channeltx/modnfm/nfmmodgui.h index e4dbd25a0..030ab68ae 100644 --- a/plugins/channeltx/modnfm/nfmmodgui.h +++ b/plugins/channeltx/modnfm/nfmmodgui.h @@ -57,6 +57,8 @@ public: virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } virtual bool getHidden() const { return m_settings.m_hidden; } virtual ChannelMarker& getChannelMarker() { return m_channelMarker; } + virtual int getStreamIndex() const { return m_settings.m_streamIndex; } + virtual void setStreamIndex(int streamIndex) { m_settings.m_streamIndex = streamIndex; } public slots: void channelMarkerChangedByCursor(); @@ -93,7 +95,6 @@ private: void blockApplySettings(bool block); void applySettings(bool force = false); void displaySettings(); - void displayStreamIndex(); void updateWithStreamData(); void updateWithStreamTime(); bool handleMessage(const Message& message); diff --git a/plugins/channeltx/modpacket/packetmodgui.cpp b/plugins/channeltx/modpacket/packetmodgui.cpp index bd98f95d9..a1bd5ac8e 100644 --- a/plugins/channeltx/modpacket/packetmodgui.cpp +++ b/plugins/channeltx/modpacket/packetmodgui.cpp @@ -394,6 +394,14 @@ void PacketModGUI::onMenuDialogCalled(const QPoint &p) dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); dialog.setReverseAPIChannelIndex(m_settings.m_reverseAPIChannelIndex); + dialog.setDefaultTitle(m_displayedName); + + if (m_deviceUISet->m_deviceMIMOEngine) + { + dialog.setNumberOfStreams(m_packetMod->getNumberOfDeviceStreams()); + dialog.setStreamIndex(m_settings.m_streamIndex); + } + dialog.move(p); dialog.exec(); @@ -409,20 +417,14 @@ void PacketModGUI::onMenuDialogCalled(const QPoint &p) setTitle(m_channelMarker.getTitle()); setTitleColor(m_settings.m_rgbColor); - applySettings(); - } - else if ((m_contextMenuType == ContextMenuStreamSettings) && (m_deviceUISet->m_deviceMIMOEngine)) - { - DeviceStreamSelectionDialog dialog(this); - dialog.setNumberOfStreams(m_packetMod->getNumberOfDeviceStreams()); - dialog.setStreamIndex(m_settings.m_streamIndex); - dialog.move(p); - dialog.exec(); + if (m_deviceUISet->m_deviceMIMOEngine) + { + m_settings.m_streamIndex = dialog.getSelectedStreamIndex(); + m_channelMarker.clearStreamIndexes(); + m_channelMarker.addStreamIndex(m_settings.m_streamIndex); + updateIndexLabel(); + } - m_settings.m_streamIndex = dialog.getSelectedStreamIndex(); - m_channelMarker.clearStreamIndexes(); - m_channelMarker.addStreamIndex(m_settings.m_streamIndex); - displayStreamIndex(); applySettings(); } @@ -549,7 +551,7 @@ void PacketModGUI::displaySettings() setTitleColor(m_settings.m_rgbColor); setWindowTitle(m_channelMarker.getTitle()); setTitle(m_channelMarker.getTitle()); - displayStreamIndex(); + updateIndexLabel(); blockApplySettings(true); @@ -593,15 +595,6 @@ void PacketModGUI::displaySettings() blockApplySettings(false); } -void PacketModGUI::displayStreamIndex() -{ - if (m_deviceUISet->m_deviceMIMOEngine) { - setStreamIndicator(tr("%1").arg(m_settings.m_streamIndex)); - } else { - setStreamIndicator("S"); // single channel indicator - } -} - void PacketModGUI::leaveEvent(QEvent*) { m_channelMarker.setHighlighted(false); diff --git a/plugins/channeltx/modpacket/packetmodgui.h b/plugins/channeltx/modpacket/packetmodgui.h index 644cbdea6..1ff5837f7 100644 --- a/plugins/channeltx/modpacket/packetmodgui.h +++ b/plugins/channeltx/modpacket/packetmodgui.h @@ -57,6 +57,8 @@ public: virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } virtual bool getHidden() const { return m_settings.m_hidden; } virtual ChannelMarker& getChannelMarker() { return m_channelMarker; } + virtual int getStreamIndex() const { return m_settings.m_streamIndex; } + virtual void setStreamIndex(int streamIndex) { m_settings.m_streamIndex = streamIndex; } public slots: void channelMarkerChangedByCursor(); @@ -85,7 +87,6 @@ private: void blockApplySettings(bool block); void applySettings(bool force = false); void displaySettings(); - void displayStreamIndex(); bool handleMessage(const Message& message); void makeUIConnections(); void updateAbsoluteCenterFrequency(); diff --git a/plugins/channeltx/modssb/ssbmodgui.cpp b/plugins/channeltx/modssb/ssbmodgui.cpp index cb7c99b91..b55f10b46 100644 --- a/plugins/channeltx/modssb/ssbmodgui.cpp +++ b/plugins/channeltx/modssb/ssbmodgui.cpp @@ -373,6 +373,13 @@ void SSBModGUI::onMenuDialogCalled(const QPoint &p) dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); dialog.setReverseAPIChannelIndex(m_settings.m_reverseAPIChannelIndex); + dialog.setDefaultTitle(m_displayedName); + + if (m_deviceUISet->m_deviceMIMOEngine) + { + dialog.setNumberOfStreams(m_ssbMod->getNumberOfDeviceStreams()); + dialog.setStreamIndex(m_settings.m_streamIndex); + } dialog.move(p); dialog.exec(); @@ -389,20 +396,14 @@ void SSBModGUI::onMenuDialogCalled(const QPoint &p) setTitle(m_channelMarker.getTitle()); setTitleColor(m_settings.m_rgbColor); - applySettings(); - } - else if ((m_contextMenuType == ContextMenuStreamSettings) && (m_deviceUISet->m_deviceMIMOEngine)) - { - DeviceStreamSelectionDialog dialog(this); - dialog.setNumberOfStreams(m_ssbMod->getNumberOfDeviceStreams()); - dialog.setStreamIndex(m_settings.m_streamIndex); - dialog.move(p); - dialog.exec(); + if (m_deviceUISet->m_deviceMIMOEngine) + { + m_settings.m_streamIndex = dialog.getSelectedStreamIndex(); + m_channelMarker.clearStreamIndexes(); + m_channelMarker.addStreamIndex(m_settings.m_streamIndex); + updateIndexLabel(); + } - m_settings.m_streamIndex = dialog.getSelectedStreamIndex(); - m_channelMarker.clearStreamIndexes(); - m_channelMarker.addStreamIndex(m_settings.m_streamIndex); - displayStreamIndex(); applySettings(); } @@ -663,7 +664,7 @@ void SSBModGUI::displaySettings() setTitleColor(m_settings.m_rgbColor); setWindowTitle(m_channelMarker.getTitle()); setTitle(m_channelMarker.getTitle()); - displayStreamIndex(); + updateIndexLabel(); blockApplySettings(true); @@ -735,15 +736,6 @@ void SSBModGUI::displaySettings() blockApplySettings(false); } -void SSBModGUI::displayStreamIndex() -{ - if (m_deviceUISet->m_deviceMIMOEngine) { - setStreamIndicator(tr("%1").arg(m_settings.m_streamIndex)); - } else { - setStreamIndicator("S"); // single channel indicator - } -} - void SSBModGUI::leaveEvent(QEvent*) { m_channelMarker.setHighlighted(false); diff --git a/plugins/channeltx/modssb/ssbmodgui.h b/plugins/channeltx/modssb/ssbmodgui.h index 5a132e8e6..dd793e1bc 100644 --- a/plugins/channeltx/modssb/ssbmodgui.h +++ b/plugins/channeltx/modssb/ssbmodgui.h @@ -58,6 +58,8 @@ public: virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } virtual bool getHidden() const { return m_settings.m_hidden; } virtual ChannelMarker& getChannelMarker() { return m_channelMarker; } + virtual int getStreamIndex() const { return m_settings.m_streamIndex; } + virtual void setStreamIndex(int streamIndex) { m_settings.m_streamIndex = streamIndex; } public slots: void channelMarkerChangedByCursor(); @@ -98,7 +100,6 @@ private: void applySettings(bool force = false); void applyBandwidths(int spanLog2, bool force = false); void displaySettings(); - void displayStreamIndex(); void updateWithStreamData(); void updateWithStreamTime(); void channelMarkerUpdate(); diff --git a/plugins/channeltx/modwfm/wfmmodgui.cpp b/plugins/channeltx/modwfm/wfmmodgui.cpp index 473922fc5..1da374cfd 100644 --- a/plugins/channeltx/modwfm/wfmmodgui.cpp +++ b/plugins/channeltx/modwfm/wfmmodgui.cpp @@ -303,6 +303,13 @@ void WFMModGUI::onMenuDialogCalled(const QPoint &p) dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); dialog.setReverseAPIChannelIndex(m_settings.m_reverseAPIChannelIndex); + dialog.setDefaultTitle(m_displayedName); + + if (m_deviceUISet->m_deviceMIMOEngine) + { + dialog.setNumberOfStreams(m_wfmMod->getNumberOfDeviceStreams()); + dialog.setStreamIndex(m_settings.m_streamIndex); + } dialog.move(p); dialog.exec(); @@ -319,20 +326,14 @@ void WFMModGUI::onMenuDialogCalled(const QPoint &p) setTitle(m_channelMarker.getTitle()); setTitleColor(m_settings.m_rgbColor); - applySettings(); - } - else if ((m_contextMenuType == ContextMenuStreamSettings) && (m_deviceUISet->m_deviceMIMOEngine)) - { - DeviceStreamSelectionDialog dialog(this); - dialog.setNumberOfStreams(m_wfmMod->getNumberOfDeviceStreams()); - dialog.setStreamIndex(m_settings.m_streamIndex); - dialog.move(p); - dialog.exec(); + if (m_deviceUISet->m_deviceMIMOEngine) + { + m_settings.m_streamIndex = dialog.getSelectedStreamIndex(); + m_channelMarker.clearStreamIndexes(); + m_channelMarker.addStreamIndex(m_settings.m_streamIndex); + updateIndexLabel(); + } - m_settings.m_streamIndex = dialog.getSelectedStreamIndex(); - m_channelMarker.clearStreamIndexes(); - m_channelMarker.addStreamIndex(m_settings.m_streamIndex); - displayStreamIndex(); applySettings(); } @@ -454,7 +455,7 @@ void WFMModGUI::displaySettings() setTitleColor(m_settings.m_rgbColor); setWindowTitle(m_channelMarker.getTitle()); setTitle(m_channelMarker.getTitle()); - displayStreamIndex(); + updateIndexLabel(); blockApplySettings(true); @@ -496,15 +497,6 @@ void WFMModGUI::displaySettings() blockApplySettings(false); } -void WFMModGUI::displayStreamIndex() -{ - if (m_deviceUISet->m_deviceMIMOEngine) { - setStreamIndicator(tr("%1").arg(m_settings.m_streamIndex)); - } else { - setStreamIndicator("S"); // single channel indicator - } -} - void WFMModGUI::leaveEvent(QEvent*) { m_channelMarker.setHighlighted(false); diff --git a/plugins/channeltx/modwfm/wfmmodgui.h b/plugins/channeltx/modwfm/wfmmodgui.h index 26ef490c7..19e72eae9 100644 --- a/plugins/channeltx/modwfm/wfmmodgui.h +++ b/plugins/channeltx/modwfm/wfmmodgui.h @@ -55,6 +55,8 @@ public: virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } virtual bool getHidden() const { return m_settings.m_hidden; } virtual ChannelMarker& getChannelMarker() { return m_channelMarker; } + virtual int getStreamIndex() const { return m_settings.m_streamIndex; } + virtual void setStreamIndex(int streamIndex) { m_settings.m_streamIndex = streamIndex; } public slots: void channelMarkerChangedByCursor(); @@ -89,7 +91,6 @@ private: void blockApplySettings(bool block); void applySettings(bool force = false); void displaySettings(); - void displayStreamIndex(); void updateWithStreamData(); void updateWithStreamTime(); bool handleMessage(const Message& message); diff --git a/plugins/channeltx/remotesource/remotesourcegui.cpp b/plugins/channeltx/remotesource/remotesourcegui.cpp index c1d3fea70..7bf6f5198 100644 --- a/plugins/channeltx/remotesource/remotesourcegui.cpp +++ b/plugins/channeltx/remotesource/remotesourcegui.cpp @@ -242,7 +242,7 @@ void RemoteSourceGUI::displaySettings() setTitleColor(m_settings.m_rgbColor); setWindowTitle(m_channelMarker.getTitle()); setTitle(m_channelMarker.getTitle()); - displayStreamIndex(); + updateIndexLabel(); blockApplySettings(true); ui->dataAddress->setText(m_settings.m_dataAddress); @@ -271,15 +271,6 @@ void RemoteSourceGUI::displayPosition() ui->filterChainText->setText(s); } -void RemoteSourceGUI::displayStreamIndex() -{ - if (m_deviceUISet->m_deviceMIMOEngine) { - setStreamIndicator(tr("%1").arg(m_settings.m_streamIndex)); - } else { - setStreamIndicator("S"); // single channel indicator - } -} - void RemoteSourceGUI::leaveEvent(QEvent*) { m_channelMarker.setHighlighted(false); @@ -322,6 +313,13 @@ void RemoteSourceGUI::onMenuDialogCalled(const QPoint &p) dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); dialog.setReverseAPIChannelIndex(m_settings.m_reverseAPIChannelIndex); + dialog.setDefaultTitle(m_displayedName); + + if (m_deviceUISet->m_deviceMIMOEngine) + { + dialog.setNumberOfStreams(m_remoteSrc->getNumberOfDeviceStreams()); + dialog.setStreamIndex(m_settings.m_streamIndex); + } dialog.move(p); dialog.exec(); @@ -338,20 +336,14 @@ void RemoteSourceGUI::onMenuDialogCalled(const QPoint &p) setTitle(m_channelMarker.getTitle()); setTitleColor(m_settings.m_rgbColor); - applySettings(); - } - else if ((m_contextMenuType == ContextMenuStreamSettings) && (m_deviceUISet->m_deviceMIMOEngine)) - { - DeviceStreamSelectionDialog dialog(this); - dialog.setNumberOfStreams(m_remoteSrc->getNumberOfDeviceStreams()); - dialog.setStreamIndex(m_settings.m_streamIndex); - dialog.move(p); - dialog.exec(); + if (m_deviceUISet->m_deviceMIMOEngine) + { + m_settings.m_streamIndex = dialog.getSelectedStreamIndex(); + m_channelMarker.clearStreamIndexes(); + m_channelMarker.addStreamIndex(m_settings.m_streamIndex); + updateIndexLabel(); + } - m_settings.m_streamIndex = dialog.getSelectedStreamIndex(); - m_channelMarker.clearStreamIndexes(); - m_channelMarker.addStreamIndex(m_settings.m_streamIndex); - displayStreamIndex(); applySettings(); } diff --git a/plugins/channeltx/remotesource/remotesourcegui.h b/plugins/channeltx/remotesource/remotesourcegui.h index 6f8cd729e..0035875b1 100644 --- a/plugins/channeltx/remotesource/remotesourcegui.h +++ b/plugins/channeltx/remotesource/remotesourcegui.h @@ -56,6 +56,8 @@ public: virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } virtual bool getHidden() const { return m_settings.m_hidden; } virtual ChannelMarker& getChannelMarker() { return m_channelMarker; } + virtual int getStreamIndex() const { return m_settings.m_streamIndex; } + virtual void setStreamIndex(int streamIndex) { m_settings.m_streamIndex = streamIndex; } public slots: void channelMarkerChangedByCursor(); @@ -94,7 +96,6 @@ private: void displaySettings(); void displayRateAndShift(); void displayPosition(); - void displayStreamIndex(); bool handleMessage(const Message& message); void makeUIConnections(); void updateAbsoluteCenterFrequency(); diff --git a/plugins/channeltx/udpsource/udpsourcegui.cpp b/plugins/channeltx/udpsource/udpsourcegui.cpp index 5ac765630..da0f41275 100644 --- a/plugins/channeltx/udpsource/udpsourcegui.cpp +++ b/plugins/channeltx/udpsource/udpsourcegui.cpp @@ -209,7 +209,7 @@ void UDPSourceGUI::displaySettings() setTitleColor(m_settings.m_rgbColor); setWindowTitle(m_channelMarker.getTitle()); setTitle(m_channelMarker.getTitle()); - displayStreamIndex(); + updateIndexLabel(); blockApplySettings(true); @@ -256,15 +256,6 @@ void UDPSourceGUI::displaySettings() blockApplySettings(false); } -void UDPSourceGUI::displayStreamIndex() -{ - if (m_deviceUISet->m_deviceMIMOEngine) { - setStreamIndicator(tr("%1").arg(m_settings.m_streamIndex)); - } else { - setStreamIndicator("S"); // single channel indicator - } -} - void UDPSourceGUI::channelMarkerChangedByCursor() { ui->deltaFrequency->setValue(m_channelMarker.getCenterFrequency()); @@ -503,6 +494,13 @@ void UDPSourceGUI::onMenuDialogCalled(const QPoint &p) dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); dialog.setReverseAPIChannelIndex(m_settings.m_reverseAPIChannelIndex); + dialog.setDefaultTitle(m_displayedName); + + if (m_deviceUISet->m_deviceMIMOEngine) + { + dialog.setNumberOfStreams(m_udpSource->getNumberOfDeviceStreams()); + dialog.setStreamIndex(m_settings.m_streamIndex); + } dialog.move(p); dialog.exec(); @@ -518,20 +516,14 @@ void UDPSourceGUI::onMenuDialogCalled(const QPoint &p) setTitle(m_channelMarker.getTitle()); setTitleColor(m_settings.m_rgbColor); - applySettings(); - } - else if ((m_contextMenuType == ContextMenuStreamSettings) && (m_deviceUISet->m_deviceMIMOEngine)) - { - DeviceStreamSelectionDialog dialog(this); - dialog.setNumberOfStreams(m_udpSource->getNumberOfDeviceStreams()); - dialog.setStreamIndex(m_settings.m_streamIndex); - dialog.move(p); - dialog.exec(); + if (m_deviceUISet->m_deviceMIMOEngine) + { + m_settings.m_streamIndex = dialog.getSelectedStreamIndex(); + m_channelMarker.clearStreamIndexes(); + m_channelMarker.addStreamIndex(m_settings.m_streamIndex); + updateIndexLabel(); + } - m_settings.m_streamIndex = dialog.getSelectedStreamIndex(); - m_channelMarker.clearStreamIndexes(); - m_channelMarker.addStreamIndex(m_settings.m_streamIndex); - displayStreamIndex(); applySettings(); } diff --git a/plugins/channeltx/udpsource/udpsourcegui.h b/plugins/channeltx/udpsource/udpsourcegui.h index aa8afa1d8..68afee72d 100644 --- a/plugins/channeltx/udpsource/udpsourcegui.h +++ b/plugins/channeltx/udpsource/udpsourcegui.h @@ -58,6 +58,8 @@ public: virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } virtual bool getHidden() const { return m_settings.m_hidden; } virtual ChannelMarker& getChannelMarker() { return m_channelMarker; } + virtual int getStreamIndex() const { return m_settings.m_streamIndex; } + virtual void setStreamIndex(int streamIndex) { m_settings.m_streamIndex = streamIndex; } public slots: void channelMarkerChangedByCursor(); @@ -88,7 +90,6 @@ private: void blockApplySettings(bool block); void applySettings(bool force = false); void displaySettings(); - void displayStreamIndex(); void setSampleFormat(int index); void setSampleFormatIndex(const UDPSourceSettings::SampleFormat& sampleFormat); bool handleMessage(const Message& message); diff --git a/sdrgui/channel/channelgui.cpp b/sdrgui/channel/channelgui.cpp index 50a83d37e..f04055bcc 100644 --- a/sdrgui/channel/channelgui.cpp +++ b/sdrgui/channel/channelgui.cpp @@ -233,11 +233,6 @@ void ChannelGUI::mouseMoveEvent(QMouseEvent* event) } } -void ChannelGUI::setStreamIndicator(const QString& indicator) -{ - (void) indicator; // TODO -} - void ChannelGUI::activateSettingsDialog() { QPoint p = QCursor::pos(); @@ -363,7 +358,12 @@ void ChannelGUI::setStatusText(const QString& text) void ChannelGUI::updateIndexLabel() { - m_indexLabel->setText(tr("%1%2:%3").arg(getDeviceTypeTag()).arg(m_deviceSetIndex).arg(m_channelIndex)); + if ((m_deviceType == DeviceMIMO) && (getStreamIndex() >= 0)) { + m_indexLabel->setText(tr("%1%2:%3.%4").arg(getDeviceTypeTag()).arg(m_deviceSetIndex).arg(m_channelIndex).arg(getStreamIndex())); + } + else { + m_indexLabel->setText(tr("%1%2:%3").arg(getDeviceTypeTag()).arg(m_deviceSetIndex).arg(m_channelIndex)); + } } bool ChannelGUI::isOnMovingPad() diff --git a/sdrgui/channel/channelgui.h b/sdrgui/channel/channelgui.h index 6a78c8734..049bd21d3 100644 --- a/sdrgui/channel/channelgui.h +++ b/sdrgui/channel/channelgui.h @@ -47,8 +47,7 @@ public: enum ContextMenuType { ContextMenuNone, - ContextMenuChannelSettings, - ContextMenuStreamSettings + ContextMenuChannelSettings }; ChannelGUI(QWidget *parent = nullptr); @@ -68,6 +67,8 @@ public: virtual void zetHidden(bool hidden) = 0; virtual bool getHidden() const = 0; virtual ChannelMarker& getChannelMarker() = 0; + virtual int getStreamIndex() const = 0; + virtual void setStreamIndex(int streamIndex) = 0; virtual MessageQueue* getInputMessageQueue() = 0; @@ -90,7 +91,7 @@ protected: void mousePressEvent(QMouseEvent* event); void mouseMoveEvent(QMouseEvent* event); void resetContextMenuType() { m_contextMenuType = ContextMenuNone; } - void setStreamIndicator(const QString& indicator); // TODO + void updateIndexLabel(); DeviceType m_deviceType; int m_deviceSetIndex; @@ -104,7 +105,6 @@ protected slots: void shrinkWindow(); private: - void updateIndexLabel(); bool isOnMovingPad(); QString getDeviceTypeTag(); static QColor getTitleColor(const QColor& backgroundColor); diff --git a/sdrgui/channel/readme.md b/sdrgui/channel/readme.md index 339b2c71c..6ed955ce1 100644 --- a/sdrgui/channel/readme.md +++ b/sdrgui/channel/readme.md @@ -35,11 +35,15 @@ Opens a dialog to update the common channel settings Changes the channel window title -

    F)LQ*>pE$G^93pJx!;t_E%iM zY(Yw9ZDd4M3+C+r0%oV*KfvFK&_K7z1!91ghXxxlq`=%MGzFQxkFP|rW**GiPrf!^2jiLIAH1jDCNe`HE)y@PKWV1u5A=5mqh2rV#-zd;=3D%2jslo&F; z3Gy8SG}&r}M&+b#HcHlj^d$j4KL3EZ5GqDiZ&qvvBT9&gSFG*C2TNcY8AII|FGoYh zje#I2TYssO?WWLo0BRH~WJj14Tw_x>gmRb+1~$I^eg)~CkHjgYvS|CW8&EeIe+Z_lc_Ez%NkShOML)xf~{j(`0XV}>lVwv*HSt&E3I zRbVFNSNU!Tr&PQ6d4sWVi0jpxoNmX2H)Yskm$0R=yXb%u(FMLHkd!wP z$fg|t-eGZZObxUy;q-qaaiB-4!i4S)7axCib5m#8qJkcV(0}CR0n(Y<_OKi&NKv0m zt>nAWuwMs$!&s*?><|S$xUPAiL<%Kf;g26bnipUe0{DU3hcWORwVLq-p}WZsJuGd9 zG6u|wLEE{F?$hVC-vNW9XyAlW&cvI+rAs?oUrCdRKNvO|Vb5NI-{|U^pqv?7!&}(e_Km+w*marP&>FX(LP}?! zoj#S3);B4d-i=d9>##wbl|VW!bI)yoLsM%g#u_zDo--MmZc}lvZ&$0rnT{Md3roW9>0xIT{PJB1@2T8lDieF=(m z3nGJ@ZuLL2C?FPqM+4s)S@rc|^&s_ZsI+MfPXU|L&nRYO6-zUlu1c#Xcm|}3CMCc; z6x3u~jAq9OK>#A~CEd&h@YE8yFgDmAd}!?nVr? zg(GXWAB24iG4U9juO$=};R1md5n4dJm9wRhuw*7+JPH%f|am9XwDNU4h1?3pHcnMj5c=H_nRnawV^FThki7u)sGcgIcO?- z3JfKIBLWnMpm}hYlF^JwIsl=i%HU-gV#G!?Qt8Z$%p?vDuQdz*L47jXBR#$ttK2>M zy74z;E|*nOdF1h<8=(}V>U!08{LSRDRsBr{q%*BF`m@b4GFM8J2I#FA2O@vcB@W3C zsOvLR`u>rpy!ik-v@Pxz3u~?5PKV~hfJgTvcU%R?c3&^XbaNNRcuej7jblZ**scrn z5DN&1g~$a--1cw3eQ%RP+NIdeV4bp30b* z&>(Yn@OX#zu~PUcJ!|W3=xF?<$OS$5pf!MDi3k|$+J^SA55NWttE9Bj)*nx+sjIWV z(_6f~y^)av09;^O-U6}7W_6GerWP^lnga;FJ<;{f==$~R69USivVz0=uiZ7&cJ?;O z6p@UZot4n%U^53Dw-^D-*tU+RcU>u)E!4b7U$tB`pY$IVQ&Ul<43DjASz(3eXl`2`(7ghTO3-wQ{oxj-NfhXx0 zM5m+s*`^m&yt-qvPl)q7UJTGzi(37U$*>^r;JB|~!zx0}CM z8OG)&S9}55!fv_uF)~{Mljs>5rA%m?R^w0Ze8NiuPdyZRkeuWeCuPM;-D9-})o;EVWIAXVyw)~1H!^^ zK;uBvRu5pp5Xul_)~Jk57=+S?#Yj#|Be>aFR8-_{#+=f6{BfVlda91DuoXlHq@_U~ z1p+La0cdE+DJgJzF+GoFuJ>89yz%gmnmX!V@qTX&U0#-Iexg)2kk#QQulh~c@Oi7Z z@+$|Tin_+k?F;tPi!tDwGu~p}>4*xQUm&#E&O?6|6=AKc(CZfMUZpZEEGzt;KmoY80Lp(;Obp5*K$F1oZ#r3^!-Chd@w)WWVM zbJXplBPsSpAV(7o8AhWZZ8I&guEe!U^m*DUg$7ckF1M}?-Ho#>V6cv?I zJ@9P2x|G0JQ&W>^(;WrtUf8@Rpt>MH7zxD)-X+*ZS56HJzoYx#Q??{|G$6ua~AQi#v8VL?W=)zf^`?djh6nw6!$mRHGtfnkACR-)uu+prq_^QEh41W$4!1jdk-Ie z_z&BH{(BKn)_{M(DiIM8*xLo@9BxdNZI3g<8>y=DV7tB!&#jJjZ{Gl2+=&y^knLdX zMfs`f*zQR=pKQyuQL!ia`9X=SX8YW1&O2t$^>mf7E~=fMVKOwp2MD`kVX51Cg0GXp zBz7~luy)xx<#U=JyQma@^i0nWqvT~Sz70vS;c?6n)q_r74yqo#041lY^Xt(uzE@&y zIt#|jCBH5&RG2ohPJeDG_`>>fJ=3;|x1X%<3ex|Kx-vC-plw-u_U~#m77-bV$?a~t zVSRmlqE_A`pn>{EMv^E-!p^g=%$(tHS^2e40#8Uk$SNZ=fPB`SYRah*-^Rv9I`fVp zm4fZakw0$&2tN#G$(>Ei6?kfQA|!?iV~-G;19N6(<|_^kMMFamVO_%Px)VsFc@`7u zGD%B9`N|6kLU>Z2M25z?AMs?TjccQ-p_wo{J{b)*kl>Y*lapIVRZ?_(3kptv z%HUP?Z`3=iXO;3iIVWkvSU>iC!=Sr=dx1u1wYy0XMXx8VCdA6eMLczQOA@}icm34G3A;UyB0Aouun_Fkpy0ajf=|%049pkEsy0UGAUyV zSdA#2)hMTWR#v{TvFrfzVo{%sth<3*fH;l=a`a%g@FUQ$1WrTMC$|Wn@c;;UllgD$ zQ1J((rlz|5Sf!&LNw+iJZM|nk<_NGY!0@f#rtnY)32A8r(-E#5Q7<>~g(Ki%0N&xA z5qJo$v9zY%&oH9hA#DdPD~zuF@vIiju5%?Oqgy6bGWO2M>D2#uGw?*;;?%oil6S6H z+gvXCBoy$}L~kWORQc}Gc;fQ-@`b{~X$OmRXDBVFE2|UQEx*qyio55jm|InjTfgGx zJYIO-f778RuJq)AKtVo(GQ+*w53c%#d;;n z$Xu$>v?($&r$xkoyhIMNzy4BCQ20ATP2``no{G+5Obf3{hk}r%1lE1RWtW!r!JIJ` z=NGVnPLIBsd9YZt3!fH>WEUTwvbvVGHV8l=l+Cyg0EnEv+&>WCzi)!d+S)oo)TPC; z+_<~gZFWbGw6W`=zg?z7ZLjMvo$HC*u&N2%mPAoV&;{rfJ0Wy6AC_>pg%F*cHsM`^N6Bp`%_m zp_OMx?JIR*pAh_qc(C78S0^d!N@HV!dZKLYOPv%!X4~^DUi=KNLpr2is5nP7Yh22- z2}*h~uk10yW{k%|;U%24@eK-zmY5HJ{5dsdX=U}Xs%oErK#RS~vx?|u^Lq2FwvGQ}kY)_n(=U$}5~xUK(!l{+g7( z_R*S`uarUV^bc_{#3z^*HHpDlx_*O#lH6*irSjAkWyu(oO}R}O?;AAa6|$pt9SPGC zd*UZnd)_iet1Doj!8~b6!9&x}X;c?PqT96MLMpaHE9=_ee5Lw8mqFi_`Q|42w!O(_Y1Wfk9DsW1jGL z-B3{LcqN>%@}8M4!9Yc~c+aA4QnYevrV_jXJWN^kRIxoAbzhh!PFP$r_X-yM`Yh}G zHm9Ru-yVqU)=rwV?tAclRSnBo?mU&4XUbe>o)$}rXD;4PotfEaJnF8dGqIe+Abv5{ z+-dOHS8FU-cAb}s>rd6}-p9geH@Ii&4GNDulN8%+$5R)l8P~Jj-m*199Dl8Oxdnew z4&L4(E+XFFvxlwpFTm4o)B7JwfVmi;lGvcYc1C`oDKJV)p2M^ue5tLzIOxLt87b|L z{dH6OsJ^y-9BNeMIm=s_*!$9nc`}^N6-1&B|Yq+7jLpfC?SB9 z=lR|(C*!k@s&P@&&wyB$GF-fNlgry9GBVQgPw{Mu^y6p_4sYe(-&T<&?$P+5-gF-? zZ?YCY;Ocb#_3dlf@sLVZZ&YX(x^3ynKeJry*498#s0@AXJ5tvX)=?AODObbZZ@VgC zWtS(fw65j)pzCcJxgDaT1u@O^+BE^&1f3T88e>~JI!qniayLhw$TiTFmH0`={3Kg< z;H6dCb!{6b3$HaMK^-6z@yG!Luebbe)%(_qPzq?HMd)iUz$lEdf=D=%bxW)Ts)5Ns zdigLUWF3i+1R!e!de-t&cJ*w&mX=oNVf|HT`Ct#X0|pL`36q59L$$U+sLgh$tb%6uJ%2Bf0PAh4`5mp1SwkP4HpwPWJiIF=xqQV_rMV zrqkq*WiTFnGU3ILiP>|h1f{8W?<>D8i&8ARs5!Q5mM1v%FT0WjRTrghJ5 z9oz@5(hLK6TZV~ZteaV8TG|n}w4*yVY871bMXCf522hFpoRk38tbFwygY)8 z*nqc4rL_L->HLBh{yB7V<+Kt#Fy^-yc7za!^LifCbuUzR#-Fr+j*;LN1TS8vs}WEX zs;~Oww{xb(R_NP)s+YgcQj)S6+@4P9{Yoz>uReCs*|y^oL&SqU#{~jqLypNZd*xsbHC6oMD;M0t72vJ%oMwyzMz8_Oh4XE|ObL@KEHA|9 zj$VK7j2IHVK(pM1dmpUd)a0Zd)*nfP!*JY({SEu~J#X(-&rnH7JOn^PkZFrhEg`E= zLW!RE0|5C;A><3Hzk@TQP$3!u#R9dG<~U_Q^uqS`d|` z+u9Ca`=*9DR*&%>ywrfTi54;4tZ_eiQzD|G^164(?2H?lnw14PgDq$(&=C)Gbc?&E z&|~;O_Yf+@k9pt&t~1wefCfC^Iz5P3xV7^$OO1ZcHY&!AIDBo=2fslSA1TE@yzkvx z54*M=jlzKg2e71U?d-&r=r8QKK3sPT($V18A9gZc@ws{AuJ`P;z3|$NYY#_70T+Cb1{0)sYr4+E7U0AcV1?H)$w|2f~murv2coI@~tzsqGnnTW=c3hQA2 zjG{gscmyKGaL@p40DfC3HF;Cy*Y`fO`}8i<5AjjJ3Tz6Q#0r*pp7R_h+{~_^y+~9C z34rc_A&qf`-T`Ay@W?SJGdPjpa`-dJ7XVPsE@W|BQb{e%fEo+tU3Yi4rACrQmg(nw zevp_{{R0DO*iQG(FdSv_g}D2P^JA1ots>{31l5%A@q+yPRn$vaScG~VCQgvCS$F21 z#(Zdoj|}DVFCHGco1j@$hqH_>Up}9p!b3&oeqcS0=sZI~Pa;d{7 z1&mxzI5c+6B`L`_xX^XA!{@~B*R(e_{&EPG|I|0!UFj*j;-uvgnM$dCbkxbw(H3NBV?NT-}|VDy(G6+{f=iH)g$_*8fH(Rp58-m)9> zUcT)R!C>?v$!_3$tL$&8Io&a*mH5=r5l+xXhbcY{%9b%h925UNE4%jqUw4Kgys)YA3AW)5hJ8E;$-I8@@Z??CHKX=V$m^z@l(^YN@~AuKDCzb#Oxp4Oo(3$Wp! zb{NlSXlOio{P^;`8ZPelhJ`6n{nI{*`a5P}yW2xiE`AU=g#NlmyWn(OSU4awbp1_u z8_Z(DMV!(Pu5WB;7{+H6)`|p^%QTHcR<=9?Dj3p9B8OcBG*lIr1lufXo!!jG#T7$)k*{iuu9SO~iA^0}u(2VHLhW*^91s9p!7-Oa z)Jy=5wf4G@z#-ji3P=k>foR2fE4uB_BqIXW_WE@}{`2ICTYdzX9MFI*LisNEL$DJD z`arql$o3(?F7_i0)PjOJ9yYbiC%*LemjWt9zyTj*Jf#%MACgKtglzS~LM*JTgoXS0 z`2p1WxxBoH=Hw&R>$$UsS4KP$HVyeG^z&H`@#saxh5)-)rUc6RK zVZovjplkiHxQLu8O$eN=!w(%jx)sVXbOAoM=OQ0S#k5=1&CEHuK!aYO95WBD=oX z^Ee3$lMJV*ufsAj8och3v8v`)R|qtWSR}|61YNXJTAy^JzST62TU2_7z~230Vo|8b zmzGM-ja*SVg*pi)jPJzxVf1|ZfFVewr$RDi1fJopnS5}bYQ?U?Q2ufW^{^ca1Q9`^ zmxz_vM69kRM z(3nh9PM!h)xF1Ryz#b$Ah+eW6-Kg=??nt`N*r(rMS2y_~{1IRp5?6J{N1QtI9ekhx zJO&^Ysx0kseO-<=#SSFZpl6cTZ9`U#S5XlhI?I_OHtY9rb3ecf^Xe3qL+D=7*=PC1 z=)-N*!bK~a-Sw(RC#_8_1SXolb2mP;I#=|*hS~g>`MvEHIa<7FA=gaC7R&ke`&(2L zE@)2o4OYG|>F-^*z3s2HPFJ`^?w|Z6b1-ebiS^^{D48#j8;ZF#-2!rNTXy8w_2!K` zEE+88A~H->$7+N88K`!A7R(kYlL4sAxH58g()gLn?%+ImcYrEq}Y)F-OGN0#K zIs>G&pQ2E%qL(0s%(&H`P#H)HIq`~MyD_GaEZ?I^kQ0WliY+MF#pLU~J?ixb+S(?r zqZ9q^Q9a*B_FecKId^_iv#wBN^=NkK;~f##TXgQp3{;2dKiK)Tx?tNALz9GF=V(9v zT``NlUYceM(C2h5oD?l94Ul&lV_@C=EmO<4cCfL`eY$eoA~rdvGb?sC>x;$_A}2<_ zgw8Wg?&$iT3~F z(Dy&x8`C?p#V zeIlk-O1E+129TyLkKRj{3#iRp?C*(sn}1^TiNLit?=`BwuIO+&ge~;FiH?62bYTw} z%g@BFHFIUC;~&y8O)C3tx<0+c-Km zAhy{?Ut4mNwOM22ip=j9+0@)}`=5b3kpMMYIBk|=w0=+T{8~^KEiTGF|c4 zAYq++D>OkpLL|_mOOLzJdCZc3)+wJ?Fz{;IRiU)1Q69zLKHDJ2@eP#)gl@gkK@AD}LK*v?d^+jNT|oA!{+8R7CU z&levK-hM?P+9qAkQ%#n3>PLXOhhg-I+3wXmrlskRG;F03&%n}bjqqP3eD>6O zHkLw66f*u7#JKV8{E^@K=mF3Wz=NPz|Nj*TYVrTR%r+FyAb#&IgntCENX~oCL38ce zwduigxC!w%P+>QOqsJqYOP#P8W4WnmXtcB~d^F`l0HA6z)8@^a+uxAaZ|30x2Rk)4 zS5{F$BdO#~w=q)T1H|_*-Icw48~wgmx2))-xW`QKSLDnRVIPE^2%g1M3{I*S`#X1I znO19}y2SbOThQO9$F5gWa4upg(osb3iQkI=xgenw)G{!>Fq$DrQz@Z&bF&2*$epl3 z5zQ|0+l(7=M#fuA$*>Hw5bRX>6Eb9>16I({i5f|RoF9h~xavkNp&cNhuz2ykNPV{q zkBRvUqaFHBRx~2u!iZ=Wg7BjDtYgjQ{E*H)#|Yx+=tvG{07^~)E&BmAXP;1l4`P{) zS$7JNgnJkkK9-k1gKqGj*>|ue(xuf1I7)*WI>@kOq#08Wlf>*^edhvD|5xU|UIOiTqx)$AV#(038^K_QULchE zWl|CkrY3ew5QWgZX%)NfJaOU#2KF+Xed4q{*LvU2Z!J-ylYm!T?leAVJbcata8CGPe5E|=O%aLP z>G13AA{MQe)P>@>b20UL78aT#w-PvLusaW-eh)$-$Z^ySR5fmN-^hr%Ii}#tA`ffH zX%gf>7nI_|$dua6%q+kCC(B_yD(`3#dXI%lg%nBy45%cP4e@1EIQL=%8N?*!4m&W_ zWfPMOw2H|(MKS<1W`z%Rh!rM;-n`z%>4#;uf6Xcj)x%6%qW9;AB}acax*A6U z$VvM!)&T7GP)Xof_<^R9Yk7SmhJuF=AHD%p;v+XRH;3BIi*(c|Vqs|N$;`>NG6Y7|QscBBq3_*%~=r}79qlp_2GMR!+Szs$xBQgRwKllYhnleIIp$%F+ ziIDE_JN6`AHOg&X9bO)(m_KD@E4Qwg(uQ36F0Iqx>M(ZcVFH%4eY!{SyZboHS&iW8xr-xLyu(N5-695d z#><~Q%dsE^c-ki`%OO^D0mQm;$qhHsHIhG#$4Nbkmh>U{@8{{gAPk|Q0{#>(VC5YW zvK<~|K}{93UPqXGfliLO)1^pV7!kdkaYs9bEH82e1dm{uz+Ht{fE}(#*gWrS-1!zx z1MqX3)_PK!R#(^52+;n*^w%>wYH{ra&l@5U0}h4z9oXaAFMSV3ETmuFK=@KBSRlYF zF&sK{2(?TB+YN3YSnmj!`~eB!5+Mf+FAnA?Y8rfppmLghNq6q$5x~Of>W*@)Bh`RsuN}fvD*q62PLq z3YY;p14X=b1hw~snM5HL=Efc;vdpF%=|Kv~&X&GuZ4`2)z>! z`xsI*bW5VJHytr3T@RvN9^*WIM?YvzyqVT-Z-}$vF+nVVkwWHB4q6Sj9UoQr+zSgD z>cc8`x05iVf11k%gAG#;5g?6=g=YF5R^feBW z_hH>nzsXBA`&X*YAY87je2kGuMq{)^ob;LhgN2&naI$yzJ8H_}n8p8j?tDV1siMvh)ZF-BF7#HX?TT*+soL01J?`OKO!mGW4hBMNF!M$_APo8JVej|E-o*0 z`xI?GbET3&e7#bm=gbeu&=t7>v3J+6g$N5d9A^f4!LE~}6|1ZSX$2DB|FPNKi z0>c7;1{LCXekU=UK?xLwPA|%J`rHF4!vNV$L77_kDEy<$oovtddR?_Cd3}+Sc$x{| zPkX5FrbZI)wc-ZeT+PlxV4Q!!mW)RMT@$Ukmfc%tLxg=CLT>Ta1VUNl@QI^4j< zwpBd%9VI1U_8>W=4Pz+aWJjQg_a8ji$IbmMDg-A}KySdj18oR!N`xGjtFYhRf~U!w zKY{Z8&%zGMM4oqd7s zWMOv%#!BGTR2cY?o&THhq^MI{pLyd)ZR_kpag!hTc_t?JnK?%J0NT?0{Ct3Cfm-fhm&YszzesFS$scrf+#w|7% z7Z-fL7w*gSQU#hCKt>e6_CDNw)|J$+bW6m#y1Gbb185Rgxs5#iDE{~T{SRH+iC2xZ z2O=algOa7-91--sVv|U?M3F#HcKIiFj|Q9{hs*S?0;Ug98M1a+l2kOupT^Mt+J6xV zW+oXqvj8>^j9wI1o25!{inM(l@XY;p~n z8{C&*A#EjkfPMrT|KQJhh`*Cwk^*-g1d4%BmO-PArGAr$jNt=@m+|J`_9xhkpS6yr zWn>tFYepivLbd=KSlPmLg3&bA>q_nzjnuuebB^#jd6Fbt5O**3^4OXQa7)mTDA9Z2 zrh=^WrmI7v{2Tk5nMupaDyy##fK$qLc3276fgd(Wa<~MTrJrLq?!+Mk8`;CKu#Y6p zo1E{as2IYdK&B|J`x>Hye$6w+?6a5||-5=j^TwB#(k?Z17$aYAoEt(P%e1swZqAT#9 z-&me>gl}Zi=MT3o@sul+4Arf{m^A=@kyfGF^v6L4$%73E7wNqq1SO&kTU!og403YV zmjUNOp1hTjQRQo$HH61-d=s@F;1cVu!h=h~m=irRBD_z;XV=}Y4oW9dQfQec%yyPh zgSgs_u--l>jL{%P0+0z|x((MQGurKU*rqT&*$z}NU~}csj35QNOSWQUkMJf1E3d4; z38yP)3Xu*AakC;)cD;go|65#ASzTU!>SZH9-1wtB#N-X6FgTwSpsf*QpM+w1RLW2b zv9*!&t5j86X?XquMhl=VG%_mXcQM9Xc`H3T8>tnTN5EBDd$}Zm2IAsM;M7H-UB97b zU2?p~xmAkm*}PH4o~{`Zef?=oSG#=dq2B{_FlFS6y6v{#59x%#p$TJmfV%qbNY2E{hQ-)wk;LaIZ1^x$>Mq*{{^H;`L+N6 literal 0 HcmV?d00001 diff --git a/doc/img/DeviceWindow.xcf b/doc/img/DeviceWindow.xcf new file mode 100644 index 0000000000000000000000000000000000000000..fa1ca2efbc70dfcd580184f584942a1092449c5f GIT binary patch literal 81246 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 KL%-{ivi}E+8*C^5 literal 0 HcmV?d00001 diff --git a/doc/img/DeviceWindow_bottom.png b/doc/img/DeviceWindow_bottom.png new file mode 100644 index 0000000000000000000000000000000000000000..eb436eabc2b6cc349bd28f3c6c0777aeaa03a360 GIT binary patch literal 3276 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 Sb&O|o0Z0pb1j+nL;=cfUXh#A7 literal 0 HcmV?d00001 diff --git a/doc/img/DeviceWindow_bottom.xcf b/doc/img/DeviceWindow_bottom.xcf new file mode 100644 index 0000000000000000000000000000000000000000..dc4a86e705418c026b037cc0eaa7f43efa2c9d1f GIT binary patch literal 14194 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 v#!Nx-rD7u)iL^^cHOv184}zV? literal 0 HcmV?d00001 diff --git a/doc/img/DeviceWindow_top.png b/doc/img/DeviceWindow_top.png new file mode 100644 index 0000000000000000000000000000000000000000..5cae79348d0c1b8425d7d40fb2b4dee2a464c7fe GIT binary patch literal 11528 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 D!BUnD literal 0 HcmV?d00001 diff --git a/doc/img/DeviceWindow_top.xcf b/doc/img/DeviceWindow_top.xcf new file mode 100644 index 0000000000000000000000000000000000000000..89aec7d38b5609d92697eada0f976f99eb86f506 GIT binary patch literal 47954 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 F^S{C_LGb_p literal 0 HcmV?d00001 diff --git a/doc/img/MainWindow_presets.png b/doc/img/MainWindow_presets.png index 31791a73030fdb38a5210d6446c72d36a58f9494..f6e1b7096fd8767d73ba77057ce27bb68991b4bb 100644 GIT binary patch literal 6865 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 E0{Jve#sB~S diff --git a/doc/img/MainWindow_presets.xcf b/doc/img/MainWindow_presets.xcf index ace00b859b05310a4052d41801ce2cc7cd6cf3db..51008e7cc6439358daecad3bc31380142e1055ed 100644 GIT binary patch literal 26513 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 literal 24207 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 iRD?7WsT8S_>d>wxUD5t&B| z`aRG3T=)0B?#F%q{ktBItLrM`eV*rW9Iw}Nz0UiW6s7i%QInC7knE9>zNkV%vWW`+ z<=?Rl|MFpU