From 5abef702e4f91240caea2be71b886fec9a8473e6 Mon Sep 17 00:00:00 2001 From: f4exb Date: Mon, 4 Apr 2022 10:23:52 +0200 Subject: [PATCH] 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$ zSC*G#gKaKSncv*E6X$ulbxnfiOpVI$`AR||41mx_=VEM*PfkEPJjco(o4#INHnAN4 z`K5#Hj@RRzeS=-coTFpk<8OQYP0i(-^dgSZWt%@GyVj1o4p&&6zzMe&)pa-DP;Tr5 zEkXA8wPNJh1UY)voT-JA09HekD~!5BRd^9;IY{<^>&xc*{jU`GTkKjK8Xf#jT69bt z$ui%UWDH6TIIjKFd)1@x%xpPiz6B)7am&;Gg|U|}bsJ!QT3rE`_wJ=j3rwNuWQCb+ z=`XK!|0(4_sJxOX{q?oYH@Tj+~N^I1k=vXaN#-sk*_kM1OX!6w@zrX0>_sj4^@XqlC6>?ytL z15aBvj?v6}?u+&b$V!-XT5;{hDh$4BNIrU7#q*{oG&^ij+m-k1^yvfrkN_E@CZrnc=#<=TXdH<}I<)DZM?DXGHw+_rJ0tCoe2lEnkkzN(Eq zPoBfuw_Ar!Rn#s%6B?^y-s8M|B{%7-II7Qr;wss6ET^rm`MyngUF1^!#Q z&0$ix51yM~7iRQni;X_8I6l&;{1{+#fwI5s!ky?6)myCSyJhx?z-Qf}%XwaMHMa`u zjpOPEj)&oP#cUR-1ll(Z77O@CXFqM^4L*FPRGnQJV+@4fphp3>uT0v(pz>XI7HOLm z=&8=8a^!+pV&QucR6|n)r!OOKUUa*hiD3e313x^y6r?TZ+E>Q%#2d~odDzhUJW-FQ z@42z)ruc%5N_x-eGlt}HTTUF(WV}!b(gfAA!7wOkmt3kJt9U8}dN>yQaQg5)Tp_M~ zpjdIQi#L4Pi|c9oBPPJhb&^UlD%M_;nQtYs__f|-jcd1HIsC9}Ni)um;nnpuW8QQ( z-?EJrGWb$Zx9EPoU6Tfkmo8JcbX(!oRKaSUnZCds+W_pX}8TJ;sOz*qsp23!x49^LucJ69xL zeSUne*4LoU{3{hMCR0(33eAvb3zb=TVQ}^`T>)ea7%d23IMl6VhX`FPxcv6IkYbi# zaG7f9?D5CX&YYKw;y&~jw=1m_QO{qqHq&oj{_g==-H6N45 zZ)A48V<*eg&jYO+#L4|w_F9wH>{u`Hjwg-xuQhORF1Hodpw3bujGPd8>vlXRqf5S#)@+W4E*CV>a=yS zz%PD+56o}HYN}jphq_sr#-Gd}E`k6&t5I4&D{g9Z2UNJ(e@##2(t=sHOJMAGo!qik zyn7l!*&_wfXB6==>2ncn7R%?g4%uw;nY>#Y;}WY%uUR&FUu$al)j6n5OsxW}MWXAo z4Y#3OEMt0)2_A-qs$J0A+c(Qc<``Q{l4L5D1idZ`>*2z_3&mdiDwKD5kc#8{oo*YZ z;$klD)EdBA#6e*OkBvb`o!&s6tEyGCo4YzbN(s-p6ZG^aoo!!emS(NMkcZRVg;9u; z;uFK7rGVRtzQTEmR28)ukJF=H6tii5t7Wxxkx)O-I3s*bdqdjv?!nhMh8kd*HJk7~ zY2jfkVTn70h9ik2@U_NeADWG;70weZQ)FL-yxU8skcNy_v5;yJH#3X7=HQf;0h?Tn zd!VXP#@1SCJ4xHFT;(>VDyVb|8UmK8hNoX*-RpgM7=GR1-6VqcZ-)0+X za<)PkB~?hP0k31B$)b3(`PK4Kq>+Paa!5Wy8k|3MMqmr`!t`rde@Y3}oc2 zRdLDWYKkkY4(q*TAKX(rT_zY_D=`N&jGHTcElTly6JsD&T0&}K9T`ZrXAr}@Nd09s zCCe{}GUAwUM#uMGk=9kq4DPAm=Hpg*sQ)3a^u7z>?^8q5_#%JgCG~RiO z3hSS@SdAqtn{!8-N9AjV=bV$ia;Y30c$tZ!1G|@AQ|Z)iuNbs(K{@xW3Pji84b9{3 z^V!B(tR;4o3|Ubp=d==D34O^tp?xugZ0&v5oz!5VDWysVvHUt}+P_j0yeZIy?4qX< z{8?amqAeassThc*32ea9kuyi^tI9myVTQ4KWumd90;*r_834I5!E%_w_&H3Xd(^vf z4y8*krc%kY3)*A1f>$WRV%KcqH_k_DR8&q%XS8cy-3Qja9F1@YGin-swRX@To(I9IW-oLoZ)2b0xq24a>oK5)z zo1!nhWHbKQbXJ(}18x3@hfVjMX4yAgxG~So=jHdyGYiWDzcvz{Hq-QOtv)yiKj;!? z5_oBKX`U&4n*0c0?MCG-4*ZJk$^ZhR?sb*%UaHNb?!#-sjR!6KR%S;=H%HSRJ&~+d z$D#-y>7hQa{g)JDNF}>bLU*g8l-=708ceCfMb@ANWp)6kjQOMkEj7-lFl((Xtwve> zqbOz$x2ue2ywsF|{uTK-ESxM@@W>j=D??-Oz?I9ZTRkf}oT{W!2U18*lQf6)+gPVy0 zsShm6ZOk>~3=c;1O}FFyBU%+n#!1Z01t+xYM*~)s;FU>}TU*aZ69`*=V2|EXn zIoxJniRleCrFekl(OWTn&Dcqyf5Rq$-@lMLk@HG0Cd$pt%=+_~iEa0Mqx?8WDV}@%`r{ z9(W7^EhyBUVcjD5!N8~9Z?QWB+J4gK2OE;b=;rnTOqC{|HE)!ZM^nTNUdX`RM+Q2p zM;hq_<1NXh@sBnTXQwS~v1Z&JMbQb!q~oE#UWf&~t#!D&?L_H2&ll99_H>zyRq8=t zr0q|Zx`1OFX$5_lhOW^REW(OZbT~gxzC2WuWFJ3E?Cnltja8~UYy>wKFg{8 zE{q=?3b2#;$eHVKuF$CIUQ!H#)tMu;avc|yJ)z!x_DA^Fm$56y2-AxNhWotV=&w>| z6#Xbzc__?D9{Y(G%G(=Ej{(lsBUDq^jJgkV`lpy4mWC1ZmG2f){g5rm^6&|D4Ff@<=kEg9gtzOm=jQ^lq+b^}WEmKUtx@@v*O*@!FXF4on zk$tYtZpL7L1RUp<1;tTyjnVbPH=VltoVy51TDwL5M;pT$@6&Z6OP<90gSEH)Z5)G09C{UnQcz* zC^MKc?oz(p3^#qA&#H*|P?~PB;C0z=OPSQDozjAuE*fMn9c2Vv;5DmcJR9>SywSTj ziw~dDp0H^>TpB6I;=G*sFg>8^-n>fc$eE=VvnsQj*r#R#WaCCX6inl)3-2$wziHB` zmY4NS$a$Z_wA=^~s?5hf@wVngwcNj=&E?g?R**Sy7%8>$Vx~n9+@SnXFRM%J`MOPJ z>;dqYyD9F+nZS?M=1Jpz>GAwhW23sH9Cs+ghM80oLN|wm ze97Lc_s$H~KbEQgTykVSeYX3aWr@p1je5%sPIM?eor8YRdh-*Z&3gO1ql=~T2&7wm zYceuYA^N@<+kz+8bhw=g@Y3QA^d!kQGz={$&(w1aJTbW21b(nOOa5Usr;lP#Jb+Ou z_Sq~)YhK>!@Y@XNPn)d-5n=VTB!T!Kw&{`qifAsJV6X?~vsl)03UQ%qI02{#Ja8 zlm)Yuk|?n50SH)jJ6S+&XCH3@j|yIg5jYveLDHYN@8~YR%REk|$iUz^=6m-D+F-F^ASuO~zDkj{5M882)n^qPNWWjmRr3TzxjU z2&1d(!(%s9_ZHU@2@k9C{I&|avgGT=@0?toJoNl>GW#y;z`=FfyJc(LXSMY%rNwI7 z7JYdO%d4%I%_tk4_--Mn>A_AZIu6fub^x`4#48 zN_?c#pfd9pz(DtV&0tnzS9i0A?`CagnHUQqla7P_^ICha3Qmg(Y!DsJnRvOYu@~zjk$;8qjWzqzOIMX7HG-&J7(zPv zqA36!)foP_`hnEKi>a}XJ!kp8td+E)fZ(u3pk@Gzu1KDDy;of77@4`IOiAb)e_m=f zNZ57km!R;3oC`x`HOvIn0?6EbW|8e@wY65M+p*lRFXirgue_<47iC7$OiBQqwJKAO zE9=(=S&_$xua#tq!DvU{3Dgr)W{IR$??Fq(Dj_EY!?N^AsB|k3UYo)vweEDmjrYOU-OucY>zO3nb)&s#!ewwelTv z9-j{`gJ4NVI;D6D3hDw)qC(wzM|e`+ZXXlgeK(9YAM);2T0hFO526CAqyil3-j>jI zQy4O=uZ-943Tr@*5_pT{pIIjd7I59Py6rU0#&M9>OY&kRSaB@|Rc~czlPbn`nF<_S z*F5NS!)|k=d*X!;cRDTlNM{J{iSqk)jRtLE#j5Zuq;L z*K(OTY!_;G%@&La=!Yd?>EZ;PR|-tu?OoyLd(WwQ8-6c9(XW1f+a_!E+s*jp&Dm$tg7}=+=#G@e_Db{(GK+&T%(;b7+F~T?XkSYO8Gz> zvW|arTE+`8hX)Y4iV5d$8?Iz4(X}Ou5iT6Nbbg!69JzT;u$WwePvsi+hBf+?)--ZB z2sp-!y!&HBthkvr(ri{KXHOPR!}B5XnM-M=7})O#gWXb)YcN=~R{@j2NZ>%+&tQ1F z9%-r0v?}Qx=L%cBmp({YF?={vE(&8fvt&);_bHCTPFwp+NQKViXXmIdJ#;g5-=sy@ zhZX_rO$?L??p}s^~w= z^tk?h-=_n1Fe+v-;Cer^;|f^mvsprjmtH_E#2stoVA9dK@k)VNA$;S?0PmMLPZV%V#uIZjho3UC0It^D^<4t#IM{peIm`+Dw9%0w|=I0(vx;8xw_Xb(oB9W60nPkSfg z=`0C}tb(T#5@n0Q11vFCI0rf2t;QB!01hq3drMpks^z4FvBs$pTrdU%Z9^2n7A1}5 zRgkBV^+XT>>@j#Gz|-E&!4=^t$NLKxLA*Z|LwEtdOz^gHye3+@03}Bk3;+&>gP|Z5 zPnw?B2^p$V?4ncg9|8 zdO6^co*)NTzEgNX)9_Q!)IK@O-I=bQIczKC>z#sA1J85bC3Gd+gI}1cU zAf8Akh$t8evA2i(eS|As#hnQ9dqDr|2v*!)H$E%5RaP$02VTiNG z7~qkoq=`zvrKDj{s1#IETue+{{4XP8jEgI=;Z89{pvw3FjKjTLYT`K!GFvcDBY4d+U< z@H*}I-+SHwlPj^ySM)oxaLK z?WAT(d~*fNcY2-i%u{eD3MujGS}K$)WTY2Zm{EOQw~2dES9N7YL#e)r_?OzGip(L= zQznI_T%5NgmV%N^3Lk`wBnGxmvSnXJK54Jt7VM;oB&V`IOM0#^P{uX7WAoKDs_}Oq zto!Ev%G(!lw4EW(nG_xdofrLzH==(^`rRLBYBiZl@ew=OnA(@86s(JYqdrTXi0j;^Nh_E%knldF0M9HBstn)%>u|)q);WPLAfUx#~U2O7!Jpj_Tg%uc>OYZjS0~$Qc^MK7f45a=E;2NHj}S_pD!_GRMW{lIg0j7e!areHb4^`)jDY%1Q3M@4NSY?|bj0 zk{vZU!o|tk34$OONn~g=1X)#*x1&9<{M2@*5Q40qrp8P+Mx!QJr`IZ!Dgrj9=m?lF zD-{rAzV@U*y<&(@->eQp&M;Jkt5(#u`n1`L7SCRvr`dh*!n7MfYguvzGjZ8)asRj9 z7yGs!6*ppE&)KY>Fl6(3{C-B3wq7<(Iy1g)%cG>_!`g1QBt|Y>Q&M};=RX&UJ!ld; zY8w92!S_#xIC;;zoV(PPa8D@oZzynEnRn;M!*Ns1W%_*& zJ>GkGO8z-6_?g!k<7wBnt-~FHcDNmR7SQ&x{Tp6&g3vbQUpV55zral+{4$ZEjR*Gb zp5e9Mw|RHj2+!Jzwl^r_jyy8rFCSUN{(@Q_p`{vU0m}+!wZb?2=pYpbDXbGoatt+P`AU#Tg z1+OkmzkKNG%AkQ?1;)f6+vh~f8g`73ybI&q?;VkGZXQ{%+@ z0sDt?%2kbqv!zFsOYdbg+SyEKOj##tIZ%FM!L8@BMqene+s>IFp{>Aclh3%@N@ z{5(@rDvKE1cD1Tpb7Nr(0bkC_9^Jxy`+xQmIKG$tkZ|2H)eU_gSX0f+zYX_i(q@dtR2j z>EGhI;up7mLZI4RLFS<{9pW*1 zPt{$|GrbySdJ1ish813B+9w}S``u+Si{b_>`@R)-Omj?}p(tREl=S)WtJ$wwpS96W z?YUO`&S0#FTW&Qj-&`=bOnN9pSfw1Y=~Z^^4!Gj@gX1NB2`gXcKhCMwZhB607P3`k ztDYZ6G3Kpf6P)*CeCIx;e?*M5e38$SU8ho(@0qs!hdR+4XK`E_vEeb|;9gHDfI~7f zoO1kY!q-eI@^qG5%d2|kx_@zYyi;{fV)OLi!QS!)*Z(>kIasClxH~J_-`;lC&i!o(9CKbII18>7-ZI=_*}vWV?>ftBceP#c!UBg^bbb2&l(9YN6MA!mCWAyC~ z5h0OAeMBHzN>ZwImZkuCpCyyyALDfM^(sq@9H$d1LJdF-pg!hj@kXWM6Iq|tMtUsm zbT0&?`-uNp^oQOp!5~U16@+T>`D9W_sE9(=E0Ak(rCeb7$YL@HJ{#ju7qg10w1YM^|vN*_P5rAd@u_&oifNZgYUIb!2 zff}{?7_C+%qL8)1B&0>&u&{GcB9#UZkwPlIOY<^fUgx*YEud0bTrg~rtpLS4lNiti zgxrz{%{uYi$eI9K}CP)GqU~e-JDuWfnWC=Kk071quSONxv zLhovrPVVXt9SiBd;Y4T&=ynW%d7U;J8!_FkUcgrA{6R?&OlJb zDvt(W>7$E`GeA(kTJpB4*KU&oqk~Z*4Rg3}Ywg4hxaSdyK#=W8NoYt6_)n+dyh}Rl z78=rUZprDnc3vI9ESXi{ibJ{tFzwG>#qtNWRXfLEjdvrlj$Iw^suNtHC6su`aZuMd zW(QeDy3VR|!0}M$=y}M~EcY`bvPbQ>xVJ2^<4MrsFO=q(-Ct2`&rm?dKS4~lT)Iru z)}Ws2bahGVU2EvJb?|72Fvotd?B)hgle3nR2Qb|gs z5~XsG2q!tEIA0=(I{7T8UFmygXuH1iPuKOG|C;N1XWsXDe$Vf|@85Gj_cPDCi{V3^ ztz)JGfk0-{Jl*}kUlYZpr4D|3#l4h5AR4R~{{WdEGYTq}h`9U+04j?X15iND=RzRz zo{@Fy_YUCn-VfPG)R_Yk^NP{IS;RP`>a^t1=T3x{0G|5!^cu#y#>E#6J@%-+E zZZ)CCJp-wS^WML=-g(O{*y>=$N=A*k_Ce#}xeVWPdjcde;mh|A$sfdDQn}W*v^`_f z$1#^*w>FHKGmDl>`q~bcsy=ej_d^>V@aWTT{QfLz9{&2R}>x;5~c`u34Wc;CHo@e>WC_N_89p*uc2-Mp)ybjup6U^8Ti@Yr)x z9IiU}mB5f%e+%V1xr`ujeG%#EyeAH8UZ{iKxc&(Cc<14o8$OF{ZGC-A_Bpn0Xw9k? z=UG7(zf4}R)COa%)}}AlD386D`bydw7fi*au?-?IZYG6ef4w$GYMdIh^p!oneEN{_ zRzKI-)ZcF1*KiGc@Tj>eS#;yC8=H-wVSlun&gv>DgcaO23bLS9CN7A;rtvCPndL#M zwQk{-QB>9Uc(Ud{^=#Xv71vf*x}341Q>>=WX;KTU-0hx_-F+~9>)eqm5wVEmbC48T zj(XT`|BXkXxJKX3G?Nocj&T>j*H^I|yzYddo8}k3-;{Nv#n0Pj(BH2;%OPkFanM}S zazSjES&|#z*K#x`aJ)mO#VBfB#PJK8vyZh0MwAV%4II%rSn(j{dD&3M%G1d^^u`)@ zTBSSPJf6K|Ise&tQ(i&z-u1@I1udO`UB&qc+w$I3CF1&b4da{d3~x6R9f@UMD%l>i z`qD0F={#v$f{C5V_+u^Tp>DJzrc5FpHa}N_s;oB&EJ(I*HdIR@?GoHxbopcE);jp{ zIBon@OJnm(qpk>o44FA;c=6yNTy4(_O8tuF-#ZFpv^wYPx8=pI%I-DIbrUrY@3fg% zTsQHjmq=}sv}~Kt?kuYNsd8%Y;@>g*!u37KHaf;W`KdY@_{6*zVNOWN-Wc-QcE;kn z#dllFS5DnAagI`Jt}%WSZ!BR97QIVdAh#S^+|;!Y!rRrW$MKpsj*+Pp8I~vB)efWP zTd!SVd>)>3+|uBNYVWyOrUL)!C--`{#p{I{jZw8@b~oA?sKtjIUDVQlk@D6it3i|U z7O?KcrTAJ?#+wY&O8YL1a&wLRz3z{_I%7b%~~<-~Qv8MKn8XJR++l(w2D7hhkd{jlzc9 zWGB*jxo4YRc*kgLlxueHYwS|JJE*ZC`0C+G*ZRKJz`6qo9o*5A%XHjS+;$7?@6RjK z{~%M1%V~IuNB-GOwOokRJ4u#Xr0X({JgE&GN{}XxCfx|6kkx|ZA13N|Xcvt90lR`% zHBhHOU=~2S!=6PzZHy-Dy6Kd=df|u8vozg`JsLq5%i(lsrxz5iyZ=JlL$|BASt)rj zp+|b({ikJ=rh6I>v5-U1{-jEw!~C`T95FK?ta zwcdJ@`^&)_W8Djjs8}P<+T9fkm>11tVc|<;Mi%zkryi4RPvCDk8!dKea>^|)?n?$T z=k6GFblQaQO&N84PDygo{Fh;6tLba^3o)UHveykIa23Ohs)X9LsQ&eq4#t?s$Fw zN5X->KEB=%)=kUbt9v<2j0eB$Gq#b-_S0N zFS@=AL3tXSn;N|;@d7fEyG9sry=kP*WxmVKk!9_$@oc%HmsJDzo^Lk)%>Z9`&WvX~ zbnJ9b{b2rTr-&8a`!X4~v$@aA`_C-hVxv`EYMryB@mV`|^=2#E!N(G)*-qL1?X~Ts zTdQw+TFi^ym*VIbUEtLAG4AxX19$hY>jI49r;v>LlTCNq+7tW+=cX zGAv9c6O)k0sHiAJls!Tu;UUpPA`ywgATby?h=5C@g)*ibE|gj+D5g2w0V!L;7t8n} zAymQ1WQn%O$S@e#5B=nyKuo89rWZor%9&y$8i7Iz1jw&PNM#<8AjynFe>+0z z4{q{EKR_zlB4Gm_k$_NU^)&^RMrV9xP=v(e3&hHxK=HmY#$kWPiML21lo$>h2}A$_ zkW>o#hyDgH<8!}=^^I?e9%VRRI|7>f%>NDgQ|wAGh@#U;?jrUUg;AP28K&@;#1XOi z9Fp>uiAA9}_BallgJ&_}Sd;@3P9zXna6Ar2MB`B$77lIy6%|b=l`(~EKtTnPBlsW> zK(xmY30wy_8$-mvu{a_Q&SZg%_AC?&%jGzr9ZBAq4(JWefek030WO@#1W*`zA`6cLm`WKz(UpL@;Gskwid-dGm!^ z1df~TVuvw>JOEtZGYR#no&PtJ zh39b5Y%~WA&JL(6)&YfqGaZ<0IN-p>a?m&oivV!HWtWP$vM8nmaN~iIg3*8pq>Ki- zOsTQWx9F%aKp`LsT-zvnxINk*g(jh}B$R_43PVDnV91$(k&0FQDPkw&|7hZ*1bkHn zz;V-UV0i(n74mbrn$b)l<3ISBnTvnW0!aPC$am@cL#`ikeU}2?1^$s;Kjiu@1-=XX zBfI`@a_M|I@Bl*aUr-cyoLTZd=PGy%ljBy7Ls53Z&qG z;+>zSj7Im+e4=Jytgi8@E{zPHMVHdt-TVoyxp&Y$s;dl|#(zzKr&kzQt%Dw@9~d?r zwz;B;@6&5ipKGh{>bCM{PgM2P&-YV9B0chLdTTjT(GS~dy(z&w{9w4*?X<4v=KaG_ zy(jB>?z`TJ9-mN+ta^oeKgaXuGacg`>r+$-8acPWKOu!@vxYf2Fp}AIwk}x9DlI%= zwAriOP^);y%kxiCmqcoyO~>X{g{?^pim*r5q{`Tw z;-wpU_D-4FEKl-52(7r{X5V(t^cQiD$SC+LG)X4m&A1mz~-m2SpnV1A6=MIK8 z(~8e^bb7b)qYvDHXdtJ`w>c5#{%q2hv7 z5d{^B3RSTdaebgHr7S8`McgV1DpV~htyZeMNkGN(`j6+l{%6jaWahiS?|1M0efPU3 z->eM{S!88tZ;8Xk%0ar@#6z>eGCO`BPuYZI@0sN@2F{)(3R^ZYdF*rrw}Ax1auLQJJ$0?CE**$NWtpsc92#3!0zARS!=Vg#bOJnIY*Z9`o@QswH9++P^eo+Y|1KX zz!i78)4R(8>?{hchAe+b9bPbe3NAVL@tl+gronU5JAA!I3ZJ-L*b(14!@m|XtJQyE zc3s?dz&;9CbGLfE)@CrWxTLW%-S8S5LIiqcDa^tC3u4EU1*Tp51DU@4>Sj zs(U4k!EV)(PRrih4qNkT<*A>xFmF7O6{+A#t806|Ehn(z9b=GcbZb7k=brM$m;Jg6 zby4=n{pR}Y_0)les3Pjm7Yp|aOW?~z3`>gxzqNENEzM)xER4xcJd(XzW@mcfj-Q=8 z_Ujp|SA04(R`B$;2lv|sFsz21m$lKhWiZ^1vvU>{OSY2i)S}Pwj$7PZ5LDYZ=kvBK z{~IN-M2`V4@DX3-RCT$ltK5IGr(nKSbZ9B?GFH_6xSrMjEA_7~6Vk4<$@>5^H{|L5 zOY%9%b5qR%xto7AFWWlxyk)cW>!CC4!TToW{~R>S={Yx1dfBz*#G|m*AEK%U=0lt=94cK6B;T|L{k zErOS@aB<;=(2fGxfJ0B!Je}L5V|!gT&utiJr$?-CcJJ%e0QNcBqU=-c-18AF0gf}0 z3)gstCvWGqjjTGfa>u2jD0#7ke`w9sip%<`zpbxguK8UqKCWzzdG+#W_D6e*|GYP+ z7Uy~+H0wLv(NwHY*&|p#5(^h|A(es%N>vh=s8^`5eucyFy!C1jiifp;1ePO8KK{3p zwRixL^6}9OAz7#vz;Q@GiUy8I2@ydl@eoIf_x7^n>A4tz0@i|nUXh?gxq3d{$jim< z4Z|cnV1#Jn`FOD~6cDI1FhD2LiDZJm9!aF)y(|HqMk?cm`z;)!z@GT{IIULAC6SVn zl88w(qDmttQ8*k9iA*I?sRRr`K$DePP)|^z&IXE64nG)$G>BS@sFZ+#6O^cQT0S0+ z*@4&bDbzyY8+s)=rUIr1Ne`+?6e5|VP>{yYK(+pf7|B>be>(#eVQrlh4x=ia27>() zVWrl2JjJ3wVdxtMLrQW)p*ALkiT92(Df9-X)@c%q7%4=86JP~Kiem9m-oa}T*;}#R ziOpa!rZYYe%-tLQchIkMH-a&gP{{RDK{|ueKtDd-5HDA%f)FX!c*|zW$Q%fw5m*u? zhd`&YsRRkk1PKhLl*I;FY&Mfc9!C|ZM75w2f(=v{IT68lBoKuKGwE~!Op#CtbPf|H zfRIc|ka6e~ibO`HF{zw!6iYM+R+V7F_^1q2QjCgCCo^a=4ue3JKo}Lw;t)7=4x2!x zNn}zgn*wpjY$KIn9k{-sfqXobI2xaEBs2lk%2XN!AHNt;>hy1+B18d4XhDN$6egX` zqR`0{28G39QK)Z0QLqNZ3eLbuArq-|qeTjF7h;ScRyc?Pl*1&oQf@RDmW7KYg9!^7 z>J(!(j$^rS1sWLCsx%^%DuItTLv8077K;kgs4|v>PM~2@6X+5ei@>4D zm;@?~L6wl%pcI0c@3NyRnKlX3z`k-UQ!E>-fQ;Dyu11aB-bE+H!3F`z*xn}72sDa_ zLgtd0Tn1x4naU-T@uabYNrqkhI%6K`e>CA40pr>LHg9wcYcE*0BE4x>W11Oc{0Bc{ zYw-_yz^Fesc`toG$n`<4_fp`!z#ppXgIw>WzO2+uS=%XkL%QIa<=qFK-@qJN|hY zv^0Nfnyz>MlkIlB8Q1TpR1%xqVH-u$YM5faxFY7M@5xPvm9vLsdwdS1U3AXd7|o+N zE?F11ZLjI69tSaKl77aTzg}Qv!;kdNWA7gLPTYTG_w-er$12V@X4GDUEP5x@;2&1! z<=kid-g%0BU_cZ~I^p<(ihij&T{V!OQ*yDS{MuR6WJCO2LI=Fdy3Tblt4LWuoDl7k z>v-$Ok(g53{g3K68_dc~hLS9-(?Q&SCY-+-6>R!P`oEskE!x#k*+7tFR_2G*qKW6OWY*Y|E)6&a)z#IwtQ~RkLKVzen+;TbnDc$s{AoG8j0oLB}h- vFAfF+b6RCr`aWCqqwUXUjvyJs%V{+Of0brD%8h4Zi-HUE5Ai#=AU5N_8VQe$ literal 0 HcmV?d00001