mirror of
				https://github.com/ciphervance/supercell-wx.git
				synced 2025-10-31 07:40:05 +00:00 
			
		
		
		
	
						commit
						85573d6103
					
				
					 27 changed files with 2127 additions and 503 deletions
				
			
		
							
								
								
									
										4
									
								
								.github/workflows/ci.yml
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.github/workflows/ci.yml
									
										
									
									
										vendored
									
									
								
							|  | @ -30,7 +30,7 @@ jobs: | ||||||
|             msvc_version: 2022 |             msvc_version: 2022 | ||||||
|             qt_version: 6.6.2 |             qt_version: 6.6.2 | ||||||
|             qt_arch: win64_msvc2019_64 |             qt_arch: win64_msvc2019_64 | ||||||
|             qt_modules: qtimageformats qtmultimedia qtpositioning |             qt_modules: qtimageformats qtmultimedia qtpositioning qtserialport | ||||||
|             qt_tools: '' |             qt_tools: '' | ||||||
|             conan_arch: x86_64 |             conan_arch: x86_64 | ||||||
|             conan_compiler: Visual Studio |             conan_compiler: Visual Studio | ||||||
|  | @ -46,7 +46,7 @@ jobs: | ||||||
|             compiler: gcc |             compiler: gcc | ||||||
|             qt_version: 6.6.2 |             qt_version: 6.6.2 | ||||||
|             qt_arch: gcc_64 |             qt_arch: gcc_64 | ||||||
|             qt_modules: qtimageformats qtmultimedia qtpositioning |             qt_modules: qtimageformats qtmultimedia qtpositioning qtserialport | ||||||
|             qt_tools: '' |             qt_tools: '' | ||||||
|             conan_arch: x86_64 |             conan_arch: x86_64 | ||||||
|             conan_compiler: gcc |             conan_compiler: gcc | ||||||
|  |  | ||||||
|  | @ -34,7 +34,7 @@ Supercell Wx uses code from the following dependencies: | ||||||
| | [MapLibre Native](https://maplibre.org/projects/maplibre-native/) | [BSD 2-Clause "Simplified" License](https://spdx.org/licenses/BSD-2-Clause.html) | | | [MapLibre Native](https://maplibre.org/projects/maplibre-native/) | [BSD 2-Clause "Simplified" License](https://spdx.org/licenses/BSD-2-Clause.html) | | ||||||
| | [nunicode](https://bitbucket.org/alekseyt/nunicode/src/master/) | [MIT License](https://spdx.org/licenses/MIT.html) | Modified for MapLibre Native | | | [nunicode](https://bitbucket.org/alekseyt/nunicode/src/master/) | [MIT License](https://spdx.org/licenses/MIT.html) | Modified for MapLibre Native | | ||||||
| | [OpenSSL](https://www.openssl.org/) | [OpenSSL License](https://spdx.org/licenses/OpenSSL.html) | | | [OpenSSL](https://www.openssl.org/) | [OpenSSL License](https://spdx.org/licenses/OpenSSL.html) | | ||||||
| | [Qt](https://www.qt.io/) | [GNU Lesser General Public License v3.0 only](https://spdx.org/licenses/LGPL-3.0-only.html) | Qt Core, Qt GUI, Qt Multimedia, Qt Network, Qt OpenGL, Qt Positioning, Qt SQL, Qt SVG, Qt Widgets<br/>Additional Licenses: https://doc.qt.io/qt-6/licenses-used-in-qt.html | | | [Qt](https://www.qt.io/) | [GNU Lesser General Public License v3.0 only](https://spdx.org/licenses/LGPL-3.0-only.html) | Qt Core, Qt GUI, Qt Multimedia, Qt Network, Qt OpenGL, Qt Positioning, Qt Serial Port, Qt SQL, Qt SVG, Qt Widgets<br/>Additional Licenses: https://doc.qt.io/qt-6/licenses-used-in-qt.html | | ||||||
| | [re2](https://github.com/google/re2) | [BSD 3-Clause "New" or "Revised" License](https://spdx.org/licenses/BSD-3-Clause.html) | | | [re2](https://github.com/google/re2) | [BSD 3-Clause "New" or "Revised" License](https://spdx.org/licenses/BSD-3-Clause.html) | | ||||||
| | [spdlog](https://github.com/gabime/spdlog) | [MIT License](https://spdx.org/licenses/MIT.html) | | | [spdlog](https://github.com/gabime/spdlog) | [MIT License](https://spdx.org/licenses/MIT.html) | | ||||||
| | [SQLite](https://www.sqlite.org/) | Public Domain | | | [SQLite](https://www.sqlite.org/) | Public Domain | | ||||||
|  |  | ||||||
							
								
								
									
										1
									
								
								scwx-qt/res/icons/font-awesome-6/copy-regular.svg
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								scwx-qt/res/icons/font-awesome-6/copy-regular.svg
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1 @@ | ||||||
|  | <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--!Font Awesome Free 6.5.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M384 336H192c-8.8 0-16-7.2-16-16V64c0-8.8 7.2-16 16-16l140.1 0L400 115.9V320c0 8.8-7.2 16-16 16zM192 384H384c35.3 0 64-28.7 64-64V115.9c0-12.7-5.1-24.9-14.1-33.9L366.1 14.1c-9-9-21.2-14.1-33.9-14.1H192c-35.3 0-64 28.7-64 64V320c0 35.3 28.7 64 64 64zM64 128c-35.3 0-64 28.7-64 64V448c0 35.3 28.7 64 64 64H256c35.3 0 64-28.7 64-64V416H272v32c0 8.8-7.2 16-16 16H64c-8.8 0-16-7.2-16-16V192c0-8.8 7.2-16 16-16H96V128H64z"/></svg> | ||||||
| After Width: | Height: | Size: 646 B | 
|  | @ -28,6 +28,7 @@ find_package(QT NAMES Qt6 | ||||||
|                         OpenGL |                         OpenGL | ||||||
|                         OpenGLWidgets |                         OpenGLWidgets | ||||||
|                         Positioning |                         Positioning | ||||||
|  |                         SerialPort | ||||||
|                         Svg |                         Svg | ||||||
|                         Widgets REQUIRED) |                         Widgets REQUIRED) | ||||||
| 
 | 
 | ||||||
|  | @ -39,6 +40,7 @@ find_package(Qt${QT_VERSION_MAJOR} | ||||||
|                         OpenGL |                         OpenGL | ||||||
|                         OpenGLWidgets |                         OpenGLWidgets | ||||||
|                         Positioning |                         Positioning | ||||||
|  |                         SerialPort | ||||||
|                         Svg |                         Svg | ||||||
|                         Widgets |                         Widgets | ||||||
|              REQUIRED) |              REQUIRED) | ||||||
|  | @ -97,6 +99,7 @@ set(HDR_MANAGER source/scwx/qt/manager/alert_manager.hpp | ||||||
|                 source/scwx/qt/manager/resource_manager.hpp |                 source/scwx/qt/manager/resource_manager.hpp | ||||||
|                 source/scwx/qt/manager/settings_manager.hpp |                 source/scwx/qt/manager/settings_manager.hpp | ||||||
|                 source/scwx/qt/manager/text_event_manager.hpp |                 source/scwx/qt/manager/text_event_manager.hpp | ||||||
|  |                 source/scwx/qt/manager/thread_manager.hpp | ||||||
|                 source/scwx/qt/manager/timeline_manager.hpp |                 source/scwx/qt/manager/timeline_manager.hpp | ||||||
|                 source/scwx/qt/manager/update_manager.hpp) |                 source/scwx/qt/manager/update_manager.hpp) | ||||||
| set(SRC_MANAGER source/scwx/qt/manager/alert_manager.cpp | set(SRC_MANAGER source/scwx/qt/manager/alert_manager.cpp | ||||||
|  | @ -111,6 +114,7 @@ set(SRC_MANAGER source/scwx/qt/manager/alert_manager.cpp | ||||||
|                 source/scwx/qt/manager/resource_manager.cpp |                 source/scwx/qt/manager/resource_manager.cpp | ||||||
|                 source/scwx/qt/manager/settings_manager.cpp |                 source/scwx/qt/manager/settings_manager.cpp | ||||||
|                 source/scwx/qt/manager/text_event_manager.cpp |                 source/scwx/qt/manager/text_event_manager.cpp | ||||||
|  |                 source/scwx/qt/manager/thread_manager.cpp | ||||||
|                 source/scwx/qt/manager/timeline_manager.cpp |                 source/scwx/qt/manager/timeline_manager.cpp | ||||||
|                 source/scwx/qt/manager/update_manager.cpp) |                 source/scwx/qt/manager/update_manager.cpp) | ||||||
| set(HDR_MAP source/scwx/qt/map/alert_layer.hpp | set(HDR_MAP source/scwx/qt/map/alert_layer.hpp | ||||||
|  | @ -235,6 +239,7 @@ set(HDR_UI source/scwx/qt/ui/about_dialog.hpp | ||||||
|            source/scwx/qt/ui/county_dialog.hpp |            source/scwx/qt/ui/county_dialog.hpp | ||||||
|            source/scwx/qt/ui/download_dialog.hpp |            source/scwx/qt/ui/download_dialog.hpp | ||||||
|            source/scwx/qt/ui/flow_layout.hpp |            source/scwx/qt/ui/flow_layout.hpp | ||||||
|  |            source/scwx/qt/ui/gps_info_dialog.hpp | ||||||
|            source/scwx/qt/ui/hotkey_edit.hpp |            source/scwx/qt/ui/hotkey_edit.hpp | ||||||
|            source/scwx/qt/ui/imgui_debug_dialog.hpp |            source/scwx/qt/ui/imgui_debug_dialog.hpp | ||||||
|            source/scwx/qt/ui/imgui_debug_widget.hpp |            source/scwx/qt/ui/imgui_debug_widget.hpp | ||||||
|  | @ -248,6 +253,7 @@ set(HDR_UI source/scwx/qt/ui/about_dialog.hpp | ||||||
|            source/scwx/qt/ui/placefile_settings_widget.hpp |            source/scwx/qt/ui/placefile_settings_widget.hpp | ||||||
|            source/scwx/qt/ui/progress_dialog.hpp |            source/scwx/qt/ui/progress_dialog.hpp | ||||||
|            source/scwx/qt/ui/radar_site_dialog.hpp |            source/scwx/qt/ui/radar_site_dialog.hpp | ||||||
|  |            source/scwx/qt/ui/serial_port_dialog.hpp | ||||||
|            source/scwx/qt/ui/settings_dialog.hpp |            source/scwx/qt/ui/settings_dialog.hpp | ||||||
|            source/scwx/qt/ui/update_dialog.hpp) |            source/scwx/qt/ui/update_dialog.hpp) | ||||||
| set(SRC_UI source/scwx/qt/ui/about_dialog.cpp | set(SRC_UI source/scwx/qt/ui/about_dialog.cpp | ||||||
|  | @ -258,6 +264,7 @@ set(SRC_UI source/scwx/qt/ui/about_dialog.cpp | ||||||
|            source/scwx/qt/ui/county_dialog.cpp |            source/scwx/qt/ui/county_dialog.cpp | ||||||
|            source/scwx/qt/ui/download_dialog.cpp |            source/scwx/qt/ui/download_dialog.cpp | ||||||
|            source/scwx/qt/ui/flow_layout.cpp |            source/scwx/qt/ui/flow_layout.cpp | ||||||
|  |            source/scwx/qt/ui/gps_info_dialog.cpp | ||||||
|            source/scwx/qt/ui/hotkey_edit.cpp |            source/scwx/qt/ui/hotkey_edit.cpp | ||||||
|            source/scwx/qt/ui/imgui_debug_dialog.cpp |            source/scwx/qt/ui/imgui_debug_dialog.cpp | ||||||
|            source/scwx/qt/ui/imgui_debug_widget.cpp |            source/scwx/qt/ui/imgui_debug_widget.cpp | ||||||
|  | @ -272,6 +279,7 @@ set(SRC_UI source/scwx/qt/ui/about_dialog.cpp | ||||||
|            source/scwx/qt/ui/progress_dialog.cpp |            source/scwx/qt/ui/progress_dialog.cpp | ||||||
|            source/scwx/qt/ui/radar_site_dialog.cpp |            source/scwx/qt/ui/radar_site_dialog.cpp | ||||||
|            source/scwx/qt/ui/settings_dialog.cpp |            source/scwx/qt/ui/settings_dialog.cpp | ||||||
|  |            source/scwx/qt/ui/serial_port_dialog.cpp | ||||||
|            source/scwx/qt/ui/update_dialog.cpp) |            source/scwx/qt/ui/update_dialog.cpp) | ||||||
| set(UI_UI  source/scwx/qt/ui/about_dialog.ui | set(UI_UI  source/scwx/qt/ui/about_dialog.ui | ||||||
|            source/scwx/qt/ui/alert_dialog.ui |            source/scwx/qt/ui/alert_dialog.ui | ||||||
|  | @ -279,6 +287,7 @@ set(UI_UI  source/scwx/qt/ui/about_dialog.ui | ||||||
|            source/scwx/qt/ui/animation_dock_widget.ui |            source/scwx/qt/ui/animation_dock_widget.ui | ||||||
|            source/scwx/qt/ui/collapsible_group.ui |            source/scwx/qt/ui/collapsible_group.ui | ||||||
|            source/scwx/qt/ui/county_dialog.ui |            source/scwx/qt/ui/county_dialog.ui | ||||||
|  |            source/scwx/qt/ui/gps_info_dialog.ui | ||||||
|            source/scwx/qt/ui/imgui_debug_dialog.ui |            source/scwx/qt/ui/imgui_debug_dialog.ui | ||||||
|            source/scwx/qt/ui/layer_dialog.ui |            source/scwx/qt/ui/layer_dialog.ui | ||||||
|            source/scwx/qt/ui/open_url_dialog.ui |            source/scwx/qt/ui/open_url_dialog.ui | ||||||
|  | @ -287,6 +296,7 @@ set(UI_UI  source/scwx/qt/ui/about_dialog.ui | ||||||
|            source/scwx/qt/ui/progress_dialog.ui |            source/scwx/qt/ui/progress_dialog.ui | ||||||
|            source/scwx/qt/ui/radar_site_dialog.ui |            source/scwx/qt/ui/radar_site_dialog.ui | ||||||
|            source/scwx/qt/ui/settings_dialog.ui |            source/scwx/qt/ui/settings_dialog.ui | ||||||
|  |            source/scwx/qt/ui/serial_port_dialog.ui | ||||||
|            source/scwx/qt/ui/update_dialog.ui) |            source/scwx/qt/ui/update_dialog.ui) | ||||||
| set(HDR_UI_SETTINGS source/scwx/qt/ui/settings/hotkey_settings_widget.hpp | set(HDR_UI_SETTINGS source/scwx/qt/ui/settings/hotkey_settings_widget.hpp | ||||||
|                     source/scwx/qt/ui/settings/settings_page_widget.hpp |                     source/scwx/qt/ui/settings/settings_page_widget.hpp | ||||||
|  | @ -597,11 +607,13 @@ target_link_libraries(scwx-qt PUBLIC Qt${QT_VERSION_MAJOR}::Widgets | ||||||
|                                      Qt${QT_VERSION_MAJOR}::OpenGLWidgets |                                      Qt${QT_VERSION_MAJOR}::OpenGLWidgets | ||||||
|                                      Qt${QT_VERSION_MAJOR}::Multimedia |                                      Qt${QT_VERSION_MAJOR}::Multimedia | ||||||
|                                      Qt${QT_VERSION_MAJOR}::Positioning |                                      Qt${QT_VERSION_MAJOR}::Positioning | ||||||
|  |                                      Qt${QT_VERSION_MAJOR}::SerialPort | ||||||
|                                      Qt${QT_VERSION_MAJOR}::Svg |                                      Qt${QT_VERSION_MAJOR}::Svg | ||||||
|                                      Boost::json |                                      Boost::json | ||||||
|                                      Boost::timer |                                      Boost::timer | ||||||
|                                      QMapLibre::Core |                                      QMapLibre::Core | ||||||
|                                      $<$<CXX_COMPILER_ID:MSVC>:opengl32> |                                      $<$<CXX_COMPILER_ID:MSVC>:opengl32> | ||||||
|  |                                      $<$<CXX_COMPILER_ID:MSVC>:SetupAPI> | ||||||
|                                      Fontconfig::Fontconfig |                                      Fontconfig::Fontconfig | ||||||
|                                      GeographicLib::GeographicLib |                                      GeographicLib::GeographicLib | ||||||
|                                      GEOS::geos |                                      GEOS::geos | ||||||
|  |  | ||||||
|  | @ -32,6 +32,7 @@ | ||||||
|         <file>res/icons/font-awesome-6/angles-up-solid.svg</file> |         <file>res/icons/font-awesome-6/angles-up-solid.svg</file> | ||||||
|         <file>res/icons/font-awesome-6/backward-step-solid.svg</file> |         <file>res/icons/font-awesome-6/backward-step-solid.svg</file> | ||||||
|         <file>res/icons/font-awesome-6/book-solid.svg</file> |         <file>res/icons/font-awesome-6/book-solid.svg</file> | ||||||
|  |         <file>res/icons/font-awesome-6/copy-regular.svg</file> | ||||||
|         <file>res/icons/font-awesome-6/discord.svg</file> |         <file>res/icons/font-awesome-6/discord.svg</file> | ||||||
|         <file>res/icons/font-awesome-6/earth-americas-solid.svg</file> |         <file>res/icons/font-awesome-6/earth-americas-solid.svg</file> | ||||||
|         <file>res/icons/font-awesome-6/font-solid.svg</file> |         <file>res/icons/font-awesome-6/font-solid.svg</file> | ||||||
|  |  | ||||||
|  | @ -7,6 +7,7 @@ | ||||||
| #include <scwx/qt/manager/radar_product_manager.hpp> | #include <scwx/qt/manager/radar_product_manager.hpp> | ||||||
| #include <scwx/qt/manager/resource_manager.hpp> | #include <scwx/qt/manager/resource_manager.hpp> | ||||||
| #include <scwx/qt/manager/settings_manager.hpp> | #include <scwx/qt/manager/settings_manager.hpp> | ||||||
|  | #include <scwx/qt/manager/thread_manager.hpp> | ||||||
| #include <scwx/qt/settings/general_settings.hpp> | #include <scwx/qt/settings/general_settings.hpp> | ||||||
| #include <scwx/qt/types/qt_types.hpp> | #include <scwx/qt/types/qt_types.hpp> | ||||||
| #include <scwx/qt/ui/setup/setup_wizard.hpp> | #include <scwx/qt/ui/setup/setup_wizard.hpp> | ||||||
|  | @ -129,6 +130,9 @@ int main(int argc, char* argv[]) | ||||||
|    // Deinitialize application
 |    // Deinitialize application
 | ||||||
|    scwx::qt::manager::RadarProductManager::Cleanup(); |    scwx::qt::manager::RadarProductManager::Cleanup(); | ||||||
| 
 | 
 | ||||||
|  |    // Stop Qt Threads
 | ||||||
|  |    scwx::qt::manager::ThreadManager::Instance().StopThreads(); | ||||||
|  | 
 | ||||||
|    // Gracefully stop the io_context main loop
 |    // Gracefully stop the io_context main loop
 | ||||||
|    work.reset(); |    work.reset(); | ||||||
|    threadPool.join(); |    threadPool.join(); | ||||||
|  |  | ||||||
|  | @ -23,6 +23,7 @@ | ||||||
| #include <scwx/qt/ui/animation_dock_widget.hpp> | #include <scwx/qt/ui/animation_dock_widget.hpp> | ||||||
| #include <scwx/qt/ui/collapsible_group.hpp> | #include <scwx/qt/ui/collapsible_group.hpp> | ||||||
| #include <scwx/qt/ui/flow_layout.hpp> | #include <scwx/qt/ui/flow_layout.hpp> | ||||||
|  | #include <scwx/qt/ui/gps_info_dialog.hpp> | ||||||
| #include <scwx/qt/ui/imgui_debug_dialog.hpp> | #include <scwx/qt/ui/imgui_debug_dialog.hpp> | ||||||
| #include <scwx/qt/ui/layer_dialog.hpp> | #include <scwx/qt/ui/layer_dialog.hpp> | ||||||
| #include <scwx/qt/ui/level2_products_widget.hpp> | #include <scwx/qt/ui/level2_products_widget.hpp> | ||||||
|  | @ -85,6 +86,7 @@ public: | ||||||
|        alertDockWidget_ {nullptr}, |        alertDockWidget_ {nullptr}, | ||||||
|        animationDockWidget_ {nullptr}, |        animationDockWidget_ {nullptr}, | ||||||
|        aboutDialog_ {nullptr}, |        aboutDialog_ {nullptr}, | ||||||
|  |        gpsInfoDialog_ {nullptr}, | ||||||
|        imGuiDebugDialog_ {nullptr}, |        imGuiDebugDialog_ {nullptr}, | ||||||
|        layerDialog_ {nullptr}, |        layerDialog_ {nullptr}, | ||||||
|        placefileDialog_ {nullptr}, |        placefileDialog_ {nullptr}, | ||||||
|  | @ -190,6 +192,7 @@ public: | ||||||
|    ui::AlertDockWidget*     alertDockWidget_; |    ui::AlertDockWidget*     alertDockWidget_; | ||||||
|    ui::AnimationDockWidget* animationDockWidget_; |    ui::AnimationDockWidget* animationDockWidget_; | ||||||
|    ui::AboutDialog*         aboutDialog_; |    ui::AboutDialog*         aboutDialog_; | ||||||
|  |    ui::GpsInfoDialog*       gpsInfoDialog_; | ||||||
|    ui::ImGuiDebugDialog*    imGuiDebugDialog_; |    ui::ImGuiDebugDialog*    imGuiDebugDialog_; | ||||||
|    ui::LayerDialog*         layerDialog_; |    ui::LayerDialog*         layerDialog_; | ||||||
|    ui::PlacefileDialog*     placefileDialog_; |    ui::PlacefileDialog*     placefileDialog_; | ||||||
|  | @ -264,6 +267,9 @@ MainWindow::MainWindow(QWidget* parent) : | ||||||
|    p->alertDockWidget_->setVisible(false); |    p->alertDockWidget_->setVisible(false); | ||||||
|    addDockWidget(Qt::BottomDockWidgetArea, p->alertDockWidget_); |    addDockWidget(Qt::BottomDockWidgetArea, p->alertDockWidget_); | ||||||
| 
 | 
 | ||||||
|  |    // GPS Info Dialog
 | ||||||
|  |    p->gpsInfoDialog_ = new ui::GpsInfoDialog(this); | ||||||
|  | 
 | ||||||
|    // Configure Menu
 |    // Configure Menu
 | ||||||
|    ui->menuView->insertAction(ui->actionRadarToolbox, |    ui->menuView->insertAction(ui->actionRadarToolbox, | ||||||
|                               ui->radarToolboxDock->toggleViewAction()); |                               ui->radarToolboxDock->toggleViewAction()); | ||||||
|  | @ -535,6 +541,11 @@ void MainWindow::on_actionExit_triggered() | ||||||
|    close(); |    close(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void MainWindow::on_actionGpsInfo_triggered() | ||||||
|  | { | ||||||
|  |    p->gpsInfoDialog_->show(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void MainWindow::on_actionColorTable_triggered(bool checked) | void MainWindow::on_actionColorTable_triggered(bool checked) | ||||||
| { | { | ||||||
|    p->layerModel_->SetLayerDisplayed(types::LayerType::Information, |    p->layerModel_->SetLayerDisplayed(types::LayerType::Information, | ||||||
|  |  | ||||||
|  | @ -38,6 +38,7 @@ private slots: | ||||||
|    void on_actionOpenTextEvent_triggered(); |    void on_actionOpenTextEvent_triggered(); | ||||||
|    void on_actionSettings_triggered(); |    void on_actionSettings_triggered(); | ||||||
|    void on_actionExit_triggered(); |    void on_actionExit_triggered(); | ||||||
|  |    void on_actionGpsInfo_triggered(); | ||||||
|    void on_actionColorTable_triggered(bool checked); |    void on_actionColorTable_triggered(bool checked); | ||||||
|    void on_actionRadarRange_triggered(bool checked); |    void on_actionRadarRange_triggered(bool checked); | ||||||
|    void on_actionRadarSites_triggered(bool checked); |    void on_actionRadarSites_triggered(bool checked); | ||||||
|  |  | ||||||
|  | @ -39,7 +39,7 @@ | ||||||
|      <x>0</x> |      <x>0</x> | ||||||
|      <y>0</y> |      <y>0</y> | ||||||
|      <width>1024</width> |      <width>1024</width> | ||||||
|      <height>21</height> |      <height>33</height> | ||||||
|     </rect> |     </rect> | ||||||
|    </property> |    </property> | ||||||
|    <widget class="QMenu" name="menuFile"> |    <widget class="QMenu" name="menuFile"> | ||||||
|  | @ -85,6 +85,7 @@ | ||||||
|     </widget> |     </widget> | ||||||
|     <addaction name="actionRadarToolbox"/> |     <addaction name="actionRadarToolbox"/> | ||||||
|     <addaction name="actionAlerts"/> |     <addaction name="actionAlerts"/> | ||||||
|  |     <addaction name="actionGpsInfo"/> | ||||||
|     <addaction name="separator"/> |     <addaction name="separator"/> | ||||||
|     <addaction name="menuMapLayers"/> |     <addaction name="menuMapLayers"/> | ||||||
|    </widget> |    </widget> | ||||||
|  | @ -135,13 +136,13 @@ | ||||||
|      <item> |      <item> | ||||||
|       <widget class="QScrollArea" name="radarToolboxScrollArea"> |       <widget class="QScrollArea" name="radarToolboxScrollArea"> | ||||||
|        <property name="frameShape"> |        <property name="frameShape"> | ||||||
|         <enum>QFrame::NoFrame</enum> |         <enum>QFrame::Shape::NoFrame</enum> | ||||||
|        </property> |        </property> | ||||||
|        <property name="horizontalScrollBarPolicy"> |        <property name="horizontalScrollBarPolicy"> | ||||||
|         <enum>Qt::ScrollBarAsNeeded</enum> |         <enum>Qt::ScrollBarPolicy::ScrollBarAsNeeded</enum> | ||||||
|        </property> |        </property> | ||||||
|        <property name="sizeAdjustPolicy"> |        <property name="sizeAdjustPolicy"> | ||||||
|         <enum>QAbstractScrollArea::AdjustToContents</enum> |         <enum>QAbstractScrollArea::SizeAdjustPolicy::AdjustToContents</enum> | ||||||
|        </property> |        </property> | ||||||
|        <property name="widgetResizable"> |        <property name="widgetResizable"> | ||||||
|         <bool>true</bool> |         <bool>true</bool> | ||||||
|  | @ -151,8 +152,8 @@ | ||||||
|          <rect> |          <rect> | ||||||
|           <x>0</x> |           <x>0</x> | ||||||
|           <y>0</y> |           <y>0</y> | ||||||
|           <width>193</width> |           <width>190</width> | ||||||
|           <height>688</height> |           <height>686</height> | ||||||
|          </rect> |          </rect> | ||||||
|         </property> |         </property> | ||||||
|         <layout class="QVBoxLayout" name="verticalLayout_6"> |         <layout class="QVBoxLayout" name="verticalLayout_6"> | ||||||
|  | @ -171,10 +172,10 @@ | ||||||
|          <item> |          <item> | ||||||
|           <widget class="QFrame" name="radarInfoFrame"> |           <widget class="QFrame" name="radarInfoFrame"> | ||||||
|            <property name="frameShape"> |            <property name="frameShape"> | ||||||
|             <enum>QFrame::StyledPanel</enum> |             <enum>QFrame::Shape::StyledPanel</enum> | ||||||
|            </property> |            </property> | ||||||
|            <property name="frameShadow"> |            <property name="frameShadow"> | ||||||
|             <enum>QFrame::Raised</enum> |             <enum>QFrame::Shadow::Raised</enum> | ||||||
|            </property> |            </property> | ||||||
|            <layout class="QGridLayout" name="gridLayout" columnstretch="0,0,0,0,0"> |            <layout class="QGridLayout" name="gridLayout" columnstretch="0,0,0,0,0"> | ||||||
|             <item row="0" column="2"> |             <item row="0" column="2"> | ||||||
|  | @ -209,10 +210,10 @@ | ||||||
|             <item row="0" column="4"> |             <item row="0" column="4"> | ||||||
|              <widget class="QFrame" name="frame"> |              <widget class="QFrame" name="frame"> | ||||||
|               <property name="frameShape"> |               <property name="frameShape"> | ||||||
|                <enum>QFrame::NoFrame</enum> |                <enum>QFrame::Shape::NoFrame</enum> | ||||||
|               </property> |               </property> | ||||||
|               <property name="frameShadow"> |               <property name="frameShadow"> | ||||||
|                <enum>QFrame::Raised</enum> |                <enum>QFrame::Shadow::Raised</enum> | ||||||
|               </property> |               </property> | ||||||
|               <layout class="QHBoxLayout" name="horizontalLayout"> |               <layout class="QHBoxLayout" name="horizontalLayout"> | ||||||
|                <property name="leftMargin"> |                <property name="leftMargin"> | ||||||
|  | @ -260,7 +261,7 @@ | ||||||
|                    <normaloff>:/res/icons/font-awesome-6/star-solid.svg</normaloff>:/res/icons/font-awesome-6/star-solid.svg</iconset> |                    <normaloff>:/res/icons/font-awesome-6/star-solid.svg</normaloff>:/res/icons/font-awesome-6/star-solid.svg</iconset> | ||||||
|                  </property> |                  </property> | ||||||
|                  <property name="popupMode"> |                  <property name="popupMode"> | ||||||
|                   <enum>QToolButton::InstantPopup</enum> |                   <enum>QToolButton::ToolButtonPopupMode::InstantPopup</enum> | ||||||
|                  </property> |                  </property> | ||||||
|                 </widget> |                 </widget> | ||||||
|                </item> |                </item> | ||||||
|  | @ -340,7 +341,7 @@ | ||||||
|          <item> |          <item> | ||||||
|           <spacer name="radarToolboxSpacer"> |           <spacer name="radarToolboxSpacer"> | ||||||
|            <property name="orientation"> |            <property name="orientation"> | ||||||
|             <enum>Qt::Vertical</enum> |             <enum>Qt::Orientation::Vertical</enum> | ||||||
|            </property> |            </property> | ||||||
|            <property name="sizeHint" stdset="0"> |            <property name="sizeHint" stdset="0"> | ||||||
|             <size> |             <size> | ||||||
|  | @ -481,6 +482,11 @@ | ||||||
|     <string>Radar &Sites</string> |     <string>Radar &Sites</string> | ||||||
|    </property> |    </property> | ||||||
|   </action> |   </action> | ||||||
|  |   <action name="actionGpsInfo"> | ||||||
|  |    <property name="text"> | ||||||
|  |     <string>&GPS Info</string> | ||||||
|  |    </property> | ||||||
|  |   </action> | ||||||
|  </widget> |  </widget> | ||||||
|  <resources> |  <resources> | ||||||
|   <include location="../../../../scwx-qt.qrc"/> |   <include location="../../../../scwx-qt.qrc"/> | ||||||
|  |  | ||||||
|  | @ -1,10 +1,16 @@ | ||||||
| #include <scwx/qt/manager/position_manager.hpp> | #include <scwx/qt/manager/position_manager.hpp> | ||||||
|  | #include <scwx/qt/manager/settings_manager.hpp> | ||||||
|  | #include <scwx/qt/manager/thread_manager.hpp> | ||||||
|  | #include <scwx/qt/settings/general_settings.hpp> | ||||||
|  | #include <scwx/qt/types/location_types.hpp> | ||||||
| #include <scwx/common/geographic.hpp> | #include <scwx/common/geographic.hpp> | ||||||
| #include <scwx/util/logger.hpp> | #include <scwx/util/logger.hpp> | ||||||
| 
 | 
 | ||||||
|  | #include <mutex> | ||||||
| #include <set> | #include <set> | ||||||
| 
 | 
 | ||||||
| #include <boost/uuid/random_generator.hpp> | #include <boost/uuid/random_generator.hpp> | ||||||
|  | #include <QAbstractEventDispatcher> | ||||||
| #include <QGeoPositionInfoSource> | #include <QGeoPositionInfoSource> | ||||||
| 
 | 
 | ||||||
| namespace scwx | namespace scwx | ||||||
|  | @ -23,47 +29,82 @@ public: | ||||||
|    explicit Impl(PositionManager* self) : |    explicit Impl(PositionManager* self) : | ||||||
|        self_ {self}, trackingUuid_ {boost::uuids::random_generator()()} |        self_ {self}, trackingUuid_ {boost::uuids::random_generator()()} | ||||||
|    { |    { | ||||||
|       // TODO: macOS requires permission
 |       auto& generalSettings = settings::GeneralSettings::Instance(); | ||||||
|       geoPositionInfoSource_ = |  | ||||||
|          QGeoPositionInfoSource::createDefaultSource(self); |  | ||||||
| 
 | 
 | ||||||
|       if (geoPositionInfoSource_ != nullptr) |       gpsParent_->moveToThread(gpsThread_); | ||||||
|       { |  | ||||||
|          logger_->debug("Using position source: {}", |  | ||||||
|                         geoPositionInfoSource_->sourceName().toStdString()); |  | ||||||
| 
 | 
 | ||||||
|          QObject::connect(geoPositionInfoSource_, |       logger_->debug( | ||||||
|                           &QGeoPositionInfoSource::positionUpdated, |          "Available sources: {}", | ||||||
|  |          QGeoPositionInfoSource::availableSources().join(", ").toStdString()); | ||||||
|  | 
 | ||||||
|  |       CreatePositionSourceAsync(); | ||||||
|  | 
 | ||||||
|  |       positioningPluginCallbackUuid_ = | ||||||
|  |          generalSettings.positioning_plugin().RegisterValueChangedCallback( | ||||||
|  |             [this](const std::string&) | ||||||
|  |             { createPositionSourcePending_ = true; }); | ||||||
|  |       nmeaBaudRateCallbackUuid_ = | ||||||
|  |          generalSettings.nmea_baud_rate().RegisterValueChangedCallback( | ||||||
|  |             [this](const std::int64_t&) | ||||||
|  |             { createPositionSourcePending_ = true; }); | ||||||
|  |       nmeaSourceCallbackUuid_ = | ||||||
|  |          generalSettings.nmea_source().RegisterValueChangedCallback( | ||||||
|  |             [this](const std::string&) | ||||||
|  |             { createPositionSourcePending_ = true; }); | ||||||
|  | 
 | ||||||
|  |       connect(&SettingsManager::Instance(), | ||||||
|  |               &SettingsManager::SettingsSaved, | ||||||
|               self_, |               self_, | ||||||
|                           [this](const QGeoPositionInfo& info) |               [this]() | ||||||
|               { |               { | ||||||
|                              auto coordinate = info.coordinate(); |                  if (createPositionSourcePending_) | ||||||
| 
 |  | ||||||
|                              if (coordinate != position_.coordinate()) |  | ||||||
|                  { |                  { | ||||||
|                                 logger_->debug("Position updated: {}, {}", |                     CreatePositionSourceAsync(); | ||||||
|                                                coordinate.latitude(), |  | ||||||
|                                                coordinate.longitude()); |  | ||||||
|                  } |                  } | ||||||
| 
 |  | ||||||
|                              position_ = info; |  | ||||||
| 
 |  | ||||||
|                              Q_EMIT self_->PositionUpdated(info); |  | ||||||
|               }); |               }); | ||||||
|    } |    } | ||||||
|  |    ~Impl() | ||||||
|  |    { | ||||||
|  |       auto& generalSettings = settings::GeneralSettings::Instance(); | ||||||
|  | 
 | ||||||
|  |       generalSettings.positioning_plugin().UnregisterValueChangedCallback( | ||||||
|  |          positioningPluginCallbackUuid_); | ||||||
|  |       generalSettings.nmea_baud_rate().UnregisterValueChangedCallback( | ||||||
|  |          nmeaBaudRateCallbackUuid_); | ||||||
|  |       generalSettings.nmea_source().UnregisterValueChangedCallback( | ||||||
|  |          nmeaSourceCallbackUuid_); | ||||||
|  | 
 | ||||||
|  |       gpsParent_->deleteLater(); | ||||||
|    } |    } | ||||||
| 
 | 
 | ||||||
|    ~Impl() {} |    void CreatePositionSource(); | ||||||
|  |    void CreatePositionSourceAsync(); | ||||||
|  |    void EnablePositionUpdates(boost::uuids::uuid uuid, bool enabled); | ||||||
| 
 | 
 | ||||||
|    PositionManager* self_; |    PositionManager* self_; | ||||||
|  |    QThread* gpsThread_ {ThreadManager::Instance().thread("position_manager")}; | ||||||
| 
 | 
 | ||||||
|    boost::uuids::uuid trackingUuid_; |    boost::uuids::uuid trackingUuid_; | ||||||
|    bool               trackingEnabled_ {false}; |    bool               trackingEnabled_ {false}; | ||||||
| 
 | 
 | ||||||
|    std::set<boost::uuids::uuid> uuids_ {}; |    std::set<boost::uuids::uuid> uuids_ {}; | ||||||
| 
 | 
 | ||||||
|  |    std::mutex positionSourceMutex_ {}; | ||||||
|  | 
 | ||||||
|  |    QObject*                gpsParent_ {new QObject}; | ||||||
|    QGeoPositionInfoSource* geoPositionInfoSource_ {}; |    QGeoPositionInfoSource* geoPositionInfoSource_ {}; | ||||||
|    QGeoPositionInfo        position_ {}; |    QGeoPositionInfo        position_ {}; | ||||||
|  | 
 | ||||||
|  |    types::PositioningPlugin lastPositioningPlugin_ { | ||||||
|  |       types::PositioningPlugin::Unknown}; | ||||||
|  |    std::int64_t lastNmeaBaudRate_ {-1}; | ||||||
|  |    std::string  lastNmeaSource_ {"?"}; | ||||||
|  | 
 | ||||||
|  |    boost::uuids::uuid positioningPluginCallbackUuid_ {}; | ||||||
|  |    boost::uuids::uuid nmeaBaudRateCallbackUuid_ {}; | ||||||
|  |    boost::uuids::uuid nmeaSourceCallbackUuid_ {}; | ||||||
|  | 
 | ||||||
|  |    bool createPositionSourcePending_ {false}; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| PositionManager::PositionManager() : p(std::make_unique<Impl>(this)) {} | PositionManager::PositionManager() : p(std::make_unique<Impl>(this)) {} | ||||||
|  | @ -79,30 +120,131 @@ bool PositionManager::IsLocationTracked() | ||||||
|    return p->trackingEnabled_; |    return p->trackingEnabled_; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void PositionManager::Impl::CreatePositionSourceAsync() | ||||||
|  | { | ||||||
|  |    QMetaObject::invokeMethod(QAbstractEventDispatcher::instance(gpsThread_), | ||||||
|  |                              [this]() { CreatePositionSource(); }); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void PositionManager::Impl::CreatePositionSource() | ||||||
|  | { | ||||||
|  |    auto& generalSettings = settings::GeneralSettings::Instance(); | ||||||
|  | 
 | ||||||
|  |    createPositionSourcePending_ = false; | ||||||
|  | 
 | ||||||
|  |    types::PositioningPlugin positioningPlugin = types::GetPositioningPlugin( | ||||||
|  |       generalSettings.positioning_plugin().GetValue()); | ||||||
|  |    std::int64_t nmeaBaudRate = generalSettings.nmea_baud_rate().GetValue(); | ||||||
|  |    std::string  nmeaSource   = generalSettings.nmea_source().GetValue(); | ||||||
|  | 
 | ||||||
|  |    if (positioningPlugin == lastPositioningPlugin_ && | ||||||
|  |        nmeaBaudRate == lastNmeaBaudRate_ && nmeaSource == lastNmeaSource_) | ||||||
|  |    { | ||||||
|  |       return; | ||||||
|  |    } | ||||||
|  | 
 | ||||||
|  |    QGeoPositionInfoSource* positionSource = nullptr; | ||||||
|  | 
 | ||||||
|  |    // TODO: macOS requires permission
 | ||||||
|  |    if (positioningPlugin == types::PositioningPlugin::Default) | ||||||
|  |    { | ||||||
|  |       positionSource = QGeoPositionInfoSource::createDefaultSource(gpsParent_); | ||||||
|  |    } | ||||||
|  |    else if (positioningPlugin == types::PositioningPlugin::Nmea) | ||||||
|  |    { | ||||||
|  |       QVariantMap params {}; | ||||||
|  |       params["nmea.source"]   = QString::fromStdString(nmeaSource); | ||||||
|  |       params["nmea.baudrate"] = static_cast<int>(nmeaBaudRate); | ||||||
|  | 
 | ||||||
|  |       positionSource = | ||||||
|  |          QGeoPositionInfoSource::createSource("nmea", params, gpsParent_); | ||||||
|  |    } | ||||||
|  | 
 | ||||||
|  |    if (positionSource != nullptr) | ||||||
|  |    { | ||||||
|  |       logger_->debug("Using position source: {}", | ||||||
|  |                      positionSource->sourceName().toStdString()); | ||||||
|  | 
 | ||||||
|  |       QObject::connect(positionSource, | ||||||
|  |                        &QGeoPositionInfoSource::positionUpdated, | ||||||
|  |                        self_, | ||||||
|  |                        [this](const QGeoPositionInfo& info) | ||||||
|  |                        { | ||||||
|  |                           auto coordinate = info.coordinate(); | ||||||
|  | 
 | ||||||
|  |                           if (coordinate != position_.coordinate()) | ||||||
|  |                           { | ||||||
|  |                              logger_->trace("Position updated: {}, {}", | ||||||
|  |                                             coordinate.latitude(), | ||||||
|  |                                             coordinate.longitude()); | ||||||
|  |                           } | ||||||
|  | 
 | ||||||
|  |                           position_ = info; | ||||||
|  | 
 | ||||||
|  |                           Q_EMIT self_->PositionUpdated(info); | ||||||
|  |                        }); | ||||||
|  |    } | ||||||
|  |    else | ||||||
|  |    { | ||||||
|  |       logger_->error("Unable to create position source for plugin: {}", | ||||||
|  |                      types::GetPositioningPluginName(positioningPlugin)); | ||||||
|  |       return; | ||||||
|  |    } | ||||||
|  | 
 | ||||||
|  |    lastPositioningPlugin_ = positioningPlugin; | ||||||
|  |    lastNmeaBaudRate_      = nmeaBaudRate; | ||||||
|  |    lastNmeaSource_        = nmeaSource; | ||||||
|  | 
 | ||||||
|  |    std::unique_lock lock {positionSourceMutex_}; | ||||||
|  | 
 | ||||||
|  |    if (geoPositionInfoSource_ != nullptr) | ||||||
|  |    { | ||||||
|  |       geoPositionInfoSource_->stopUpdates(); | ||||||
|  |       delete geoPositionInfoSource_; | ||||||
|  |    } | ||||||
|  | 
 | ||||||
|  |    geoPositionInfoSource_ = positionSource; | ||||||
|  | 
 | ||||||
|  |    if (!uuids_.empty()) | ||||||
|  |    { | ||||||
|  |       positionSource->startUpdates(); | ||||||
|  |    } | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void PositionManager::EnablePositionUpdates(boost::uuids::uuid uuid, | void PositionManager::EnablePositionUpdates(boost::uuids::uuid uuid, | ||||||
|                                             bool               enabled) |                                             bool               enabled) | ||||||
| { | { | ||||||
|    if (p->geoPositionInfoSource_ == nullptr) |    QMetaObject::invokeMethod(QAbstractEventDispatcher::instance(p->gpsThread_), | ||||||
|  |                              [=, this]() | ||||||
|  |                              { p->EnablePositionUpdates(uuid, enabled); }); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void PositionManager::Impl::EnablePositionUpdates(boost::uuids::uuid uuid, | ||||||
|  |                                                   bool               enabled) | ||||||
|  | { | ||||||
|  |    std::unique_lock lock {positionSourceMutex_}; | ||||||
|  | 
 | ||||||
|  |    if (geoPositionInfoSource_ == nullptr) | ||||||
|    { |    { | ||||||
|       return; |       return; | ||||||
|    } |    } | ||||||
| 
 | 
 | ||||||
|    if (enabled) |    if (enabled) | ||||||
|    { |    { | ||||||
|       if (p->uuids_.empty()) |       if (uuids_.empty()) | ||||||
|       { |       { | ||||||
|          p->geoPositionInfoSource_->startUpdates(); |          geoPositionInfoSource_->startUpdates(); | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|       p->uuids_.insert(uuid); |       uuids_.insert(uuid); | ||||||
|    } |    } | ||||||
|    else |    else | ||||||
|    { |    { | ||||||
|       p->uuids_.erase(uuid); |       uuids_.erase(uuid); | ||||||
| 
 | 
 | ||||||
|       if (p->uuids_.empty()) |       if (uuids_.empty()) | ||||||
|       { |       { | ||||||
|          p->geoPositionInfoSource_->stopUpdates(); |          geoPositionInfoSource_->stopUpdates(); | ||||||
|       } |       } | ||||||
|    } |    } | ||||||
| } | } | ||||||
|  |  | ||||||
							
								
								
									
										97
									
								
								scwx-qt/source/scwx/qt/manager/thread_manager.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										97
									
								
								scwx-qt/source/scwx/qt/manager/thread_manager.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,97 @@ | ||||||
|  | #include <scwx/qt/manager/thread_manager.hpp> | ||||||
|  | #include <scwx/util/logger.hpp> | ||||||
|  | 
 | ||||||
|  | #include <execution> | ||||||
|  | #include <mutex> | ||||||
|  | 
 | ||||||
|  | #include <boost/unordered/unordered_flat_map.hpp> | ||||||
|  | #include <QThread> | ||||||
|  | 
 | ||||||
|  | namespace scwx | ||||||
|  | { | ||||||
|  | namespace qt | ||||||
|  | { | ||||||
|  | namespace manager | ||||||
|  | { | ||||||
|  | 
 | ||||||
|  | static const std::string logPrefix_ = "scwx::qt::manager::thread_manager"; | ||||||
|  | static const auto        logger_    = scwx::util::Logger::Create(logPrefix_); | ||||||
|  | 
 | ||||||
|  | class ThreadManager::Impl | ||||||
|  | { | ||||||
|  | public: | ||||||
|  |    explicit Impl() {} | ||||||
|  |    ~Impl() {} | ||||||
|  | 
 | ||||||
|  |    std::mutex mutex_ {}; | ||||||
|  | 
 | ||||||
|  |    boost::unordered_flat_map<std::string, QThread*> threadMap_ {}; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | ThreadManager::ThreadManager() : p(std::make_unique<Impl>()) {} | ||||||
|  | ThreadManager::~ThreadManager() = default; | ||||||
|  | 
 | ||||||
|  | QThread* ThreadManager::thread(const std::string& id, bool autoStart) | ||||||
|  | { | ||||||
|  |    std::unique_lock lock {p->mutex_}; | ||||||
|  |    QThread*         thread = nullptr; | ||||||
|  | 
 | ||||||
|  |    auto it = p->threadMap_.find(id); | ||||||
|  |    if (it != p->threadMap_.cend()) | ||||||
|  |    { | ||||||
|  |       thread = it->second; | ||||||
|  |    } | ||||||
|  | 
 | ||||||
|  |    if (thread == nullptr) | ||||||
|  |    { | ||||||
|  |       logger_->debug("Creating thread: {}", id); | ||||||
|  | 
 | ||||||
|  |       thread = new QThread(this); | ||||||
|  |       p->threadMap_.insert_or_assign(id, thread); | ||||||
|  | 
 | ||||||
|  |       if (autoStart) | ||||||
|  |       { | ||||||
|  |          thread->start(); | ||||||
|  |       } | ||||||
|  |    } | ||||||
|  | 
 | ||||||
|  |    return thread; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ThreadManager::StopThreads() | ||||||
|  | { | ||||||
|  |    std::unique_lock lock {p->mutex_}; | ||||||
|  | 
 | ||||||
|  |    logger_->debug("Stopping threads"); | ||||||
|  | 
 | ||||||
|  |    std::for_each(std::execution::par_unseq, | ||||||
|  |                  p->threadMap_.begin(), | ||||||
|  |                  p->threadMap_.end(), | ||||||
|  |                  [](auto& thread) | ||||||
|  |                  { | ||||||
|  |                     logger_->trace("Stopping thread: {}", thread.first); | ||||||
|  | 
 | ||||||
|  |                     thread.second->quit(); | ||||||
|  |                     if (!thread.second->wait(5000)) | ||||||
|  |                     { | ||||||
|  |                        logger_->warn("Terminating thread: {}", thread.first); | ||||||
|  | 
 | ||||||
|  |                        thread.second->terminate(); | ||||||
|  |                        thread.second->wait(); | ||||||
|  |                     } | ||||||
|  | 
 | ||||||
|  |                     delete thread.second; | ||||||
|  |                  }); | ||||||
|  | 
 | ||||||
|  |    p->threadMap_.clear(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | ThreadManager& ThreadManager::Instance() | ||||||
|  | { | ||||||
|  |    static ThreadManager threadManager_ {}; | ||||||
|  |    return threadManager_; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | } // namespace manager
 | ||||||
|  | } // namespace qt
 | ||||||
|  | } // namespace scwx
 | ||||||
							
								
								
									
										37
									
								
								scwx-qt/source/scwx/qt/manager/thread_manager.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								scwx-qt/source/scwx/qt/manager/thread_manager.hpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,37 @@ | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <memory> | ||||||
|  | 
 | ||||||
|  | #include <QObject> | ||||||
|  | #include <QThread> | ||||||
|  | 
 | ||||||
|  | namespace scwx | ||||||
|  | { | ||||||
|  | namespace qt | ||||||
|  | { | ||||||
|  | namespace manager | ||||||
|  | { | ||||||
|  | 
 | ||||||
|  | class ThreadManager : public QObject | ||||||
|  | { | ||||||
|  |    Q_OBJECT | ||||||
|  |    Q_DISABLE_COPY_MOVE(ThreadManager) | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  |    explicit ThreadManager(); | ||||||
|  |    ~ThreadManager(); | ||||||
|  | 
 | ||||||
|  |    QThread* thread(const std::string& id, bool autoStart = true); | ||||||
|  | 
 | ||||||
|  |    void StopThreads(); | ||||||
|  | 
 | ||||||
|  |    static ThreadManager& Instance(); | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |    class Impl; | ||||||
|  |    std::unique_ptr<Impl> p; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | } // namespace manager
 | ||||||
|  | } // namespace qt
 | ||||||
|  | } // namespace scwx
 | ||||||
|  | @ -3,6 +3,7 @@ | ||||||
| #include <scwx/qt/settings/settings_definitions.hpp> | #include <scwx/qt/settings/settings_definitions.hpp> | ||||||
| #include <scwx/qt/map/map_provider.hpp> | #include <scwx/qt/map/map_provider.hpp> | ||||||
| #include <scwx/qt/types/alert_types.hpp> | #include <scwx/qt/types/alert_types.hpp> | ||||||
|  | #include <scwx/qt/types/location_types.hpp> | ||||||
| #include <scwx/qt/types/qt_types.hpp> | #include <scwx/qt/types/qt_types.hpp> | ||||||
| #include <scwx/qt/types/time_types.hpp> | #include <scwx/qt/types/time_types.hpp> | ||||||
| #include <scwx/util/time.hpp> | #include <scwx/util/time.hpp> | ||||||
|  | @ -37,6 +38,8 @@ public: | ||||||
|          types::GetDefaultTimeZoneName(types::DefaultTimeZone::Radar); |          types::GetDefaultTimeZoneName(types::DefaultTimeZone::Radar); | ||||||
|       std::string defaultMapProviderValue = |       std::string defaultMapProviderValue = | ||||||
|          map::GetMapProviderName(map::MapProvider::MapTiler); |          map::GetMapProviderName(map::MapProvider::MapTiler); | ||||||
|  |       std::string defaultPositioningPlugin = | ||||||
|  |          types::GetPositioningPluginName(types::PositioningPlugin::Default); | ||||||
|       std::string defaultThemeValue = |       std::string defaultThemeValue = | ||||||
|          types::GetUiStyleName(types::UiStyle::Default); |          types::GetUiStyleName(types::UiStyle::Default); | ||||||
| 
 | 
 | ||||||
|  | @ -44,6 +47,7 @@ public: | ||||||
|       boost::to_lower(defaultDefaultAlertActionValue); |       boost::to_lower(defaultDefaultAlertActionValue); | ||||||
|       boost::to_lower(defaultDefaultTimeZoneValue); |       boost::to_lower(defaultDefaultTimeZoneValue); | ||||||
|       boost::to_lower(defaultMapProviderValue); |       boost::to_lower(defaultMapProviderValue); | ||||||
|  |       boost::to_lower(defaultPositioningPlugin); | ||||||
|       boost::to_lower(defaultThemeValue); |       boost::to_lower(defaultThemeValue); | ||||||
| 
 | 
 | ||||||
|       antiAliasingEnabled_.SetDefault(true); |       antiAliasingEnabled_.SetDefault(true); | ||||||
|  | @ -61,6 +65,9 @@ public: | ||||||
|       mapProvider_.SetDefault(defaultMapProviderValue); |       mapProvider_.SetDefault(defaultMapProviderValue); | ||||||
|       mapboxApiKey_.SetDefault("?"); |       mapboxApiKey_.SetDefault("?"); | ||||||
|       maptilerApiKey_.SetDefault("?"); |       maptilerApiKey_.SetDefault("?"); | ||||||
|  |       nmeaBaudRate_.SetDefault(9600); | ||||||
|  |       nmeaSource_.SetDefault(""); | ||||||
|  |       positioningPlugin_.SetDefault(defaultPositioningPlugin); | ||||||
|       showMapAttribution_.SetDefault(true); |       showMapAttribution_.SetDefault(true); | ||||||
|       showMapCenter_.SetDefault(false); |       showMapCenter_.SetDefault(false); | ||||||
|       showMapLogo_.SetDefault(true); |       showMapLogo_.SetDefault(true); | ||||||
|  | @ -83,6 +90,8 @@ public: | ||||||
|       loopSpeed_.SetMaximum(99.99); |       loopSpeed_.SetMaximum(99.99); | ||||||
|       loopTime_.SetMinimum(1); |       loopTime_.SetMinimum(1); | ||||||
|       loopTime_.SetMaximum(1440); |       loopTime_.SetMaximum(1440); | ||||||
|  |       nmeaBaudRate_.SetMinimum(1); | ||||||
|  |       nmeaBaudRate_.SetMaximum(999999999); | ||||||
| 
 | 
 | ||||||
|       clockFormat_.SetValidator( |       clockFormat_.SetValidator( | ||||||
|          SCWX_SETTINGS_ENUM_VALIDATOR(scwx::util::ClockFormat, |          SCWX_SETTINGS_ENUM_VALIDATOR(scwx::util::ClockFormat, | ||||||
|  | @ -104,6 +113,10 @@ public: | ||||||
|                                  { return !value.empty(); }); |                                  { return !value.empty(); }); | ||||||
|       maptilerApiKey_.SetValidator([](const std::string& value) |       maptilerApiKey_.SetValidator([](const std::string& value) | ||||||
|                                    { return !value.empty(); }); |                                    { return !value.empty(); }); | ||||||
|  |       positioningPlugin_.SetValidator( | ||||||
|  |          SCWX_SETTINGS_ENUM_VALIDATOR(types::PositioningPlugin, | ||||||
|  |                                       types::PositioningPluginIterator(), | ||||||
|  |                                       types::GetPositioningPluginName)); | ||||||
|       theme_.SetValidator(                            //
 |       theme_.SetValidator(                            //
 | ||||||
|          SCWX_SETTINGS_ENUM_VALIDATOR(types::UiStyle, //
 |          SCWX_SETTINGS_ENUM_VALIDATOR(types::UiStyle, //
 | ||||||
|                                       types::UiStyleIterator(), |                                       types::UiStyleIterator(), | ||||||
|  | @ -130,6 +143,9 @@ public: | ||||||
|    SettingsVariable<std::string>                mapProvider_ {"map_provider"}; |    SettingsVariable<std::string>                mapProvider_ {"map_provider"}; | ||||||
|    SettingsVariable<std::string>  mapboxApiKey_ {"mapbox_api_key"}; |    SettingsVariable<std::string>  mapboxApiKey_ {"mapbox_api_key"}; | ||||||
|    SettingsVariable<std::string>  maptilerApiKey_ {"maptiler_api_key"}; |    SettingsVariable<std::string>  maptilerApiKey_ {"maptiler_api_key"}; | ||||||
|  |    SettingsVariable<std::int64_t> nmeaBaudRate_ {"nmea_baud_rate"}; | ||||||
|  |    SettingsVariable<std::string>  nmeaSource_ {"nmea_source"}; | ||||||
|  |    SettingsVariable<std::string>  positioningPlugin_ {"positioning_plugin"}; | ||||||
|    SettingsVariable<bool>         showMapAttribution_ {"show_map_attribution"}; |    SettingsVariable<bool>         showMapAttribution_ {"show_map_attribution"}; | ||||||
|    SettingsVariable<bool>         showMapCenter_ {"show_map_center"}; |    SettingsVariable<bool>         showMapCenter_ {"show_map_center"}; | ||||||
|    SettingsVariable<bool>         showMapLogo_ {"show_map_logo"}; |    SettingsVariable<bool>         showMapLogo_ {"show_map_logo"}; | ||||||
|  | @ -157,6 +173,9 @@ GeneralSettings::GeneralSettings() : | ||||||
|                       &p->mapProvider_, |                       &p->mapProvider_, | ||||||
|                       &p->mapboxApiKey_, |                       &p->mapboxApiKey_, | ||||||
|                       &p->maptilerApiKey_, |                       &p->maptilerApiKey_, | ||||||
|  |                       &p->nmeaBaudRate_, | ||||||
|  |                       &p->nmeaSource_, | ||||||
|  |                       &p->positioningPlugin_, | ||||||
|                       &p->showMapAttribution_, |                       &p->showMapAttribution_, | ||||||
|                       &p->showMapCenter_, |                       &p->showMapCenter_, | ||||||
|                       &p->showMapLogo_, |                       &p->showMapLogo_, | ||||||
|  | @ -248,6 +267,21 @@ SettingsVariable<std::string>& GeneralSettings::maptiler_api_key() const | ||||||
|    return p->maptilerApiKey_; |    return p->maptilerApiKey_; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | SettingsVariable<std::int64_t>& GeneralSettings::nmea_baud_rate() const | ||||||
|  | { | ||||||
|  |    return p->nmeaBaudRate_; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | SettingsVariable<std::string>& GeneralSettings::nmea_source() const | ||||||
|  | { | ||||||
|  |    return p->nmeaSource_; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | SettingsVariable<std::string>& GeneralSettings::positioning_plugin() const | ||||||
|  | { | ||||||
|  |    return p->positioningPlugin_; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| SettingsVariable<bool>& GeneralSettings::show_map_attribution() const | SettingsVariable<bool>& GeneralSettings::show_map_attribution() const | ||||||
| { | { | ||||||
|    return p->showMapAttribution_; |    return p->showMapAttribution_; | ||||||
|  | @ -319,6 +353,9 @@ bool operator==(const GeneralSettings& lhs, const GeneralSettings& rhs) | ||||||
|            lhs.p->mapProvider_ == rhs.p->mapProvider_ && |            lhs.p->mapProvider_ == rhs.p->mapProvider_ && | ||||||
|            lhs.p->mapboxApiKey_ == rhs.p->mapboxApiKey_ && |            lhs.p->mapboxApiKey_ == rhs.p->mapboxApiKey_ && | ||||||
|            lhs.p->maptilerApiKey_ == rhs.p->maptilerApiKey_ && |            lhs.p->maptilerApiKey_ == rhs.p->maptilerApiKey_ && | ||||||
|  |            lhs.p->nmeaBaudRate_ == rhs.p->nmeaBaudRate_ && | ||||||
|  |            lhs.p->nmeaSource_ == rhs.p->nmeaSource_ && | ||||||
|  |            lhs.p->positioningPlugin_ == rhs.p->positioningPlugin_ && | ||||||
|            lhs.p->showMapAttribution_ == rhs.p->showMapAttribution_ && |            lhs.p->showMapAttribution_ == rhs.p->showMapAttribution_ && | ||||||
|            lhs.p->showMapCenter_ == rhs.p->showMapCenter_ && |            lhs.p->showMapCenter_ == rhs.p->showMapCenter_ && | ||||||
|            lhs.p->showMapLogo_ == rhs.p->showMapLogo_ && |            lhs.p->showMapLogo_ == rhs.p->showMapLogo_ && | ||||||
|  |  | ||||||
|  | @ -40,6 +40,9 @@ public: | ||||||
|    SettingsVariable<std::string>&                map_provider() const; |    SettingsVariable<std::string>&                map_provider() const; | ||||||
|    SettingsVariable<std::string>&                mapbox_api_key() const; |    SettingsVariable<std::string>&                mapbox_api_key() const; | ||||||
|    SettingsVariable<std::string>&                maptiler_api_key() const; |    SettingsVariable<std::string>&                maptiler_api_key() const; | ||||||
|  |    SettingsVariable<std::int64_t>&               nmea_baud_rate() const; | ||||||
|  |    SettingsVariable<std::string>&                nmea_source() const; | ||||||
|  |    SettingsVariable<std::string>&                positioning_plugin() const; | ||||||
|    SettingsVariable<bool>&                       show_map_attribution() const; |    SettingsVariable<bool>&                       show_map_attribution() const; | ||||||
|    SettingsVariable<bool>&                       show_map_center() const; |    SettingsVariable<bool>&                       show_map_center() const; | ||||||
|    SettingsVariable<bool>&                       show_map_logo() const; |    SettingsVariable<bool>&                       show_map_logo() const; | ||||||
|  |  | ||||||
|  | @ -19,13 +19,24 @@ static const std::unordered_map<LocationMethod, std::string> | ||||||
|                         {LocationMethod::All, "All"}, |                         {LocationMethod::All, "All"}, | ||||||
|                         {LocationMethod::Unknown, "?"}}; |                         {LocationMethod::Unknown, "?"}}; | ||||||
| 
 | 
 | ||||||
|  | static const std::unordered_map<PositioningPlugin, std::string> | ||||||
|  |    positioningPluginName_ {{PositioningPlugin::Default, "Default"}, | ||||||
|  |                            {PositioningPlugin::Nmea, "NMEA"}, | ||||||
|  |                            {PositioningPlugin::Unknown, "?"}}; | ||||||
|  | 
 | ||||||
| SCWX_GET_ENUM(LocationMethod, GetLocationMethod, locationMethodName_) | SCWX_GET_ENUM(LocationMethod, GetLocationMethod, locationMethodName_) | ||||||
|  | SCWX_GET_ENUM(PositioningPlugin, GetPositioningPlugin, positioningPluginName_) | ||||||
| 
 | 
 | ||||||
| const std::string& GetLocationMethodName(LocationMethod locationMethod) | const std::string& GetLocationMethodName(LocationMethod locationMethod) | ||||||
| { | { | ||||||
|    return locationMethodName_.at(locationMethod); |    return locationMethodName_.at(locationMethod); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | const std::string& GetPositioningPluginName(PositioningPlugin positioningPlugin) | ||||||
|  | { | ||||||
|  |    return positioningPluginName_.at(positioningPlugin); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| } // namespace types
 | } // namespace types
 | ||||||
| } // namespace qt
 | } // namespace qt
 | ||||||
| } // namespace scwx
 | } // namespace scwx
 | ||||||
|  |  | ||||||
|  | @ -23,9 +23,24 @@ typedef scwx::util:: | ||||||
|    Iterator<LocationMethod, LocationMethod::Fixed, LocationMethod::All> |    Iterator<LocationMethod, LocationMethod::Fixed, LocationMethod::All> | ||||||
|       LocationMethodIterator; |       LocationMethodIterator; | ||||||
| 
 | 
 | ||||||
|  | enum class PositioningPlugin | ||||||
|  | { | ||||||
|  |    Default, | ||||||
|  |    Nmea, | ||||||
|  |    Unknown | ||||||
|  | }; | ||||||
|  | typedef scwx::util::Iterator<PositioningPlugin, | ||||||
|  |                              PositioningPlugin::Default, | ||||||
|  |                              PositioningPlugin::Nmea> | ||||||
|  |    PositioningPluginIterator; | ||||||
|  | 
 | ||||||
| LocationMethod     GetLocationMethod(const std::string& name); | LocationMethod     GetLocationMethod(const std::string& name); | ||||||
| const std::string& GetLocationMethodName(LocationMethod locationMethod); | const std::string& GetLocationMethodName(LocationMethod locationMethod); | ||||||
| 
 | 
 | ||||||
|  | PositioningPlugin GetPositioningPlugin(const std::string& name); | ||||||
|  | const std::string& | ||||||
|  | GetPositioningPluginName(PositioningPlugin positioningPlugin); | ||||||
|  | 
 | ||||||
| } // namespace types
 | } // namespace types
 | ||||||
| } // namespace qt
 | } // namespace qt
 | ||||||
| } // namespace scwx
 | } // namespace scwx
 | ||||||
|  |  | ||||||
							
								
								
									
										201
									
								
								scwx-qt/source/scwx/qt/ui/gps_info_dialog.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										201
									
								
								scwx-qt/source/scwx/qt/ui/gps_info_dialog.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,201 @@ | ||||||
|  | #include "gps_info_dialog.hpp" | ||||||
|  | #include "ui_gps_info_dialog.h" | ||||||
|  | 
 | ||||||
|  | #include <scwx/qt/manager/position_manager.hpp> | ||||||
|  | #include <scwx/common/geographic.hpp> | ||||||
|  | #include <scwx/util/time.hpp> | ||||||
|  | 
 | ||||||
|  | #include <units/angle.h> | ||||||
|  | #include <units/length.h> | ||||||
|  | #include <units/velocity.h> | ||||||
|  | #include <QClipboard> | ||||||
|  | #include <QGeoPositionInfo> | ||||||
|  | 
 | ||||||
|  | namespace scwx | ||||||
|  | { | ||||||
|  | namespace qt | ||||||
|  | { | ||||||
|  | namespace ui | ||||||
|  | { | ||||||
|  | 
 | ||||||
|  | static const QString kDisabledString_ = "---"; | ||||||
|  | 
 | ||||||
|  | class GpsInfoDialog::Impl | ||||||
|  | { | ||||||
|  | public: | ||||||
|  |    explicit Impl(GpsInfoDialog* self) : self_ {self} {}; | ||||||
|  |    ~Impl() = default; | ||||||
|  | 
 | ||||||
|  |    std::shared_ptr<manager::PositionManager> positionManager_ { | ||||||
|  |       manager::PositionManager::Instance()}; | ||||||
|  | 
 | ||||||
|  |    void Update(const QGeoPositionInfo& info, bool updateTime = true); | ||||||
|  | 
 | ||||||
|  |    GpsInfoDialog* self_; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | GpsInfoDialog::GpsInfoDialog(QWidget* parent) : | ||||||
|  |     QDialog(parent), p {std::make_unique<Impl>(this)}, ui(new Ui::GpsInfoDialog) | ||||||
|  | { | ||||||
|  |    ui->setupUi(this); | ||||||
|  | 
 | ||||||
|  |    p->Update({}, false); | ||||||
|  | 
 | ||||||
|  |    connect(p->positionManager_.get(), | ||||||
|  |            &manager::PositionManager::PositionUpdated, | ||||||
|  |            this, | ||||||
|  |            [this](const QGeoPositionInfo& info) { p->Update(info); }); | ||||||
|  | 
 | ||||||
|  |    connect(ui->copyCoordinateButton, | ||||||
|  |            &QAbstractButton::clicked, | ||||||
|  |            this, | ||||||
|  |            [this]() | ||||||
|  |            { | ||||||
|  |               QClipboard* clipboard = QGuiApplication::clipboard(); | ||||||
|  |               clipboard->setText(ui->coordinateLabel->text()); | ||||||
|  |            }); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | GpsInfoDialog::~GpsInfoDialog() | ||||||
|  | { | ||||||
|  |    delete ui; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void GpsInfoDialog::Impl::Update(const QGeoPositionInfo& info, bool updateTime) | ||||||
|  | { | ||||||
|  |    auto coordinate = info.coordinate(); | ||||||
|  | 
 | ||||||
|  |    if (coordinate.isValid()) | ||||||
|  |    { | ||||||
|  |       const QString latitude = QString::fromStdString( | ||||||
|  |          common::GetLatitudeString(coordinate.latitude())); | ||||||
|  |       const QString longitude = QString::fromStdString( | ||||||
|  |          common::GetLongitudeString(coordinate.longitude())); | ||||||
|  | 
 | ||||||
|  |       self_->ui->coordinateLabel->setText( | ||||||
|  |          QString("%1, %2").arg(latitude).arg(longitude)); | ||||||
|  |    } | ||||||
|  |    else | ||||||
|  |    { | ||||||
|  |       self_->ui->coordinateLabel->setText(kDisabledString_); | ||||||
|  |    } | ||||||
|  | 
 | ||||||
|  |    if (coordinate.type() == QGeoCoordinate::CoordinateType::Coordinate3D) | ||||||
|  |    { | ||||||
|  |       units::length::meters<double> altitude {coordinate.altitude()}; | ||||||
|  |       self_->ui->altitudeLabel->setText( | ||||||
|  |          QString::fromStdString(units::to_string(altitude))); | ||||||
|  |    } | ||||||
|  |    else | ||||||
|  |    { | ||||||
|  |       self_->ui->altitudeLabel->setText(kDisabledString_); | ||||||
|  |    } | ||||||
|  | 
 | ||||||
|  |    if (info.hasAttribute(QGeoPositionInfo::Attribute::Direction)) | ||||||
|  |    { | ||||||
|  |       units::angle::degrees<double> direction { | ||||||
|  |          info.attribute(QGeoPositionInfo::Attribute::Direction)}; | ||||||
|  |       self_->ui->directionLabel->setText( | ||||||
|  |          QString::fromStdString(units::to_string(direction))); | ||||||
|  |    } | ||||||
|  |    else | ||||||
|  |    { | ||||||
|  |       self_->ui->directionLabel->setText(kDisabledString_); | ||||||
|  |    } | ||||||
|  | 
 | ||||||
|  |    if (info.hasAttribute(QGeoPositionInfo::Attribute::GroundSpeed)) | ||||||
|  |    { | ||||||
|  |       units::velocity::meters_per_second<double> groundSpeed { | ||||||
|  |          info.attribute(QGeoPositionInfo::Attribute::GroundSpeed)}; | ||||||
|  |       self_->ui->groundSpeedLabel->setText( | ||||||
|  |          QString::fromStdString(units::to_string(groundSpeed))); | ||||||
|  |    } | ||||||
|  |    else | ||||||
|  |    { | ||||||
|  |       self_->ui->groundSpeedLabel->setText(kDisabledString_); | ||||||
|  |    } | ||||||
|  | 
 | ||||||
|  |    if (info.hasAttribute(QGeoPositionInfo::Attribute::VerticalSpeed)) | ||||||
|  |    { | ||||||
|  |       units::velocity::meters_per_second<double> verticalSpeed { | ||||||
|  |          info.attribute(QGeoPositionInfo::Attribute::VerticalSpeed)}; | ||||||
|  |       self_->ui->verticalSpeedLabel->setText( | ||||||
|  |          QString::fromStdString(units::to_string(verticalSpeed))); | ||||||
|  |    } | ||||||
|  |    else | ||||||
|  |    { | ||||||
|  |       self_->ui->verticalSpeedLabel->setText(kDisabledString_); | ||||||
|  |    } | ||||||
|  | 
 | ||||||
|  |    if (info.hasAttribute(QGeoPositionInfo::Attribute::MagneticVariation)) | ||||||
|  |    { | ||||||
|  |       units::angle::degrees<double> magneticVariation { | ||||||
|  |          info.attribute(QGeoPositionInfo::Attribute::MagneticVariation)}; | ||||||
|  |       self_->ui->magneticVariationLabel->setText( | ||||||
|  |          QString::fromStdString(units::to_string(magneticVariation))); | ||||||
|  |    } | ||||||
|  |    else | ||||||
|  |    { | ||||||
|  |       self_->ui->magneticVariationLabel->setText(kDisabledString_); | ||||||
|  |    } | ||||||
|  | 
 | ||||||
|  |    if (info.hasAttribute(QGeoPositionInfo::Attribute::HorizontalAccuracy)) | ||||||
|  |    { | ||||||
|  |       units::length::meters<double> horizontalAccuracy { | ||||||
|  |          info.attribute(QGeoPositionInfo::Attribute::HorizontalAccuracy)}; | ||||||
|  |       if (!std::isnan(horizontalAccuracy.value())) | ||||||
|  |       { | ||||||
|  |          self_->ui->horizontalAccuracyLabel->setText( | ||||||
|  |             QString::fromStdString(units::to_string(horizontalAccuracy))); | ||||||
|  |       } | ||||||
|  |       else | ||||||
|  |       { | ||||||
|  |          self_->ui->horizontalAccuracyLabel->setText(kDisabledString_); | ||||||
|  |       } | ||||||
|  |    } | ||||||
|  |    else | ||||||
|  |    { | ||||||
|  |       self_->ui->horizontalAccuracyLabel->setText(kDisabledString_); | ||||||
|  |    } | ||||||
|  | 
 | ||||||
|  |    if (info.hasAttribute(QGeoPositionInfo::Attribute::VerticalAccuracy)) | ||||||
|  |    { | ||||||
|  |       units::length::meters<double> verticalAccuracy { | ||||||
|  |          info.attribute(QGeoPositionInfo::Attribute::VerticalAccuracy)}; | ||||||
|  |       if (!std::isnan(verticalAccuracy.value())) | ||||||
|  |       { | ||||||
|  |          self_->ui->verticalAccuracyLabel->setText( | ||||||
|  |             QString::fromStdString(units::to_string(verticalAccuracy))); | ||||||
|  |       } | ||||||
|  |       else | ||||||
|  |       { | ||||||
|  |          self_->ui->verticalAccuracyLabel->setText(kDisabledString_); | ||||||
|  |       } | ||||||
|  |    } | ||||||
|  |    else | ||||||
|  |    { | ||||||
|  |       self_->ui->verticalAccuracyLabel->setText(kDisabledString_); | ||||||
|  |    } | ||||||
|  | 
 | ||||||
|  |    if (info.hasAttribute(QGeoPositionInfo::Attribute::DirectionAccuracy)) | ||||||
|  |    { | ||||||
|  |       units::angle::degrees<double> directionAccuracy { | ||||||
|  |          info.attribute(QGeoPositionInfo::Attribute::DirectionAccuracy)}; | ||||||
|  |       self_->ui->directionAccuracyLabel->setText( | ||||||
|  |          QString::fromStdString(units::to_string(directionAccuracy))); | ||||||
|  |    } | ||||||
|  |    else | ||||||
|  |    { | ||||||
|  |       self_->ui->directionAccuracyLabel->setText(kDisabledString_); | ||||||
|  |    } | ||||||
|  | 
 | ||||||
|  |    if (updateTime) | ||||||
|  |    { | ||||||
|  |       self_->ui->lastUpdateLabel->setText( | ||||||
|  |          info.timestamp().toString(Qt::DateFormat::ISODate)); | ||||||
|  |    } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | } // namespace ui
 | ||||||
|  | } // namespace qt
 | ||||||
|  | } // namespace scwx
 | ||||||
							
								
								
									
										36
									
								
								scwx-qt/source/scwx/qt/ui/gps_info_dialog.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								scwx-qt/source/scwx/qt/ui/gps_info_dialog.hpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,36 @@ | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <QDialog> | ||||||
|  | 
 | ||||||
|  | namespace Ui | ||||||
|  | { | ||||||
|  | class GpsInfoDialog; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | namespace scwx | ||||||
|  | { | ||||||
|  | namespace qt | ||||||
|  | { | ||||||
|  | namespace ui | ||||||
|  | { | ||||||
|  | 
 | ||||||
|  | class GpsInfoDialog : public QDialog | ||||||
|  | { | ||||||
|  |    Q_OBJECT | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |    Q_DISABLE_COPY(GpsInfoDialog) | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  |    explicit GpsInfoDialog(QWidget* parent = nullptr); | ||||||
|  |    ~GpsInfoDialog(); | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |    class Impl; | ||||||
|  |    std::unique_ptr<Impl> p; | ||||||
|  |    Ui::GpsInfoDialog*    ui; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | } // namespace ui
 | ||||||
|  | } // namespace qt
 | ||||||
|  | } // namespace scwx
 | ||||||
							
								
								
									
										266
									
								
								scwx-qt/source/scwx/qt/ui/gps_info_dialog.ui
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										266
									
								
								scwx-qt/source/scwx/qt/ui/gps_info_dialog.ui
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,266 @@ | ||||||
|  | <?xml version="1.0" encoding="UTF-8"?> | ||||||
|  | <ui version="4.0"> | ||||||
|  |  <class>GpsInfoDialog</class> | ||||||
|  |  <widget class="QDialog" name="GpsInfoDialog"> | ||||||
|  |   <property name="geometry"> | ||||||
|  |    <rect> | ||||||
|  |     <x>0</x> | ||||||
|  |     <y>0</y> | ||||||
|  |     <width>313</width> | ||||||
|  |     <height>292</height> | ||||||
|  |    </rect> | ||||||
|  |   </property> | ||||||
|  |   <property name="windowTitle"> | ||||||
|  |    <string>GPS Info</string> | ||||||
|  |   </property> | ||||||
|  |   <layout class="QVBoxLayout" name="verticalLayout"> | ||||||
|  |    <item> | ||||||
|  |     <widget class="QWidget" name="widget" native="true"> | ||||||
|  |      <layout class="QGridLayout" name="gridLayout"> | ||||||
|  |       <property name="verticalSpacing"> | ||||||
|  |        <number>6</number> | ||||||
|  |       </property> | ||||||
|  |       <item row="8" column="0"> | ||||||
|  |        <widget class="QLabel" name="label_17"> | ||||||
|  |         <property name="text"> | ||||||
|  |          <string>Direction Accuracy</string> | ||||||
|  |         </property> | ||||||
|  |        </widget> | ||||||
|  |       </item> | ||||||
|  |       <item row="5" column="1"> | ||||||
|  |        <widget class="QLabel" name="magneticVariationLabel"> | ||||||
|  |         <property name="text"> | ||||||
|  |          <string/> | ||||||
|  |         </property> | ||||||
|  |        </widget> | ||||||
|  |       </item> | ||||||
|  |       <item row="6" column="1"> | ||||||
|  |        <widget class="QLabel" name="horizontalAccuracyLabel"> | ||||||
|  |         <property name="text"> | ||||||
|  |          <string/> | ||||||
|  |         </property> | ||||||
|  |        </widget> | ||||||
|  |       </item> | ||||||
|  |       <item row="8" column="1"> | ||||||
|  |        <widget class="QLabel" name="directionAccuracyLabel"> | ||||||
|  |         <property name="text"> | ||||||
|  |          <string/> | ||||||
|  |         </property> | ||||||
|  |        </widget> | ||||||
|  |       </item> | ||||||
|  |       <item row="10" column="0"> | ||||||
|  |        <spacer name="verticalSpacer"> | ||||||
|  |         <property name="orientation"> | ||||||
|  |          <enum>Qt::Orientation::Vertical</enum> | ||||||
|  |         </property> | ||||||
|  |         <property name="sizeHint" stdset="0"> | ||||||
|  |          <size> | ||||||
|  |           <width>20</width> | ||||||
|  |           <height>40</height> | ||||||
|  |          </size> | ||||||
|  |         </property> | ||||||
|  |        </spacer> | ||||||
|  |       </item> | ||||||
|  |       <item row="5" column="0"> | ||||||
|  |        <widget class="QLabel" name="label_11"> | ||||||
|  |         <property name="text"> | ||||||
|  |          <string>Magnetic Variation</string> | ||||||
|  |         </property> | ||||||
|  |        </widget> | ||||||
|  |       </item> | ||||||
|  |       <item row="2" column="0"> | ||||||
|  |        <widget class="QLabel" name="label_5"> | ||||||
|  |         <property name="text"> | ||||||
|  |          <string>Direction</string> | ||||||
|  |         </property> | ||||||
|  |        </widget> | ||||||
|  |       </item> | ||||||
|  |       <item row="4" column="0"> | ||||||
|  |        <widget class="QLabel" name="label_9"> | ||||||
|  |         <property name="text"> | ||||||
|  |          <string>Vertical Speed</string> | ||||||
|  |         </property> | ||||||
|  |        </widget> | ||||||
|  |       </item> | ||||||
|  |       <item row="2" column="1"> | ||||||
|  |        <widget class="QLabel" name="directionLabel"> | ||||||
|  |         <property name="text"> | ||||||
|  |          <string/> | ||||||
|  |         </property> | ||||||
|  |        </widget> | ||||||
|  |       </item> | ||||||
|  |       <item row="1" column="1"> | ||||||
|  |        <widget class="QLabel" name="altitudeLabel"> | ||||||
|  |         <property name="text"> | ||||||
|  |          <string/> | ||||||
|  |         </property> | ||||||
|  |        </widget> | ||||||
|  |       </item> | ||||||
|  |       <item row="7" column="0"> | ||||||
|  |        <widget class="QLabel" name="label_15"> | ||||||
|  |         <property name="text"> | ||||||
|  |          <string>Vertical Accuracy</string> | ||||||
|  |         </property> | ||||||
|  |        </widget> | ||||||
|  |       </item> | ||||||
|  |       <item row="0" column="2"> | ||||||
|  |        <widget class="QToolButton" name="copyCoordinateButton"> | ||||||
|  |         <property name="text"> | ||||||
|  |          <string>...</string> | ||||||
|  |         </property> | ||||||
|  |         <property name="icon"> | ||||||
|  |          <iconset resource="../../../../scwx-qt.qrc"> | ||||||
|  |           <normaloff>:/res/icons/font-awesome-6/copy-regular.svg</normaloff>:/res/icons/font-awesome-6/copy-regular.svg</iconset> | ||||||
|  |         </property> | ||||||
|  |        </widget> | ||||||
|  |       </item> | ||||||
|  |       <item row="6" column="0"> | ||||||
|  |        <widget class="QLabel" name="label_13"> | ||||||
|  |         <property name="text"> | ||||||
|  |          <string>Horizonal Accuracy</string> | ||||||
|  |         </property> | ||||||
|  |        </widget> | ||||||
|  |       </item> | ||||||
|  |       <item row="3" column="1"> | ||||||
|  |        <widget class="QLabel" name="groundSpeedLabel"> | ||||||
|  |         <property name="text"> | ||||||
|  |          <string/> | ||||||
|  |         </property> | ||||||
|  |        </widget> | ||||||
|  |       </item> | ||||||
|  |       <item row="3" column="0"> | ||||||
|  |        <widget class="QLabel" name="label_7"> | ||||||
|  |         <property name="text"> | ||||||
|  |          <string>Ground Speed</string> | ||||||
|  |         </property> | ||||||
|  |        </widget> | ||||||
|  |       </item> | ||||||
|  |       <item row="4" column="1"> | ||||||
|  |        <widget class="QLabel" name="verticalSpeedLabel"> | ||||||
|  |         <property name="text"> | ||||||
|  |          <string/> | ||||||
|  |         </property> | ||||||
|  |        </widget> | ||||||
|  |       </item> | ||||||
|  |       <item row="0" column="1"> | ||||||
|  |        <widget class="QLabel" name="coordinateLabel"> | ||||||
|  |         <property name="sizePolicy"> | ||||||
|  |          <sizepolicy hsizetype="Expanding" vsizetype="Preferred"> | ||||||
|  |           <horstretch>0</horstretch> | ||||||
|  |           <verstretch>0</verstretch> | ||||||
|  |          </sizepolicy> | ||||||
|  |         </property> | ||||||
|  |         <property name="text"> | ||||||
|  |          <string/> | ||||||
|  |         </property> | ||||||
|  |        </widget> | ||||||
|  |       </item> | ||||||
|  |       <item row="0" column="0"> | ||||||
|  |        <widget class="QLabel" name="label"> | ||||||
|  |         <property name="text"> | ||||||
|  |          <string>Coordinate</string> | ||||||
|  |         </property> | ||||||
|  |        </widget> | ||||||
|  |       </item> | ||||||
|  |       <item row="7" column="1"> | ||||||
|  |        <widget class="QLabel" name="verticalAccuracyLabel"> | ||||||
|  |         <property name="text"> | ||||||
|  |          <string/> | ||||||
|  |         </property> | ||||||
|  |        </widget> | ||||||
|  |       </item> | ||||||
|  |       <item row="1" column="0"> | ||||||
|  |        <widget class="QLabel" name="label_2"> | ||||||
|  |         <property name="text"> | ||||||
|  |          <string>Altitude</string> | ||||||
|  |         </property> | ||||||
|  |        </widget> | ||||||
|  |       </item> | ||||||
|  |       <item row="9" column="0"> | ||||||
|  |        <widget class="QLabel" name="label_3"> | ||||||
|  |         <property name="text"> | ||||||
|  |          <string>Last Update</string> | ||||||
|  |         </property> | ||||||
|  |        </widget> | ||||||
|  |       </item> | ||||||
|  |       <item row="9" column="1"> | ||||||
|  |        <widget class="QLabel" name="lastUpdateLabel"> | ||||||
|  |         <property name="text"> | ||||||
|  |          <string>Never</string> | ||||||
|  |         </property> | ||||||
|  |        </widget> | ||||||
|  |       </item> | ||||||
|  |      </layout> | ||||||
|  |      <zorder>verticalAccuracyLabel</zorder> | ||||||
|  |      <zorder>label_11</zorder> | ||||||
|  |      <zorder>groundSpeedLabel</zorder> | ||||||
|  |      <zorder>magneticVariationLabel</zorder> | ||||||
|  |      <zorder>label_2</zorder> | ||||||
|  |      <zorder>coordinateLabel</zorder> | ||||||
|  |      <zorder>altitudeLabel</zorder> | ||||||
|  |      <zorder>directionAccuracyLabel</zorder> | ||||||
|  |      <zorder>label_5</zorder> | ||||||
|  |      <zorder>label_15</zorder> | ||||||
|  |      <zorder>label_13</zorder> | ||||||
|  |      <zorder>verticalSpeedLabel</zorder> | ||||||
|  |      <zorder>label_7</zorder> | ||||||
|  |      <zorder>label</zorder> | ||||||
|  |      <zorder>horizontalAccuracyLabel</zorder> | ||||||
|  |      <zorder>label_17</zorder> | ||||||
|  |      <zorder>directionLabel</zorder> | ||||||
|  |      <zorder>label_9</zorder> | ||||||
|  |      <zorder>copyCoordinateButton</zorder> | ||||||
|  |      <zorder>label_3</zorder> | ||||||
|  |      <zorder>lastUpdateLabel</zorder> | ||||||
|  |     </widget> | ||||||
|  |    </item> | ||||||
|  |    <item> | ||||||
|  |     <widget class="QDialogButtonBox" name="buttonBox"> | ||||||
|  |      <property name="orientation"> | ||||||
|  |       <enum>Qt::Orientation::Horizontal</enum> | ||||||
|  |      </property> | ||||||
|  |      <property name="standardButtons"> | ||||||
|  |       <set>QDialogButtonBox::StandardButton::Close</set> | ||||||
|  |      </property> | ||||||
|  |     </widget> | ||||||
|  |    </item> | ||||||
|  |   </layout> | ||||||
|  |  </widget> | ||||||
|  |  <resources> | ||||||
|  |   <include location="../../../../scwx-qt.qrc"/> | ||||||
|  |  </resources> | ||||||
|  |  <connections> | ||||||
|  |   <connection> | ||||||
|  |    <sender>buttonBox</sender> | ||||||
|  |    <signal>accepted()</signal> | ||||||
|  |    <receiver>GpsInfoDialog</receiver> | ||||||
|  |    <slot>accept()</slot> | ||||||
|  |    <hints> | ||||||
|  |     <hint type="sourcelabel"> | ||||||
|  |      <x>248</x> | ||||||
|  |      <y>254</y> | ||||||
|  |     </hint> | ||||||
|  |     <hint type="destinationlabel"> | ||||||
|  |      <x>157</x> | ||||||
|  |      <y>274</y> | ||||||
|  |     </hint> | ||||||
|  |    </hints> | ||||||
|  |   </connection> | ||||||
|  |   <connection> | ||||||
|  |    <sender>buttonBox</sender> | ||||||
|  |    <signal>rejected()</signal> | ||||||
|  |    <receiver>GpsInfoDialog</receiver> | ||||||
|  |    <slot>reject()</slot> | ||||||
|  |    <hints> | ||||||
|  |     <hint type="sourcelabel"> | ||||||
|  |      <x>316</x> | ||||||
|  |      <y>260</y> | ||||||
|  |     </hint> | ||||||
|  |     <hint type="destinationlabel"> | ||||||
|  |      <x>286</x> | ||||||
|  |      <y>274</y> | ||||||
|  |     </hint> | ||||||
|  |    </hints> | ||||||
|  |   </connection> | ||||||
|  |  </connections> | ||||||
|  | </ui> | ||||||
							
								
								
									
										613
									
								
								scwx-qt/source/scwx/qt/ui/serial_port_dialog.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										613
									
								
								scwx-qt/source/scwx/qt/ui/serial_port_dialog.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,613 @@ | ||||||
|  | #define __STDC_WANT_LIB_EXT1__ 1 | ||||||
|  | 
 | ||||||
|  | #include "serial_port_dialog.hpp" | ||||||
|  | #include "ui_serial_port_dialog.h" | ||||||
|  | 
 | ||||||
|  | #include <scwx/util/logger.hpp> | ||||||
|  | #include <scwx/util/strings.hpp> | ||||||
|  | 
 | ||||||
|  | #include <unordered_map> | ||||||
|  | 
 | ||||||
|  | #include <QPushButton> | ||||||
|  | #include <QSerialPortInfo> | ||||||
|  | #include <QSortFilterProxyModel> | ||||||
|  | #include <QStandardItemModel> | ||||||
|  | 
 | ||||||
|  | #if defined(_WIN32) | ||||||
|  | #   include <Windows.h> | ||||||
|  | #   include <SetupAPI.h> | ||||||
|  | #   include <initguid.h> | ||||||
|  | #   include <devguid.h> | ||||||
|  | #   include <devpkey.h> | ||||||
|  | #   include <tchar.h> | ||||||
|  | #   include <cstdlib> | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | namespace scwx | ||||||
|  | { | ||||||
|  | namespace qt | ||||||
|  | { | ||||||
|  | namespace ui | ||||||
|  | { | ||||||
|  | 
 | ||||||
|  | static const std::string logPrefix_ = "scwx::qt::ui::serial_port_dialog"; | ||||||
|  | static const auto        logger_    = scwx::util::Logger::Create(logPrefix_); | ||||||
|  | 
 | ||||||
|  | class SerialPortDialog::Impl | ||||||
|  | { | ||||||
|  | public: | ||||||
|  |    struct PortProperties | ||||||
|  |    { | ||||||
|  |       std::string busReportedDeviceDescription_ {}; | ||||||
|  |    }; | ||||||
|  | 
 | ||||||
|  |    struct PortSettings | ||||||
|  |    { | ||||||
|  |       int         baudRate_ {-1}; // Positive
 | ||||||
|  |       std::string parity_ {"n"};  // [e]ven, [o]dd, [m]ark, [s]pace, [n]one
 | ||||||
|  |       int         dataBits_ {8};  // [4-8]
 | ||||||
|  |       float       stopBits_ {1};  // 1, 1.5, 2
 | ||||||
|  |       std::string | ||||||
|  |          flowControl_ {}; // "" (none), "p" (hardware), "x" (Xon / Xoff)
 | ||||||
|  |    }; | ||||||
|  | 
 | ||||||
|  |    typedef std::unordered_map<std::string, QSerialPortInfo> PortInfoMap; | ||||||
|  |    typedef std::unordered_map<std::string, PortProperties>  PortPropertiesMap; | ||||||
|  |    typedef std::unordered_map<std::string, PortSettings>    PortSettingsMap; | ||||||
|  | 
 | ||||||
|  |    explicit Impl(SerialPortDialog* self) : | ||||||
|  |        self_ {self}, | ||||||
|  |        model_ {new QStandardItemModel(self)}, | ||||||
|  |        proxyModel_ {new QSortFilterProxyModel(self)} | ||||||
|  |    { | ||||||
|  |    } | ||||||
|  |    ~Impl() = default; | ||||||
|  | 
 | ||||||
|  |    void LogSerialPortInfo(const QSerialPortInfo& info); | ||||||
|  |    void RefreshSerialDevices(); | ||||||
|  |    void UpdateModel(); | ||||||
|  | 
 | ||||||
|  |    static void ReadComPortProperties(PortPropertiesMap& portPropertiesMap); | ||||||
|  |    static void ReadComPortSettings(PortSettingsMap& portSettingsMap); | ||||||
|  |    static void StorePortSettings(const std::string& portName, | ||||||
|  |                                  const std::string& settingsString, | ||||||
|  |                                  PortSettingsMap&   portSettingsMap); | ||||||
|  | 
 | ||||||
|  | #if defined(_WIN32) | ||||||
|  |    static std::string GetDevicePropertyString(HDEVINFO&        deviceInfoSet, | ||||||
|  |                                               SP_DEVINFO_DATA& deviceInfoData, | ||||||
|  |                                               DEVPROPKEY       propertyKey); | ||||||
|  |    static std::string GetRegistryValueDataString(HKEY hKey, LPCTSTR lpValue); | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  |    SerialPortDialog*      self_; | ||||||
|  |    QStandardItemModel*    model_; | ||||||
|  |    QSortFilterProxyModel* proxyModel_; | ||||||
|  | 
 | ||||||
|  |    std::string selectedSerialPort_ {"?"}; | ||||||
|  | 
 | ||||||
|  |    PortInfoMap       portInfoMap_ {}; | ||||||
|  |    PortPropertiesMap portPropertiesMap_ {}; | ||||||
|  |    PortSettingsMap   portSettingsMap_ {}; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | SerialPortDialog::SerialPortDialog(QWidget* parent) : | ||||||
|  |     QDialog(parent), | ||||||
|  |     p {std::make_unique<Impl>(this)}, | ||||||
|  |     ui(new Ui::SerialPortDialog) | ||||||
|  | { | ||||||
|  |    ui->setupUi(this); | ||||||
|  | 
 | ||||||
|  |    p->proxyModel_->setSourceModel(p->model_); | ||||||
|  |    ui->serialPortView->setModel(p->proxyModel_); | ||||||
|  |    ui->serialPortView->setEditTriggers( | ||||||
|  |       QAbstractItemView::EditTrigger::NoEditTriggers); | ||||||
|  |    ui->serialPortView->sortByColumn(0, Qt::SortOrder::AscendingOrder); | ||||||
|  | 
 | ||||||
|  |    p->RefreshSerialDevices(); | ||||||
|  | 
 | ||||||
|  |    ui->buttonBox->button(QDialogButtonBox::StandardButton::Ok) | ||||||
|  |       ->setEnabled(false); | ||||||
|  | 
 | ||||||
|  |    connect(ui->refreshButton, | ||||||
|  |            &QAbstractButton::clicked, | ||||||
|  |            this, | ||||||
|  |            [this]() { p->RefreshSerialDevices(); }); | ||||||
|  | 
 | ||||||
|  |    connect(ui->serialPortView, | ||||||
|  |            &QTreeView::doubleClicked, | ||||||
|  |            this, | ||||||
|  |            [this]() { Q_EMIT accept(); }); | ||||||
|  |    connect( | ||||||
|  |       ui->serialPortView->selectionModel(), | ||||||
|  |       &QItemSelectionModel::selectionChanged, | ||||||
|  |       this, | ||||||
|  |       [this](const QItemSelection& selected, const QItemSelection& deselected) | ||||||
|  |       { | ||||||
|  |          if (selected.size() == 0 && deselected.size() == 0) | ||||||
|  |          { | ||||||
|  |             // Items which stay selected but change their index are not
 | ||||||
|  |             // included in selected and deselected. Thus, this signal might
 | ||||||
|  |             // be emitted with both selected and deselected empty, if only
 | ||||||
|  |             // the indices of selected items change.
 | ||||||
|  |             return; | ||||||
|  |          } | ||||||
|  | 
 | ||||||
|  |          ui->buttonBox->button(QDialogButtonBox::Ok) | ||||||
|  |             ->setEnabled(selected.size() > 0); | ||||||
|  | 
 | ||||||
|  |          if (selected.size() > 0) | ||||||
|  |          { | ||||||
|  |             QModelIndex selectedIndex = | ||||||
|  |                p->proxyModel_->mapToSource(selected[0].indexes()[0]); | ||||||
|  |             selectedIndex        = p->model_->index(selectedIndex.row(), 0); | ||||||
|  |             QVariant variantData = p->model_->data(selectedIndex); | ||||||
|  |             if (variantData.typeId() == QMetaType::QString) | ||||||
|  |             { | ||||||
|  |                p->selectedSerialPort_ = variantData.toString().toStdString(); | ||||||
|  |             } | ||||||
|  |             else | ||||||
|  |             { | ||||||
|  |                logger_->warn("Unexpected selection data type"); | ||||||
|  |                p->selectedSerialPort_ = std::string {"?"}; | ||||||
|  |             } | ||||||
|  |          } | ||||||
|  |          else | ||||||
|  |          { | ||||||
|  |             p->selectedSerialPort_ = std::string {"?"}; | ||||||
|  |          } | ||||||
|  | 
 | ||||||
|  |          logger_->debug("Selected: {}", p->selectedSerialPort_); | ||||||
|  |       }); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | SerialPortDialog::~SerialPortDialog() | ||||||
|  | { | ||||||
|  |    delete ui; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | std::string SerialPortDialog::serial_port() | ||||||
|  | { | ||||||
|  |    return p->selectedSerialPort_; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int SerialPortDialog::baud_rate() | ||||||
|  | { | ||||||
|  |    int baudRate = -1; | ||||||
|  | 
 | ||||||
|  |    auto it = p->portSettingsMap_.find(p->selectedSerialPort_); | ||||||
|  |    if (it != p->portSettingsMap_.cend()) | ||||||
|  |    { | ||||||
|  |       baudRate = it->second.baudRate_; | ||||||
|  |    } | ||||||
|  | 
 | ||||||
|  |    return baudRate; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void SerialPortDialog::Impl::LogSerialPortInfo(const QSerialPortInfo& info) | ||||||
|  | { | ||||||
|  |    logger_->trace("Serial Port:    {}", info.portName().toStdString()); | ||||||
|  |    logger_->trace("  Description:  {}", info.description().toStdString()); | ||||||
|  |    logger_->trace("  System Loc:   {}", info.systemLocation().toStdString()); | ||||||
|  |    logger_->trace("  Manufacturer: {}", info.manufacturer().toStdString()); | ||||||
|  |    logger_->trace("  Vendor ID:    {}", info.vendorIdentifier()); | ||||||
|  |    logger_->trace("  Product ID:   {}", info.productIdentifier()); | ||||||
|  |    logger_->trace("  Serial No:    {}", info.serialNumber().toStdString()); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void SerialPortDialog::Impl::RefreshSerialDevices() | ||||||
|  | { | ||||||
|  |    QList<QSerialPortInfo> availablePorts = QSerialPortInfo::availablePorts(); | ||||||
|  | 
 | ||||||
|  |    PortInfoMap       newPortInfoMap {}; | ||||||
|  |    PortPropertiesMap newPortPropertiesMap {}; | ||||||
|  |    PortSettingsMap   newPortSettingsMap {}; | ||||||
|  | 
 | ||||||
|  |    for (auto& port : availablePorts) | ||||||
|  |    { | ||||||
|  |       LogSerialPortInfo(port); | ||||||
|  |       newPortInfoMap.insert_or_assign(port.portName().toStdString(), port); | ||||||
|  |    } | ||||||
|  | 
 | ||||||
|  |    ReadComPortProperties(newPortPropertiesMap); | ||||||
|  |    ReadComPortSettings(newPortSettingsMap); | ||||||
|  | 
 | ||||||
|  |    portInfoMap_.swap(newPortInfoMap); | ||||||
|  |    portPropertiesMap_.swap(newPortPropertiesMap); | ||||||
|  |    portSettingsMap_.swap(newPortSettingsMap); | ||||||
|  | 
 | ||||||
|  |    UpdateModel(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void SerialPortDialog::Impl::UpdateModel() | ||||||
|  | { | ||||||
|  | #if defined(_WIN32) | ||||||
|  |    static const QStringList headerLabels { | ||||||
|  |       tr("Port"), tr("Description"), tr("Device")}; | ||||||
|  | #else | ||||||
|  |    static const QStringList headerLabels {tr("Port"), tr("Description")}; | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  |    // Clear existing serial ports
 | ||||||
|  |    model_->clear(); | ||||||
|  | 
 | ||||||
|  |    // Reset selected serial port and disable OK button
 | ||||||
|  |    selectedSerialPort_ = std::string {"?"}; | ||||||
|  |    self_->ui->buttonBox->button(QDialogButtonBox::StandardButton::Ok) | ||||||
|  |       ->setEnabled(false); | ||||||
|  | 
 | ||||||
|  |    // Reset headers
 | ||||||
|  |    model_->setHorizontalHeaderLabels(headerLabels); | ||||||
|  | 
 | ||||||
|  |    QStandardItem* root = model_->invisibleRootItem(); | ||||||
|  | 
 | ||||||
|  |    for (auto& port : portInfoMap_) | ||||||
|  |    { | ||||||
|  |       const QString portName    = port.second.portName(); | ||||||
|  |       const QString description = port.second.description(); | ||||||
|  | 
 | ||||||
|  | #if defined(_WIN32) | ||||||
|  |       QString device {}; | ||||||
|  | 
 | ||||||
|  |       auto portPropertiesIt = portPropertiesMap_.find(port.first); | ||||||
|  |       if (portPropertiesIt != portPropertiesMap_.cend()) | ||||||
|  |       { | ||||||
|  |          device = QString::fromStdString( | ||||||
|  |             portPropertiesIt->second.busReportedDeviceDescription_); | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       root->appendRow({new QStandardItem(portName), | ||||||
|  |                        new QStandardItem(description), | ||||||
|  |                        new QStandardItem(device)}); | ||||||
|  | #else | ||||||
|  |       root->appendRow( | ||||||
|  |          {new QStandardItem(portName), new QStandardItem(description)}); | ||||||
|  | #endif | ||||||
|  |    } | ||||||
|  | 
 | ||||||
|  |    for (int column = 0; column < model_->columnCount(); column++) | ||||||
|  |    { | ||||||
|  |       self_->ui->serialPortView->resizeColumnToContents(column); | ||||||
|  |    } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void SerialPortDialog::Impl::ReadComPortProperties( | ||||||
|  |    [[maybe_unused]] PortPropertiesMap& portPropertiesMap) | ||||||
|  | { | ||||||
|  | #if defined(_WIN32) | ||||||
|  |    GUID     classGuid  = GUID_DEVCLASS_PORTS; | ||||||
|  |    PCWSTR   enumerator = nullptr; | ||||||
|  |    HWND     hwndParent = nullptr; | ||||||
|  |    DWORD    flags      = DIGCF_PRESENT; | ||||||
|  |    HDEVINFO deviceInfoSet; | ||||||
|  | 
 | ||||||
|  |    // Retrieve COM port devices
 | ||||||
|  |    deviceInfoSet = | ||||||
|  |       SetupDiGetClassDevs(&classGuid, enumerator, hwndParent, flags); | ||||||
|  |    if (deviceInfoSet == INVALID_HANDLE_VALUE) | ||||||
|  |    { | ||||||
|  |       logger_->error("Error getting COM port devices"); | ||||||
|  |       return; | ||||||
|  |    } | ||||||
|  | 
 | ||||||
|  |    DWORD           memberIndex = 0; | ||||||
|  |    SP_DEVINFO_DATA deviceInfoData {}; | ||||||
|  |    deviceInfoData.cbSize = sizeof(deviceInfoData); | ||||||
|  |    flags                 = 0; | ||||||
|  | 
 | ||||||
|  |    // For each COM port device
 | ||||||
|  |    while (SetupDiEnumDeviceInfo(deviceInfoSet, memberIndex++, &deviceInfoData)) | ||||||
|  |    { | ||||||
|  |       DWORD  scope      = DICS_FLAG_GLOBAL; | ||||||
|  |       DWORD  hwProfile  = 0; | ||||||
|  |       DWORD  keyType    = DIREG_DEV; | ||||||
|  |       REGSAM samDesired = KEY_READ; | ||||||
|  |       HKEY   devRegKey  = SetupDiOpenDevRegKey( | ||||||
|  |          deviceInfoSet, &deviceInfoData, scope, hwProfile, keyType, samDesired); | ||||||
|  | 
 | ||||||
|  |       if (devRegKey == INVALID_HANDLE_VALUE) | ||||||
|  |       { | ||||||
|  |          logger_->error("Unable to open device registry key: {}", | ||||||
|  |                         GetLastError()); | ||||||
|  |          continue; | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       // Read Port Name and Device Description
 | ||||||
|  |       std::string portName = | ||||||
|  |          GetRegistryValueDataString(devRegKey, TEXT("PortName")); | ||||||
|  | 
 | ||||||
|  |       if (portName.empty()) | ||||||
|  |       { | ||||||
|  |          // Ignore device without port name
 | ||||||
|  |          continue; | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       PortProperties properties {}; | ||||||
|  |       properties.busReportedDeviceDescription_ = GetDevicePropertyString( | ||||||
|  |          deviceInfoSet, deviceInfoData, DEVPKEY_Device_BusReportedDeviceDesc); | ||||||
|  | 
 | ||||||
|  |       logger_->trace( | ||||||
|  |          "Port: {} ({})", portName, properties.busReportedDeviceDescription_); | ||||||
|  | 
 | ||||||
|  |       portPropertiesMap.emplace(portName, std::move(properties)); | ||||||
|  | 
 | ||||||
|  |       RegCloseKey(devRegKey); | ||||||
|  |    } | ||||||
|  | #endif | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void SerialPortDialog::Impl::ReadComPortSettings( | ||||||
|  |    [[maybe_unused]] PortSettingsMap& portSettingsMap) | ||||||
|  | { | ||||||
|  | #if defined(_WIN32) | ||||||
|  |    const LPCTSTR lpSubKey = | ||||||
|  |       TEXT("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Ports"); | ||||||
|  |    DWORD   ulOptions  = 0; | ||||||
|  |    REGSAM  samDesired = KEY_READ; | ||||||
|  |    HKEY    hkResult; | ||||||
|  |    LSTATUS status; | ||||||
|  | 
 | ||||||
|  |    // Open Port Settings Key
 | ||||||
|  |    status = RegOpenKeyEx( | ||||||
|  |       HKEY_LOCAL_MACHINE, lpSubKey, ulOptions, samDesired, &hkResult); | ||||||
|  | 
 | ||||||
|  |    if (status == ERROR_SUCCESS) | ||||||
|  |    { | ||||||
|  |       DWORD   dwIndex = 0; | ||||||
|  |       TCHAR   valueName[MAX_PATH]; | ||||||
|  |       LPDWORD lpReserved = nullptr; | ||||||
|  |       DWORD   type; | ||||||
|  |       TCHAR   valueData[64]; | ||||||
|  |       char    buffer[MAX_PATH]; // Buffer for string conversion
 | ||||||
|  | 
 | ||||||
|  |       // Number of characters, not including terminating null
 | ||||||
|  |       static constexpr DWORD maxValueNameSize = | ||||||
|  |          sizeof(valueName) / sizeof(TCHAR) - 1; | ||||||
|  |       DWORD valueNameSize = maxValueNameSize; | ||||||
|  | 
 | ||||||
|  |       // Number of bytes
 | ||||||
|  |       DWORD valueDataSize = sizeof(valueData); | ||||||
|  | 
 | ||||||
|  |       static constexpr std::size_t bufferSize = sizeof(buffer); | ||||||
|  | 
 | ||||||
|  |       // Enumerate each port value
 | ||||||
|  |       while ((status = RegEnumValue(hkResult, | ||||||
|  |                                     dwIndex++, | ||||||
|  |                                     valueName, | ||||||
|  |                                     &valueNameSize, | ||||||
|  |                                     lpReserved, | ||||||
|  |                                     &type, | ||||||
|  |                                     reinterpret_cast<LPBYTE>(&valueData), | ||||||
|  |                                     &valueDataSize)) == ERROR_SUCCESS || | ||||||
|  |              status == ERROR_MORE_DATA) | ||||||
|  |       { | ||||||
|  |          // Validate port value
 | ||||||
|  |          if (status == ERROR_SUCCESS &&            //
 | ||||||
|  |              type == REG_SZ &&                     //
 | ||||||
|  |              valueNameSize >= 5 &&                 // COM#:
 | ||||||
|  |              valueNameSize < sizeof(buffer) - 1 && // Strip off :
 | ||||||
|  |              valueDataSize > sizeof(TCHAR) &&      // Null character
 | ||||||
|  |              _tcsncmp(valueName, TEXT("COM"), 3) == 0) | ||||||
|  |          { | ||||||
|  |             errno_t     error; | ||||||
|  |             std::size_t returnValue; | ||||||
|  | 
 | ||||||
|  |             // Get port name
 | ||||||
|  |             if ((error = wcstombs_s(&returnValue, | ||||||
|  |                                     buffer, | ||||||
|  |                                     sizeof(buffer), | ||||||
|  |                                     valueName, | ||||||
|  |                                     valueNameSize - 1)) != 0) | ||||||
|  |             { | ||||||
|  |                logger_->error( | ||||||
|  |                   "Error converting registry value name to string: {}", | ||||||
|  |                   returnValue); | ||||||
|  |                continue; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             std::string portName = buffer; | ||||||
|  | 
 | ||||||
|  |             // Get port data
 | ||||||
|  |             if ((error = wcstombs_s(&returnValue, | ||||||
|  |                                     buffer, | ||||||
|  |                                     sizeof(buffer), | ||||||
|  |                                     valueData, | ||||||
|  |                                     sizeof(buffer) - 1)) != 0) | ||||||
|  |             { | ||||||
|  |                logger_->error( | ||||||
|  |                   "Error converting registry value data to string: {}", | ||||||
|  |                   returnValue); | ||||||
|  |                continue; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             std::string portData = buffer; | ||||||
|  | 
 | ||||||
|  |             logger_->trace("Port Settings: {} ({})", portName, portData); | ||||||
|  | 
 | ||||||
|  |             StorePortSettings(portName, portData, portSettingsMap); | ||||||
|  |          } | ||||||
|  | 
 | ||||||
|  |          valueNameSize = maxValueNameSize; | ||||||
|  |          valueDataSize = sizeof(valueData); | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       RegCloseKey(hkResult); | ||||||
|  |    } | ||||||
|  |    else | ||||||
|  |    { | ||||||
|  |       logger_->error("Could not open COM port settings registry key: {}", | ||||||
|  |                      status); | ||||||
|  |    } | ||||||
|  | #endif | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void SerialPortDialog::Impl::StorePortSettings( | ||||||
|  |    const std::string& portName, | ||||||
|  |    const std::string& settingsString, | ||||||
|  |    PortSettingsMap&   portSettingsMap) | ||||||
|  | { | ||||||
|  |    PortSettings portSettings {}; | ||||||
|  | 
 | ||||||
|  |    std::vector<std::string> tokenList = | ||||||
|  |       util::ParseTokens(settingsString, {",", ",", ",", ",", ","}); | ||||||
|  | 
 | ||||||
|  |    try | ||||||
|  |    { | ||||||
|  |       if (tokenList.size() >= 1) | ||||||
|  |       { | ||||||
|  |          // Positive integer
 | ||||||
|  |          portSettings.baudRate_ = std::stoi(tokenList.at(0)); | ||||||
|  |       } | ||||||
|  |       if (tokenList.size() >= 2) | ||||||
|  |       { | ||||||
|  |          // [e]ven, [o]dd, [m]ark, [s]pace, [n]one
 | ||||||
|  |          portSettings.parity_ = tokenList.at(1); | ||||||
|  |       } | ||||||
|  |       if (tokenList.size() >= 3) | ||||||
|  |       { | ||||||
|  |          // [4-8]
 | ||||||
|  |          portSettings.dataBits_ = std::stoi(tokenList.at(2)); | ||||||
|  |       } | ||||||
|  |       if (tokenList.size() >= 4) | ||||||
|  |       { | ||||||
|  |          // 1, 1.5, 2
 | ||||||
|  |          portSettings.stopBits_ = std::stof(tokenList.at(3)); | ||||||
|  |       } | ||||||
|  |       if (tokenList.size() >= 5) | ||||||
|  |       { | ||||||
|  |          // "" (none), "p" (hardware), "x" (Xon / Xoff)
 | ||||||
|  |          portSettings.flowControl_ = tokenList.at(4); | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       portSettingsMap.emplace(portName, std::move(portSettings)); | ||||||
|  |    } | ||||||
|  |    catch (const std::exception&) | ||||||
|  |    { | ||||||
|  |       logger_->error( | ||||||
|  |          "Could not parse {} port settings: {}", portName, settingsString); | ||||||
|  |    } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #if defined(_WIN32) | ||||||
|  | std::string | ||||||
|  | SerialPortDialog::Impl::GetDevicePropertyString(HDEVINFO&        deviceInfoSet, | ||||||
|  |                                                 SP_DEVINFO_DATA& deviceInfoData, | ||||||
|  |                                                 DEVPROPKEY       propertyKey) | ||||||
|  | { | ||||||
|  |    std::string devicePropertyString {}; | ||||||
|  | 
 | ||||||
|  |    DEVPROPTYPE        propertyType = 0; | ||||||
|  |    std::vector<TCHAR> propertyBuffer {}; | ||||||
|  |    DWORD              requiredSize = 0; | ||||||
|  |    DWORD              flags        = 0; | ||||||
|  | 
 | ||||||
|  |    BOOL status = SetupDiGetDeviceProperty(deviceInfoSet, | ||||||
|  |                                           &deviceInfoData, | ||||||
|  |                                           &propertyKey, | ||||||
|  |                                           &propertyType, | ||||||
|  |                                           nullptr, | ||||||
|  |                                           0, | ||||||
|  |                                           &requiredSize, | ||||||
|  |                                           flags); | ||||||
|  | 
 | ||||||
|  |    if (requiredSize > 0) | ||||||
|  |    { | ||||||
|  |       propertyBuffer.reserve(requiredSize / sizeof(TCHAR)); | ||||||
|  | 
 | ||||||
|  |       status = SetupDiGetDeviceProperty( | ||||||
|  |          deviceInfoSet, | ||||||
|  |          &deviceInfoData, | ||||||
|  |          &propertyKey, | ||||||
|  |          &propertyType, | ||||||
|  |          reinterpret_cast<PBYTE>(propertyBuffer.data()), | ||||||
|  |          static_cast<DWORD>(propertyBuffer.capacity() * sizeof(TCHAR)), | ||||||
|  |          &requiredSize, | ||||||
|  |          flags); | ||||||
|  |    } | ||||||
|  | 
 | ||||||
|  |    if (status && requiredSize > 0) | ||||||
|  |    { | ||||||
|  |       errno_t     error; | ||||||
|  |       std::size_t returnValue; | ||||||
|  | 
 | ||||||
|  |       devicePropertyString.resize(requiredSize / sizeof(TCHAR)); | ||||||
|  | 
 | ||||||
|  |       if ((error = wcstombs_s(&returnValue, | ||||||
|  |                               devicePropertyString.data(), | ||||||
|  |                               devicePropertyString.size(), | ||||||
|  |                               propertyBuffer.data(), | ||||||
|  |                               _TRUNCATE)) != 0) | ||||||
|  |       { | ||||||
|  |          logger_->error("Error converting device property string: {}", | ||||||
|  |                         returnValue); | ||||||
|  |          devicePropertyString.clear(); | ||||||
|  |       } | ||||||
|  |       else if (!devicePropertyString.empty()) | ||||||
|  |       { | ||||||
|  |          // Remove trailing null
 | ||||||
|  |          devicePropertyString.erase(devicePropertyString.size() - 1); | ||||||
|  |       } | ||||||
|  |    } | ||||||
|  | 
 | ||||||
|  |    return devicePropertyString; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | std::string SerialPortDialog::Impl::GetRegistryValueDataString(HKEY    hKey, | ||||||
|  |                                                                LPCTSTR lpValue) | ||||||
|  | { | ||||||
|  |    std::string dataString {}; | ||||||
|  | 
 | ||||||
|  |    LPCTSTR lpSubKey = nullptr; | ||||||
|  |    DWORD   dwFlags  = RRF_RT_REG_SZ; // Restrict type to REG_SZ
 | ||||||
|  |    DWORD   dwType; | ||||||
|  | 
 | ||||||
|  |    std::vector<TCHAR> dataBuffer {}; | ||||||
|  |    DWORD              dataBufferSize = 0; | ||||||
|  | 
 | ||||||
|  |    LSTATUS status = RegGetValue( | ||||||
|  |       hKey, lpSubKey, lpValue, dwFlags, &dwType, nullptr, &dataBufferSize); | ||||||
|  | 
 | ||||||
|  |    if (status == ERROR_SUCCESS && dataBufferSize > 0) | ||||||
|  |    { | ||||||
|  |       dataBuffer.reserve(dataBufferSize / sizeof(TCHAR)); | ||||||
|  | 
 | ||||||
|  |       status = RegGetValue(hKey, | ||||||
|  |                            lpSubKey, | ||||||
|  |                            lpValue, | ||||||
|  |                            dwFlags, | ||||||
|  |                            &dwType, | ||||||
|  |                            reinterpret_cast<PVOID>(dataBuffer.data()), | ||||||
|  |                            &dataBufferSize); | ||||||
|  |    } | ||||||
|  | 
 | ||||||
|  |    if (status == ERROR_SUCCESS && dataBufferSize > 0) | ||||||
|  |    { | ||||||
|  |       errno_t     error; | ||||||
|  |       std::size_t returnValue; | ||||||
|  | 
 | ||||||
|  |       dataString.resize(dataBufferSize / sizeof(TCHAR)); | ||||||
|  | 
 | ||||||
|  |       if ((error = wcstombs_s(&returnValue, | ||||||
|  |                               dataString.data(), | ||||||
|  |                               dataString.size(), | ||||||
|  |                               dataBuffer.data(), | ||||||
|  |                               _TRUNCATE)) != 0) | ||||||
|  |       { | ||||||
|  |          logger_->error("Error converting registry value data string: {}", | ||||||
|  |                         returnValue); | ||||||
|  |          dataString.clear(); | ||||||
|  |       } | ||||||
|  |       else if (!dataString.empty()) | ||||||
|  |       { | ||||||
|  |          // Remove trailing null
 | ||||||
|  |          dataString.erase(dataString.size() - 1); | ||||||
|  |       } | ||||||
|  |    } | ||||||
|  | 
 | ||||||
|  |    return dataString; | ||||||
|  | } | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | } // namespace ui
 | ||||||
|  | } // namespace qt
 | ||||||
|  | } // namespace scwx
 | ||||||
							
								
								
									
										36
									
								
								scwx-qt/source/scwx/qt/ui/serial_port_dialog.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								scwx-qt/source/scwx/qt/ui/serial_port_dialog.hpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,36 @@ | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <QDialog> | ||||||
|  | 
 | ||||||
|  | namespace Ui | ||||||
|  | { | ||||||
|  | class SerialPortDialog; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | namespace scwx | ||||||
|  | { | ||||||
|  | namespace qt | ||||||
|  | { | ||||||
|  | namespace ui | ||||||
|  | { | ||||||
|  | class SerialPortDialog : public QDialog | ||||||
|  | { | ||||||
|  |    Q_OBJECT | ||||||
|  |    Q_DISABLE_COPY_MOVE(SerialPortDialog) | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  |    explicit SerialPortDialog(QWidget* parent = nullptr); | ||||||
|  |    ~SerialPortDialog(); | ||||||
|  | 
 | ||||||
|  |    std::string serial_port(); | ||||||
|  |    int         baud_rate(); | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |    class Impl; | ||||||
|  |    std::unique_ptr<Impl> p; | ||||||
|  |    Ui::SerialPortDialog* ui; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | } // namespace ui
 | ||||||
|  | } // namespace qt
 | ||||||
|  | } // namespace scwx
 | ||||||
							
								
								
									
										102
									
								
								scwx-qt/source/scwx/qt/ui/serial_port_dialog.ui
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										102
									
								
								scwx-qt/source/scwx/qt/ui/serial_port_dialog.ui
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,102 @@ | ||||||
|  | <?xml version="1.0" encoding="UTF-8"?> | ||||||
|  | <ui version="4.0"> | ||||||
|  |  <class>SerialPortDialog</class> | ||||||
|  |  <widget class="QDialog" name="SerialPortDialog"> | ||||||
|  |   <property name="geometry"> | ||||||
|  |    <rect> | ||||||
|  |     <x>0</x> | ||||||
|  |     <y>0</y> | ||||||
|  |     <width>400</width> | ||||||
|  |     <height>300</height> | ||||||
|  |    </rect> | ||||||
|  |   </property> | ||||||
|  |   <property name="windowTitle"> | ||||||
|  |    <string>Select Serial Port</string> | ||||||
|  |   </property> | ||||||
|  |   <layout class="QVBoxLayout" name="verticalLayout"> | ||||||
|  |    <item> | ||||||
|  |     <widget class="QTreeView" name="serialPortView"> | ||||||
|  |      <property name="alternatingRowColors"> | ||||||
|  |       <bool>true</bool> | ||||||
|  |      </property> | ||||||
|  |      <property name="indentation"> | ||||||
|  |       <number>0</number> | ||||||
|  |      </property> | ||||||
|  |      <property name="sortingEnabled"> | ||||||
|  |       <bool>true</bool> | ||||||
|  |      </property> | ||||||
|  |     </widget> | ||||||
|  |    </item> | ||||||
|  |    <item> | ||||||
|  |     <widget class="QWidget" name="widget" native="true"> | ||||||
|  |      <layout class="QHBoxLayout" name="horizontalLayout"> | ||||||
|  |       <property name="leftMargin"> | ||||||
|  |        <number>0</number> | ||||||
|  |       </property> | ||||||
|  |       <property name="topMargin"> | ||||||
|  |        <number>0</number> | ||||||
|  |       </property> | ||||||
|  |       <property name="rightMargin"> | ||||||
|  |        <number>0</number> | ||||||
|  |       </property> | ||||||
|  |       <property name="bottomMargin"> | ||||||
|  |        <number>0</number> | ||||||
|  |       </property> | ||||||
|  |       <item> | ||||||
|  |        <widget class="QPushButton" name="refreshButton"> | ||||||
|  |         <property name="text"> | ||||||
|  |          <string>&Refresh</string> | ||||||
|  |         </property> | ||||||
|  |        </widget> | ||||||
|  |       </item> | ||||||
|  |       <item> | ||||||
|  |        <widget class="QDialogButtonBox" name="buttonBox"> | ||||||
|  |         <property name="orientation"> | ||||||
|  |          <enum>Qt::Orientation::Horizontal</enum> | ||||||
|  |         </property> | ||||||
|  |         <property name="standardButtons"> | ||||||
|  |          <set>QDialogButtonBox::StandardButton::Cancel|QDialogButtonBox::StandardButton::Ok</set> | ||||||
|  |         </property> | ||||||
|  |        </widget> | ||||||
|  |       </item> | ||||||
|  |      </layout> | ||||||
|  |     </widget> | ||||||
|  |    </item> | ||||||
|  |   </layout> | ||||||
|  |  </widget> | ||||||
|  |  <resources/> | ||||||
|  |  <connections> | ||||||
|  |   <connection> | ||||||
|  |    <sender>buttonBox</sender> | ||||||
|  |    <signal>accepted()</signal> | ||||||
|  |    <receiver>SerialPortDialog</receiver> | ||||||
|  |    <slot>accept()</slot> | ||||||
|  |    <hints> | ||||||
|  |     <hint type="sourcelabel"> | ||||||
|  |      <x>248</x> | ||||||
|  |      <y>254</y> | ||||||
|  |     </hint> | ||||||
|  |     <hint type="destinationlabel"> | ||||||
|  |      <x>157</x> | ||||||
|  |      <y>274</y> | ||||||
|  |     </hint> | ||||||
|  |    </hints> | ||||||
|  |   </connection> | ||||||
|  |   <connection> | ||||||
|  |    <sender>buttonBox</sender> | ||||||
|  |    <signal>rejected()</signal> | ||||||
|  |    <receiver>SerialPortDialog</receiver> | ||||||
|  |    <slot>reject()</slot> | ||||||
|  |    <hints> | ||||||
|  |     <hint type="sourcelabel"> | ||||||
|  |      <x>316</x> | ||||||
|  |      <y>260</y> | ||||||
|  |     </hint> | ||||||
|  |     <hint type="destinationlabel"> | ||||||
|  |      <x>286</x> | ||||||
|  |      <y>274</y> | ||||||
|  |     </hint> | ||||||
|  |    </hints> | ||||||
|  |   </connection> | ||||||
|  |  </connections> | ||||||
|  | </ui> | ||||||
|  | @ -4,6 +4,41 @@ | ||||||
| 
 | 
 | ||||||
| #include <QWidget> | #include <QWidget> | ||||||
| 
 | 
 | ||||||
|  | #define SCWX_ENUM_MAP_FROM_VALUE(Iterator, ToName)                             \ | ||||||
|  |    [](const std::string& text) -> std::string                                  \ | ||||||
|  |    {                                                                           \ | ||||||
|  |       for (auto enumValue : Iterator)                                          \ | ||||||
|  |       {                                                                        \ | ||||||
|  |          const std::string enumName = ToName(enumValue);                       \ | ||||||
|  |                                                                                \ | ||||||
|  |          if (boost::iequals(text, enumName))                                   \ | ||||||
|  |          {                                                                     \ | ||||||
|  |             /* Return label */                                                 \ | ||||||
|  |             return enumName;                                                   \ | ||||||
|  |          }                                                                     \ | ||||||
|  |       }                                                                        \ | ||||||
|  |                                                                                \ | ||||||
|  |       /* Label not found, return unknown */                                    \ | ||||||
|  |       return "?";                                                              \ | ||||||
|  |    } | ||||||
|  | 
 | ||||||
|  | #define SCWX_SETTINGS_COMBO_BOX(settingsInterface, comboBox, Iterator, ToName) \ | ||||||
|  |    for (const auto& enumValue : Iterator)                                      \ | ||||||
|  |    {                                                                           \ | ||||||
|  |       comboBox->addItem(QString::fromStdString(ToName(enumValue)));            \ | ||||||
|  |    }                                                                           \ | ||||||
|  |                                                                                \ | ||||||
|  |    settingsInterface.SetMapFromValueFunction(                                  \ | ||||||
|  |       SCWX_ENUM_MAP_FROM_VALUE(Iterator, ToName));                             \ | ||||||
|  |    settingsInterface.SetMapToValueFunction(                                    \ | ||||||
|  |       [](std::string text) -> std::string                                      \ | ||||||
|  |       {                                                                        \ | ||||||
|  |          boost::to_lower(text);                                                \ | ||||||
|  |          return text;                                                          \ | ||||||
|  |       });                                                                      \ | ||||||
|  |                                                                                \ | ||||||
|  |    settingsInterface.SetEditWidget(comboBox); | ||||||
|  | 
 | ||||||
| namespace scwx | namespace scwx | ||||||
| { | { | ||||||
| namespace qt | namespace qt | ||||||
|  |  | ||||||
|  | @ -12,36 +12,6 @@ | ||||||
| #include <QToolButton> | #include <QToolButton> | ||||||
| #include <QVBoxLayout> | #include <QVBoxLayout> | ||||||
| 
 | 
 | ||||||
| #define SCWX_SETTINGS_COMBO_BOX(settingsInterface, comboBox, Iterator, ToName) \ |  | ||||||
|    for (const auto& enumValue : Iterator)                                      \ |  | ||||||
|    {                                                                           \ |  | ||||||
|       comboBox->addItem(QString::fromStdString(ToName(enumValue)));            \ |  | ||||||
|    }                                                                           \ |  | ||||||
|                                                                                \ |  | ||||||
|    settingsInterface.SetMapFromValueFunction(                                  \ |  | ||||||
|       [](const std::string& text) -> std::string                               \ |  | ||||||
|       {                                                                        \ |  | ||||||
|          for (const auto& enumValue : Iterator)                                \ |  | ||||||
|          {                                                                     \ |  | ||||||
|             const std::string valueName = ToName(enumValue);                   \ |  | ||||||
|                                                                                \ |  | ||||||
|             if (boost::iequals(text, valueName))                               \ |  | ||||||
|             {                                                                  \ |  | ||||||
|                return valueName;                                               \ |  | ||||||
|             }                                                                  \ |  | ||||||
|          }                                                                     \ |  | ||||||
|                                                                                \ |  | ||||||
|          return "?";                                                           \ |  | ||||||
|       });                                                                      \ |  | ||||||
|    settingsInterface.SetMapToValueFunction(                                    \ |  | ||||||
|       [](std::string text) -> std::string                                      \ |  | ||||||
|       {                                                                        \ |  | ||||||
|          boost::to_lower(text);                                                \ |  | ||||||
|          return text;                                                          \ |  | ||||||
|       });                                                                      \ |  | ||||||
|                                                                                \ |  | ||||||
|    settingsInterface.SetEditWidget(comboBox); |  | ||||||
| 
 |  | ||||||
| namespace scwx | namespace scwx | ||||||
| { | { | ||||||
| namespace qt | namespace qt | ||||||
|  |  | ||||||
|  | @ -22,6 +22,7 @@ | ||||||
| #include <scwx/qt/types/time_types.hpp> | #include <scwx/qt/types/time_types.hpp> | ||||||
| #include <scwx/qt/ui/county_dialog.hpp> | #include <scwx/qt/ui/county_dialog.hpp> | ||||||
| #include <scwx/qt/ui/radar_site_dialog.hpp> | #include <scwx/qt/ui/radar_site_dialog.hpp> | ||||||
|  | #include <scwx/qt/ui/serial_port_dialog.hpp> | ||||||
| #include <scwx/qt/ui/settings/hotkey_settings_widget.hpp> | #include <scwx/qt/ui/settings/hotkey_settings_widget.hpp> | ||||||
| #include <scwx/qt/ui/settings/unit_settings_widget.hpp> | #include <scwx/qt/ui/settings/unit_settings_widget.hpp> | ||||||
| #include <scwx/qt/util/color.hpp> | #include <scwx/qt/util/color.hpp> | ||||||
|  | @ -96,30 +97,13 @@ static const std::unordered_map<std::string, ColorTableConversions> | ||||||
|                             {"VIL", {0u, 255u, 1.0f, 2.5f}}, |                             {"VIL", {0u, 255u, 1.0f, 2.5f}}, | ||||||
|                             {"???", {0u, 15u, 0.0f, 1.0f}}}; |                             {"???", {0u, 15u, 0.0f, 1.0f}}}; | ||||||
| 
 | 
 | ||||||
| #define SCWX_ENUM_MAP_FROM_VALUE(Type, Iterator, ToName)                       \ |  | ||||||
|    [](const std::string& text) -> std::string                                  \ |  | ||||||
|    {                                                                           \ |  | ||||||
|       for (Type enumValue : Iterator)                                          \ |  | ||||||
|       {                                                                        \ |  | ||||||
|          const std::string enumName = ToName(enumValue);                       \ |  | ||||||
|                                                                                \ |  | ||||||
|          if (boost::iequals(text, enumName))                                   \ |  | ||||||
|          {                                                                     \ |  | ||||||
|             /* Return label */                                                 \ |  | ||||||
|             return enumName;                                                   \ |  | ||||||
|          }                                                                     \ |  | ||||||
|       }                                                                        \ |  | ||||||
|                                                                                \ |  | ||||||
|       /* Label not found, return unknown */                                    \ |  | ||||||
|       return "?";                                                              \ |  | ||||||
|    } |  | ||||||
| 
 |  | ||||||
| class SettingsDialogImpl | class SettingsDialogImpl | ||||||
| { | { | ||||||
| public: | public: | ||||||
|    explicit SettingsDialogImpl(SettingsDialog* self) : |    explicit SettingsDialogImpl(SettingsDialog* self) : | ||||||
|        self_ {self}, |        self_ {self}, | ||||||
|        radarSiteDialog_ {new RadarSiteDialog(self)}, |        radarSiteDialog_ {new RadarSiteDialog(self)}, | ||||||
|  |        gpsSourceDialog_ {new SerialPortDialog(self)}, | ||||||
|        countyDialog_ {new CountyDialog(self)}, |        countyDialog_ {new CountyDialog(self)}, | ||||||
|        fontDialog_ {new QFontDialog(self)}, |        fontDialog_ {new QFontDialog(self)}, | ||||||
|        fontCategoryModel_ {new QStandardItemModel(self)}, |        fontCategoryModel_ {new QStandardItemModel(self)}, | ||||||
|  | @ -134,6 +118,9 @@ public: | ||||||
|           &defaultAlertAction_, |           &defaultAlertAction_, | ||||||
|           &clockFormat_, |           &clockFormat_, | ||||||
|           &defaultTimeZone_, |           &defaultTimeZone_, | ||||||
|  |           &positioningPlugin_, | ||||||
|  |           &nmeaBaudRate_, | ||||||
|  |           &nmeaSource_, | ||||||
|           &warningsProvider_, |           &warningsProvider_, | ||||||
|           &antiAliasingEnabled_, |           &antiAliasingEnabled_, | ||||||
|           &showMapAttribution_, |           &showMapAttribution_, | ||||||
|  | @ -210,6 +197,7 @@ public: | ||||||
| 
 | 
 | ||||||
|    SettingsDialog*   self_; |    SettingsDialog*   self_; | ||||||
|    RadarSiteDialog*  radarSiteDialog_; |    RadarSiteDialog*  radarSiteDialog_; | ||||||
|  |    SerialPortDialog* gpsSourceDialog_; | ||||||
|    CountyDialog*     countyDialog_; |    CountyDialog*     countyDialog_; | ||||||
|    QFontDialog*      fontDialog_; |    QFontDialog*      fontDialog_; | ||||||
| 
 | 
 | ||||||
|  | @ -235,6 +223,9 @@ public: | ||||||
|    settings::SettingsInterface<std::string>  defaultAlertAction_ {}; |    settings::SettingsInterface<std::string>  defaultAlertAction_ {}; | ||||||
|    settings::SettingsInterface<std::string>  clockFormat_ {}; |    settings::SettingsInterface<std::string>  clockFormat_ {}; | ||||||
|    settings::SettingsInterface<std::string>  defaultTimeZone_ {}; |    settings::SettingsInterface<std::string>  defaultTimeZone_ {}; | ||||||
|  |    settings::SettingsInterface<std::string>  positioningPlugin_ {}; | ||||||
|  |    settings::SettingsInterface<std::int64_t> nmeaBaudRate_ {}; | ||||||
|  |    settings::SettingsInterface<std::string>  nmeaSource_ {}; | ||||||
|    settings::SettingsInterface<std::string>  theme_ {}; |    settings::SettingsInterface<std::string>  theme_ {}; | ||||||
|    settings::SettingsInterface<std::string>  warningsProvider_ {}; |    settings::SettingsInterface<std::string>  warningsProvider_ {}; | ||||||
|    settings::SettingsInterface<bool>         antiAliasingEnabled_ {}; |    settings::SettingsInterface<bool>         antiAliasingEnabled_ {}; | ||||||
|  | @ -344,6 +335,32 @@ void SettingsDialogImpl::ConnectSignals() | ||||||
|                        } |                        } | ||||||
|                     }); |                     }); | ||||||
| 
 | 
 | ||||||
|  |    QObject::connect(self_->ui->gpsSourceSelectButton, | ||||||
|  |                     &QAbstractButton::clicked, | ||||||
|  |                     self_, | ||||||
|  |                     [this]() { gpsSourceDialog_->show(); }); | ||||||
|  | 
 | ||||||
|  |    QObject::connect(gpsSourceDialog_, | ||||||
|  |                     &SerialPortDialog::accepted, | ||||||
|  |                     self_, | ||||||
|  |                     [this]() | ||||||
|  |                     { | ||||||
|  |                        std::string serialPort = gpsSourceDialog_->serial_port(); | ||||||
|  |                        int         baudRate   = gpsSourceDialog_->baud_rate(); | ||||||
|  | 
 | ||||||
|  |                        if (!serialPort.empty() && serialPort != "?") | ||||||
|  |                        { | ||||||
|  |                           std::string source = | ||||||
|  |                              fmt::format("serial:{}", serialPort); | ||||||
|  |                           nmeaSource_.StageValue(source); | ||||||
|  |                        } | ||||||
|  | 
 | ||||||
|  |                        if (baudRate > 0) | ||||||
|  |                        { | ||||||
|  |                           self_->ui->nmeaBaudRateSpinBox->setValue(baudRate); | ||||||
|  |                        } | ||||||
|  |                     }); | ||||||
|  | 
 | ||||||
|    // Update the Radar Site dialog "map" location with the currently selected
 |    // Update the Radar Site dialog "map" location with the currently selected
 | ||||||
|    // radar site
 |    // radar site
 | ||||||
|    auto& defaultRadarSite = *defaultRadarSite_.GetSettingsVariable(); |    auto& defaultRadarSite = *defaultRadarSite_.GetSettingsVariable(); | ||||||
|  | @ -467,38 +484,11 @@ void SettingsDialogImpl::SetupGeneralTab() | ||||||
|    settings::GeneralSettings& generalSettings = |    settings::GeneralSettings& generalSettings = | ||||||
|       settings::GeneralSettings::Instance(); |       settings::GeneralSettings::Instance(); | ||||||
| 
 | 
 | ||||||
|    for (const auto& uiStyle : types::UiStyleIterator()) |  | ||||||
|    { |  | ||||||
|       self_->ui->themeComboBox->addItem( |  | ||||||
|          QString::fromStdString(types::GetUiStyleName(uiStyle))); |  | ||||||
|    } |  | ||||||
| 
 |  | ||||||
|    theme_.SetSettingsVariable(generalSettings.theme()); |    theme_.SetSettingsVariable(generalSettings.theme()); | ||||||
|    theme_.SetMapFromValueFunction( |    SCWX_SETTINGS_COMBO_BOX(theme_, | ||||||
|       [](const std::string& text) -> std::string |                            self_->ui->themeComboBox, | ||||||
|       { |                            types::UiStyleIterator(), | ||||||
|          for (types::UiStyle uiStyle : types::UiStyleIterator()) |                            types::GetUiStyleName); | ||||||
|          { |  | ||||||
|             const std::string uiStyleName = types::GetUiStyleName(uiStyle); |  | ||||||
| 
 |  | ||||||
|             if (boost::iequals(text, uiStyleName)) |  | ||||||
|             { |  | ||||||
|                // Return UI style label
 |  | ||||||
|                return uiStyleName; |  | ||||||
|             } |  | ||||||
|          } |  | ||||||
| 
 |  | ||||||
|          // UI style label not found, return unknown
 |  | ||||||
|          return "?"; |  | ||||||
|       }); |  | ||||||
|    theme_.SetMapToValueFunction( |  | ||||||
|       [](std::string text) -> std::string |  | ||||||
|       { |  | ||||||
|          // Convert label to lower case and return
 |  | ||||||
|          boost::to_lower(text); |  | ||||||
|          return text; |  | ||||||
|       }); |  | ||||||
|    theme_.SetEditWidget(self_->ui->themeComboBox); |  | ||||||
|    theme_.SetResetButton(self_->ui->resetThemeButton); |    theme_.SetResetButton(self_->ui->resetThemeButton); | ||||||
| 
 | 
 | ||||||
|    auto radarSites = config::RadarSite::GetAll(); |    auto radarSites = config::RadarSite::GetAll(); | ||||||
|  | @ -561,39 +551,11 @@ void SettingsDialogImpl::SetupGeneralTab() | ||||||
|    gridHeight_.SetEditWidget(self_->ui->gridHeightSpinBox); |    gridHeight_.SetEditWidget(self_->ui->gridHeightSpinBox); | ||||||
|    gridHeight_.SetResetButton(self_->ui->resetGridHeightButton); |    gridHeight_.SetResetButton(self_->ui->resetGridHeightButton); | ||||||
| 
 | 
 | ||||||
|    for (const auto& mapProvider : map::MapProviderIterator()) |  | ||||||
|    { |  | ||||||
|       self_->ui->mapProviderComboBox->addItem( |  | ||||||
|          QString::fromStdString(map::GetMapProviderName(mapProvider))); |  | ||||||
|    } |  | ||||||
| 
 |  | ||||||
|    mapProvider_.SetSettingsVariable(generalSettings.map_provider()); |    mapProvider_.SetSettingsVariable(generalSettings.map_provider()); | ||||||
|    mapProvider_.SetMapFromValueFunction( |    SCWX_SETTINGS_COMBO_BOX(mapProvider_, | ||||||
|       [](const std::string& text) -> std::string |                            self_->ui->mapProviderComboBox, | ||||||
|       { |                            map::MapProviderIterator(), | ||||||
|          for (map::MapProvider mapProvider : map::MapProviderIterator()) |                            map::GetMapProviderName); | ||||||
|          { |  | ||||||
|             const std::string mapProviderName = |  | ||||||
|                map::GetMapProviderName(mapProvider); |  | ||||||
| 
 |  | ||||||
|             if (boost::iequals(text, mapProviderName)) |  | ||||||
|             { |  | ||||||
|                // Return map provider label
 |  | ||||||
|                return mapProviderName; |  | ||||||
|             } |  | ||||||
|          } |  | ||||||
| 
 |  | ||||||
|          // Map provider label not found, return unknown
 |  | ||||||
|          return "?"; |  | ||||||
|       }); |  | ||||||
|    mapProvider_.SetMapToValueFunction( |  | ||||||
|       [](std::string text) -> std::string |  | ||||||
|       { |  | ||||||
|          // Convert label to lower case and return
 |  | ||||||
|          boost::to_lower(text); |  | ||||||
|          return text; |  | ||||||
|       }); |  | ||||||
|    mapProvider_.SetEditWidget(self_->ui->mapProviderComboBox); |  | ||||||
|    mapProvider_.SetResetButton(self_->ui->resetMapProviderButton); |    mapProvider_.SetResetButton(self_->ui->resetMapProviderButton); | ||||||
| 
 | 
 | ||||||
|    mapboxApiKey_.SetSettingsVariable(generalSettings.mapbox_api_key()); |    mapboxApiKey_.SetSettingsVariable(generalSettings.mapbox_api_key()); | ||||||
|  | @ -604,70 +566,62 @@ void SettingsDialogImpl::SetupGeneralTab() | ||||||
|    mapTilerApiKey_.SetEditWidget(self_->ui->mapTilerApiKeyLineEdit); |    mapTilerApiKey_.SetEditWidget(self_->ui->mapTilerApiKeyLineEdit); | ||||||
|    mapTilerApiKey_.SetResetButton(self_->ui->resetMapTilerApiKeyButton); |    mapTilerApiKey_.SetResetButton(self_->ui->resetMapTilerApiKeyButton); | ||||||
| 
 | 
 | ||||||
|    for (const auto& alertAction : types::AlertActionIterator()) |  | ||||||
|    { |  | ||||||
|       self_->ui->defaultAlertActionComboBox->addItem( |  | ||||||
|          QString::fromStdString(types::GetAlertActionName(alertAction))); |  | ||||||
|    } |  | ||||||
| 
 |  | ||||||
|    defaultAlertAction_.SetSettingsVariable( |    defaultAlertAction_.SetSettingsVariable( | ||||||
|       generalSettings.default_alert_action()); |       generalSettings.default_alert_action()); | ||||||
|    defaultAlertAction_.SetMapFromValueFunction( |    SCWX_SETTINGS_COMBO_BOX(defaultAlertAction_, | ||||||
|       SCWX_ENUM_MAP_FROM_VALUE(types::AlertAction, |                            self_->ui->defaultAlertActionComboBox, | ||||||
|                            types::AlertActionIterator(), |                            types::AlertActionIterator(), | ||||||
|                                types::GetAlertActionName)); |                            types::GetAlertActionName); | ||||||
|    defaultAlertAction_.SetMapToValueFunction( |  | ||||||
|       [](std::string text) -> std::string |  | ||||||
|       { |  | ||||||
|          // Convert label to lower case and return
 |  | ||||||
|          boost::to_lower(text); |  | ||||||
|          return text; |  | ||||||
|       }); |  | ||||||
|    defaultAlertAction_.SetEditWidget(self_->ui->defaultAlertActionComboBox); |  | ||||||
|    defaultAlertAction_.SetResetButton(self_->ui->resetDefaultAlertActionButton); |    defaultAlertAction_.SetResetButton(self_->ui->resetDefaultAlertActionButton); | ||||||
| 
 | 
 | ||||||
|    for (const auto& clockFormat : scwx::util::ClockFormatIterator()) |  | ||||||
|    { |  | ||||||
|       self_->ui->clockFormatComboBox->addItem( |  | ||||||
|          QString::fromStdString(scwx::util::GetClockFormatName(clockFormat))); |  | ||||||
|    } |  | ||||||
| 
 |  | ||||||
|    clockFormat_.SetSettingsVariable(generalSettings.clock_format()); |    clockFormat_.SetSettingsVariable(generalSettings.clock_format()); | ||||||
|    clockFormat_.SetMapFromValueFunction( |    SCWX_SETTINGS_COMBO_BOX(clockFormat_, | ||||||
|       SCWX_ENUM_MAP_FROM_VALUE(scwx::util::ClockFormat, |                            self_->ui->clockFormatComboBox, | ||||||
|                            scwx::util::ClockFormatIterator(), |                            scwx::util::ClockFormatIterator(), | ||||||
|                                scwx::util::GetClockFormatName)); |                            scwx::util::GetClockFormatName); | ||||||
|    clockFormat_.SetMapToValueFunction( |  | ||||||
|       [](std::string text) -> std::string |  | ||||||
|       { |  | ||||||
|          // Convert label to lower case and return
 |  | ||||||
|          boost::to_lower(text); |  | ||||||
|          return text; |  | ||||||
|       }); |  | ||||||
|    clockFormat_.SetEditWidget(self_->ui->clockFormatComboBox); |  | ||||||
|    clockFormat_.SetResetButton(self_->ui->resetClockFormatButton); |    clockFormat_.SetResetButton(self_->ui->resetClockFormatButton); | ||||||
| 
 | 
 | ||||||
|    for (const auto& timeZone : types::DefaultTimeZoneIterator()) |  | ||||||
|    { |  | ||||||
|       self_->ui->defaultTimeZoneComboBox->addItem( |  | ||||||
|          QString::fromStdString(types::GetDefaultTimeZoneName(timeZone))); |  | ||||||
|    } |  | ||||||
| 
 |  | ||||||
|    defaultTimeZone_.SetSettingsVariable(generalSettings.default_time_zone()); |    defaultTimeZone_.SetSettingsVariable(generalSettings.default_time_zone()); | ||||||
|    defaultTimeZone_.SetMapFromValueFunction( |    SCWX_SETTINGS_COMBO_BOX(defaultTimeZone_, | ||||||
|       SCWX_ENUM_MAP_FROM_VALUE(types::DefaultTimeZone, |                            self_->ui->defaultTimeZoneComboBox, | ||||||
|                            types::DefaultTimeZoneIterator(), |                            types::DefaultTimeZoneIterator(), | ||||||
|                                types::GetDefaultTimeZoneName)); |                            types::GetDefaultTimeZoneName); | ||||||
|    defaultTimeZone_.SetMapToValueFunction( |  | ||||||
|       [](std::string text) -> std::string |  | ||||||
|       { |  | ||||||
|          // Convert label to lower case and return
 |  | ||||||
|          boost::to_lower(text); |  | ||||||
|          return text; |  | ||||||
|       }); |  | ||||||
|    defaultTimeZone_.SetEditWidget(self_->ui->defaultTimeZoneComboBox); |  | ||||||
|    defaultTimeZone_.SetResetButton(self_->ui->resetDefaultTimeZoneButton); |    defaultTimeZone_.SetResetButton(self_->ui->resetDefaultTimeZoneButton); | ||||||
| 
 | 
 | ||||||
|  |    QObject::connect( | ||||||
|  |       self_->ui->positioningPluginComboBox, | ||||||
|  |       &QComboBox::currentTextChanged, | ||||||
|  |       self_, | ||||||
|  |       [this](const QString& text) | ||||||
|  |       { | ||||||
|  |          types::PositioningPlugin positioningPlugin = | ||||||
|  |             types::GetPositioningPlugin(text.toStdString()); | ||||||
|  | 
 | ||||||
|  |          bool gpsSourceEnabled = | ||||||
|  |             positioningPlugin == types::PositioningPlugin::Nmea; | ||||||
|  | 
 | ||||||
|  |          self_->ui->nmeaSourceLineEdit->setEnabled(gpsSourceEnabled); | ||||||
|  |          self_->ui->gpsSourceSelectButton->setEnabled(gpsSourceEnabled); | ||||||
|  |          self_->ui->nmeaBaudRateSpinBox->setEnabled(gpsSourceEnabled); | ||||||
|  |          self_->ui->resetNmeaSourceButton->setEnabled(gpsSourceEnabled); | ||||||
|  |          self_->ui->resetNmeaBaudRateButton->setEnabled(gpsSourceEnabled); | ||||||
|  |       }); | ||||||
|  | 
 | ||||||
|  |    positioningPlugin_.SetSettingsVariable(generalSettings.positioning_plugin()); | ||||||
|  |    SCWX_SETTINGS_COMBO_BOX(positioningPlugin_, | ||||||
|  |                            self_->ui->positioningPluginComboBox, | ||||||
|  |                            types::PositioningPluginIterator(), | ||||||
|  |                            types::GetPositioningPluginName); | ||||||
|  |    positioningPlugin_.SetResetButton(self_->ui->resetPositioningPluginButton); | ||||||
|  | 
 | ||||||
|  |    nmeaBaudRate_.SetSettingsVariable(generalSettings.nmea_baud_rate()); | ||||||
|  |    nmeaBaudRate_.SetEditWidget(self_->ui->nmeaBaudRateSpinBox); | ||||||
|  |    nmeaBaudRate_.SetResetButton(self_->ui->resetNmeaBaudRateButton); | ||||||
|  | 
 | ||||||
|  |    nmeaSource_.SetSettingsVariable(generalSettings.nmea_source()); | ||||||
|  |    nmeaSource_.SetEditWidget(self_->ui->nmeaSourceLineEdit); | ||||||
|  |    nmeaSource_.SetResetButton(self_->ui->resetNmeaSourceButton); | ||||||
|  | 
 | ||||||
|    warningsProvider_.SetSettingsVariable(generalSettings.warnings_provider()); |    warningsProvider_.SetSettingsVariable(generalSettings.warnings_provider()); | ||||||
|    warningsProvider_.SetEditWidget(self_->ui->warningsProviderLineEdit); |    warningsProvider_.SetEditWidget(self_->ui->warningsProviderLineEdit); | ||||||
|    warningsProvider_.SetResetButton(self_->ui->resetWarningsProviderButton); |    warningsProvider_.SetResetButton(self_->ui->resetWarningsProviderButton); | ||||||
|  | @ -991,27 +945,12 @@ void SettingsDialogImpl::SetupAudioTab() | ||||||
|          dialog->open(); |          dialog->open(); | ||||||
|       }); |       }); | ||||||
| 
 | 
 | ||||||
|    for (const auto& locationMethod : types::LocationMethodIterator()) |  | ||||||
|    { |  | ||||||
|       self_->ui->alertAudioLocationMethodComboBox->addItem( |  | ||||||
|          QString::fromStdString(types::GetLocationMethodName(locationMethod))); |  | ||||||
|    } |  | ||||||
| 
 |  | ||||||
|    alertAudioLocationMethod_.SetSettingsVariable( |    alertAudioLocationMethod_.SetSettingsVariable( | ||||||
|       audioSettings.alert_location_method()); |       audioSettings.alert_location_method()); | ||||||
|    alertAudioLocationMethod_.SetMapFromValueFunction( |    SCWX_SETTINGS_COMBO_BOX(alertAudioLocationMethod_, | ||||||
|       SCWX_ENUM_MAP_FROM_VALUE(types::LocationMethod, |                            self_->ui->alertAudioLocationMethodComboBox, | ||||||
|                            types::LocationMethodIterator(), |                            types::LocationMethodIterator(), | ||||||
|                                types::GetLocationMethodName)); |                            types::GetLocationMethodName); | ||||||
|    alertAudioLocationMethod_.SetMapToValueFunction( |  | ||||||
|       [](std::string text) -> std::string |  | ||||||
|       { |  | ||||||
|          // Convert label to lower case and return
 |  | ||||||
|          boost::to_lower(text); |  | ||||||
|          return text; |  | ||||||
|       }); |  | ||||||
|    alertAudioLocationMethod_.SetEditWidget( |  | ||||||
|       self_->ui->alertAudioLocationMethodComboBox); |  | ||||||
|    alertAudioLocationMethod_.SetResetButton( |    alertAudioLocationMethod_.SetResetButton( | ||||||
|       self_->ui->resetAlertAudioLocationMethodButton); |       self_->ui->resetAlertAudioLocationMethodButton); | ||||||
| 
 | 
 | ||||||
|  | @ -1159,40 +1098,11 @@ void SettingsDialogImpl::SetupTextTab() | ||||||
|    hoverTextWrap_.SetEditWidget(self_->ui->hoverTextWrapSpinBox); |    hoverTextWrap_.SetEditWidget(self_->ui->hoverTextWrapSpinBox); | ||||||
|    hoverTextWrap_.SetResetButton(self_->ui->resetHoverTextWrapButton); |    hoverTextWrap_.SetResetButton(self_->ui->resetHoverTextWrapButton); | ||||||
| 
 | 
 | ||||||
|    for (const auto& tooltipMethod : types::TooltipMethodIterator()) |  | ||||||
|    { |  | ||||||
|       self_->ui->tooltipMethodComboBox->addItem( |  | ||||||
|          QString::fromStdString(types::GetTooltipMethodName(tooltipMethod))); |  | ||||||
|    } |  | ||||||
| 
 |  | ||||||
|    tooltipMethod_.SetSettingsVariable(textSettings.tooltip_method()); |    tooltipMethod_.SetSettingsVariable(textSettings.tooltip_method()); | ||||||
|    tooltipMethod_.SetMapFromValueFunction( |    SCWX_SETTINGS_COMBO_BOX(tooltipMethod_, | ||||||
|       [](const std::string& text) -> std::string |                            self_->ui->tooltipMethodComboBox, | ||||||
|       { |                            types::TooltipMethodIterator(), | ||||||
|          for (types::TooltipMethod tooltipMethod : |                            types::GetTooltipMethodName); | ||||||
|               types::TooltipMethodIterator()) |  | ||||||
|          { |  | ||||||
|             const std::string tooltipMethodName = |  | ||||||
|                types::GetTooltipMethodName(tooltipMethod); |  | ||||||
| 
 |  | ||||||
|             if (boost::iequals(text, tooltipMethodName)) |  | ||||||
|             { |  | ||||||
|                // Return tooltip method label
 |  | ||||||
|                return tooltipMethodName; |  | ||||||
|             } |  | ||||||
|          } |  | ||||||
| 
 |  | ||||||
|          // Tooltip method label not found, return unknown
 |  | ||||||
|          return "?"; |  | ||||||
|       }); |  | ||||||
|    tooltipMethod_.SetMapToValueFunction( |  | ||||||
|       [](std::string text) -> std::string |  | ||||||
|       { |  | ||||||
|          // Convert label to lower case and return
 |  | ||||||
|          boost::to_lower(text); |  | ||||||
|          return text; |  | ||||||
|       }); |  | ||||||
|    tooltipMethod_.SetEditWidget(self_->ui->tooltipMethodComboBox); |  | ||||||
|    tooltipMethod_.SetResetButton(self_->ui->resetTooltipMethodButton); |    tooltipMethod_.SetResetButton(self_->ui->resetTooltipMethodButton); | ||||||
| 
 | 
 | ||||||
|    placefileTextDropShadowEnabled_.SetSettingsVariable( |    placefileTextDropShadowEnabled_.SetSettingsVariable( | ||||||
|  |  | ||||||
|  | @ -17,10 +17,10 @@ | ||||||
|    <item row="0" column="0"> |    <item row="0" column="0"> | ||||||
|     <widget class="QFrame" name="frame"> |     <widget class="QFrame" name="frame"> | ||||||
|      <property name="frameShape"> |      <property name="frameShape"> | ||||||
|       <enum>QFrame::StyledPanel</enum> |       <enum>QFrame::Shape::StyledPanel</enum> | ||||||
|      </property> |      </property> | ||||||
|      <property name="frameShadow"> |      <property name="frameShadow"> | ||||||
|       <enum>QFrame::Raised</enum> |       <enum>QFrame::Shadow::Raised</enum> | ||||||
|      </property> |      </property> | ||||||
|      <layout class="QHBoxLayout" name="horizontalLayout"> |      <layout class="QHBoxLayout" name="horizontalLayout"> | ||||||
|       <property name="leftMargin"> |       <property name="leftMargin"> | ||||||
|  | @ -38,7 +38,7 @@ | ||||||
|       <item> |       <item> | ||||||
|        <widget class="QSplitter" name="splitter"> |        <widget class="QSplitter" name="splitter"> | ||||||
|         <property name="orientation"> |         <property name="orientation"> | ||||||
|          <enum>Qt::Horizontal</enum> |          <enum>Qt::Orientation::Horizontal</enum> | ||||||
|         </property> |         </property> | ||||||
|         <widget class="QListWidget" name="listWidget"> |         <widget class="QListWidget" name="listWidget"> | ||||||
|          <property name="sizePolicy"> |          <property name="sizePolicy"> | ||||||
|  | @ -48,13 +48,13 @@ | ||||||
|           </sizepolicy> |           </sizepolicy> | ||||||
|          </property> |          </property> | ||||||
|          <property name="sizeAdjustPolicy"> |          <property name="sizeAdjustPolicy"> | ||||||
|           <enum>QAbstractScrollArea::AdjustToContents</enum> |           <enum>QAbstractScrollArea::SizeAdjustPolicy::AdjustToContents</enum> | ||||||
|          </property> |          </property> | ||||||
|          <property name="resizeMode"> |          <property name="resizeMode"> | ||||||
|           <enum>QListView::Adjust</enum> |           <enum>QListView::ResizeMode::Adjust</enum> | ||||||
|          </property> |          </property> | ||||||
|          <property name="viewMode"> |          <property name="viewMode"> | ||||||
|           <enum>QListView::ListMode</enum> |           <enum>QListView::ViewMode::ListMode</enum> | ||||||
|          </property> |          </property> | ||||||
|          <property name="uniformItemSizes"> |          <property name="uniformItemSizes"> | ||||||
|           <bool>true</bool> |           <bool>true</bool> | ||||||
|  | @ -137,14 +137,14 @@ | ||||||
|                 <x>0</x> |                 <x>0</x> | ||||||
|                 <y>0</y> |                 <y>0</y> | ||||||
|                 <width>513</width> |                 <width>513</width> | ||||||
|                 <height>482</height> |                 <height>566</height> | ||||||
|                </rect> |                </rect> | ||||||
|               </property> |               </property> | ||||||
|               <layout class="QVBoxLayout" name="verticalLayout"> |               <layout class="QVBoxLayout" name="verticalLayout"> | ||||||
|                <item> |                <item> | ||||||
|                 <widget class="QFrame" name="frame_2"> |                 <widget class="QFrame" name="frame_2"> | ||||||
|                  <property name="frameShape"> |                  <property name="frameShape"> | ||||||
|                   <enum>QFrame::NoFrame</enum> |                   <enum>QFrame::Shape::NoFrame</enum> | ||||||
|                  </property> |                  </property> | ||||||
|                  <layout class="QGridLayout" name="gridLayout_4"> |                  <layout class="QGridLayout" name="gridLayout_4"> | ||||||
|                   <property name="leftMargin"> |                   <property name="leftMargin"> | ||||||
|  | @ -159,93 +159,14 @@ | ||||||
|                   <property name="bottomMargin"> |                   <property name="bottomMargin"> | ||||||
|                    <number>0</number> |                    <number>0</number> | ||||||
|                   </property> |                   </property> | ||||||
|                   <item row="8" column="2"> |                   <item row="13" column="0"> | ||||||
|                    <widget class="QComboBox" name="defaultAlertActionComboBox"/> |                    <widget class="QLabel" name="label_6"> | ||||||
|  |                     <property name="text"> | ||||||
|  |                      <string>MapTiler API Key</string> | ||||||
|  |                     </property> | ||||||
|  |                    </widget> | ||||||
|                   </item> |                   </item> | ||||||
|                   <item row="6" column="4"> |                   <item row="6" column="4"> | ||||||
|                    <widget class="QToolButton" name="resetMapboxApiKeyButton"> |  | ||||||
|                     <property name="text"> |  | ||||||
|                      <string>...</string> |  | ||||||
|                     </property> |  | ||||||
|                     <property name="icon"> |  | ||||||
|                      <iconset resource="../../../../scwx-qt.qrc"> |  | ||||||
|                       <normaloff>:/res/icons/font-awesome-6/rotate-left-solid.svg</normaloff>:/res/icons/font-awesome-6/rotate-left-solid.svg</iconset> |  | ||||||
|                     </property> |  | ||||||
|                    </widget> |  | ||||||
|                   </item> |  | ||||||
|                   <item row="6" column="0"> |  | ||||||
|                    <widget class="QLabel" name="label_4"> |  | ||||||
|                     <property name="text"> |  | ||||||
|                      <string>Mapbox API Key</string> |  | ||||||
|                     </property> |  | ||||||
|                    </widget> |  | ||||||
|                   </item> |  | ||||||
|                   <item row="9" column="4"> |  | ||||||
|                    <widget class="QToolButton" name="resetClockFormatButton"> |  | ||||||
|                     <property name="text"> |  | ||||||
|                      <string>...</string> |  | ||||||
|                     </property> |  | ||||||
|                     <property name="icon"> |  | ||||||
|                      <iconset resource="../../../../scwx-qt.qrc"> |  | ||||||
|                       <normaloff>:/res/icons/font-awesome-6/rotate-left-solid.svg</normaloff>:/res/icons/font-awesome-6/rotate-left-solid.svg</iconset> |  | ||||||
|                     </property> |  | ||||||
|                    </widget> |  | ||||||
|                   </item> |  | ||||||
|                   <item row="10" column="0"> |  | ||||||
|                    <widget class="QLabel" name="label_21"> |  | ||||||
|                     <property name="text"> |  | ||||||
|                      <string>Default Time Zone</string> |  | ||||||
|                     </property> |  | ||||||
|                    </widget> |  | ||||||
|                   </item> |  | ||||||
|                   <item row="8" column="4"> |  | ||||||
|                    <widget class="QToolButton" name="resetDefaultAlertActionButton"> |  | ||||||
|                     <property name="text"> |  | ||||||
|                      <string>...</string> |  | ||||||
|                     </property> |  | ||||||
|                     <property name="icon"> |  | ||||||
|                      <iconset resource="../../../../scwx-qt.qrc"> |  | ||||||
|                       <normaloff>:/res/icons/font-awesome-6/rotate-left-solid.svg</normaloff>:/res/icons/font-awesome-6/rotate-left-solid.svg</iconset> |  | ||||||
|                     </property> |  | ||||||
|                    </widget> |  | ||||||
|                   </item> |  | ||||||
|                   <item row="9" column="0"> |  | ||||||
|                    <widget class="QLabel" name="label_20"> |  | ||||||
|                     <property name="text"> |  | ||||||
|                      <string>Clock Format</string> |  | ||||||
|                     </property> |  | ||||||
|                    </widget> |  | ||||||
|                   </item> |  | ||||||
|                   <item row="2" column="4"> |  | ||||||
|                    <widget class="QToolButton" name="resetGridWidthButton"> |  | ||||||
|                     <property name="text"> |  | ||||||
|                      <string>...</string> |  | ||||||
|                     </property> |  | ||||||
|                     <property name="icon"> |  | ||||||
|                      <iconset resource="../../../../scwx-qt.qrc"> |  | ||||||
|                       <normaloff>:/res/icons/font-awesome-6/rotate-left-solid.svg</normaloff>:/res/icons/font-awesome-6/rotate-left-solid.svg</iconset> |  | ||||||
|                     </property> |  | ||||||
|                    </widget> |  | ||||||
|                   </item> |  | ||||||
|                   <item row="1" column="4"> |  | ||||||
|                    <widget class="QToolButton" name="resetRadarSiteButton"> |  | ||||||
|                     <property name="text"> |  | ||||||
|                      <string>...</string> |  | ||||||
|                     </property> |  | ||||||
|                     <property name="icon"> |  | ||||||
|                      <iconset resource="../../../../scwx-qt.qrc"> |  | ||||||
|                       <normaloff>:/res/icons/font-awesome-6/rotate-left-solid.svg</normaloff>:/res/icons/font-awesome-6/rotate-left-solid.svg</iconset> |  | ||||||
|                     </property> |  | ||||||
|                    </widget> |  | ||||||
|                   </item> |  | ||||||
|                   <item row="3" column="0"> |  | ||||||
|                    <widget class="QLabel" name="label_3"> |  | ||||||
|                     <property name="text"> |  | ||||||
|                      <string>Grid Height</string> |  | ||||||
|                     </property> |  | ||||||
|                    </widget> |  | ||||||
|                   </item> |  | ||||||
|                   <item row="3" column="4"> |  | ||||||
|                    <widget class="QToolButton" name="resetGridHeightButton"> |                    <widget class="QToolButton" name="resetGridHeightButton"> | ||||||
|                     <property name="text"> |                     <property name="text"> | ||||||
|                      <string>...</string> |                      <string>...</string> | ||||||
|  | @ -257,23 +178,17 @@ | ||||||
|                    </widget> |                    </widget> | ||||||
|                   </item> |                   </item> | ||||||
|                   <item row="11" column="2"> |                   <item row="11" column="2"> | ||||||
|                    <widget class="QLineEdit" name="warningsProviderLineEdit"/> |                    <widget class="QComboBox" name="mapProviderComboBox"/> | ||||||
|                   </item> |                   </item> | ||||||
|                   <item row="0" column="2"> |                   <item row="4" column="0"> | ||||||
|                    <widget class="QComboBox" name="themeComboBox"/> |                    <widget class="QLabel" name="label_21"> | ||||||
|                   </item> |  | ||||||
|                   <item row="11" column="0"> |  | ||||||
|                    <widget class="QLabel" name="label_22"> |  | ||||||
|                     <property name="text"> |                     <property name="text"> | ||||||
|                      <string>Warnings Provider</string> |                      <string>Default Time Zone</string> | ||||||
|                     </property> |                     </property> | ||||||
|                    </widget> |                    </widget> | ||||||
|                   </item> |                   </item> | ||||||
|                   <item row="2" column="2"> |                   <item row="12" column="4"> | ||||||
|                    <widget class="QSpinBox" name="gridWidthSpinBox"/> |                    <widget class="QToolButton" name="resetMapboxApiKeyButton"> | ||||||
|                   </item> |  | ||||||
|                   <item row="5" column="4"> |  | ||||||
|                    <widget class="QToolButton" name="resetMapProviderButton"> |  | ||||||
|                     <property name="text"> |                     <property name="text"> | ||||||
|                      <string>...</string> |                      <string>...</string> | ||||||
|                     </property> |                     </property> | ||||||
|  | @ -284,106 +199,9 @@ | ||||||
|                    </widget> |                    </widget> | ||||||
|                   </item> |                   </item> | ||||||
|                   <item row="6" column="2"> |                   <item row="6" column="2"> | ||||||
|                    <widget class="QLineEdit" name="mapboxApiKeyLineEdit"> |  | ||||||
|                     <property name="echoMode"> |  | ||||||
|                      <enum>QLineEdit::Password</enum> |  | ||||||
|                     </property> |  | ||||||
|                    </widget> |  | ||||||
|                   </item> |  | ||||||
|                   <item row="0" column="4"> |  | ||||||
|                    <widget class="QToolButton" name="resetThemeButton"> |  | ||||||
|                     <property name="text"> |  | ||||||
|                      <string>...</string> |  | ||||||
|                     </property> |  | ||||||
|                     <property name="icon"> |  | ||||||
|                      <iconset resource="../../../../scwx-qt.qrc"> |  | ||||||
|                       <normaloff>:/res/icons/font-awesome-6/rotate-left-solid.svg</normaloff>:/res/icons/font-awesome-6/rotate-left-solid.svg</iconset> |  | ||||||
|                     </property> |  | ||||||
|                    </widget> |  | ||||||
|                   </item> |  | ||||||
|                   <item row="3" column="2"> |  | ||||||
|                    <widget class="QSpinBox" name="gridHeightSpinBox"/> |                    <widget class="QSpinBox" name="gridHeightSpinBox"/> | ||||||
|                   </item> |                   </item> | ||||||
|                   <item row="9" column="2"> |                   <item row="4" column="4"> | ||||||
|                    <widget class="QComboBox" name="clockFormatComboBox"/> |  | ||||||
|                   </item> |  | ||||||
|                   <item row="10" column="2"> |  | ||||||
|                    <widget class="QComboBox" name="defaultTimeZoneComboBox"/> |  | ||||||
|                   </item> |  | ||||||
|                   <item row="1" column="3"> |  | ||||||
|                    <widget class="QToolButton" name="radarSiteSelectButton"> |  | ||||||
|                     <property name="text"> |  | ||||||
|                      <string>...</string> |  | ||||||
|                     </property> |  | ||||||
|                    </widget> |  | ||||||
|                   </item> |  | ||||||
|                   <item row="0" column="0"> |  | ||||||
|                    <widget class="QLabel" name="label_5"> |  | ||||||
|                     <property name="text"> |  | ||||||
|                      <string>Theme</string> |  | ||||||
|                     </property> |  | ||||||
|                    </widget> |  | ||||||
|                   </item> |  | ||||||
|                   <item row="7" column="0"> |  | ||||||
|                    <widget class="QLabel" name="label_6"> |  | ||||||
|                     <property name="text"> |  | ||||||
|                      <string>MapTiler API Key</string> |  | ||||||
|                     </property> |  | ||||||
|                    </widget> |  | ||||||
|                   </item> |  | ||||||
|                   <item row="5" column="0"> |  | ||||||
|                    <widget class="QLabel" name="label_7"> |  | ||||||
|                     <property name="text"> |  | ||||||
|                      <string>Map Provider</string> |  | ||||||
|                     </property> |  | ||||||
|                    </widget> |  | ||||||
|                   </item> |  | ||||||
|                   <item row="8" column="0"> |  | ||||||
|                    <widget class="QLabel" name="defaultAlertActionLabel"> |  | ||||||
|                     <property name="text"> |  | ||||||
|                      <string>Default Alert Action</string> |  | ||||||
|                     </property> |  | ||||||
|                    </widget> |  | ||||||
|                   </item> |  | ||||||
|                   <item row="2" column="0"> |  | ||||||
|                    <widget class="QLabel" name="label_2"> |  | ||||||
|                     <property name="text"> |  | ||||||
|                      <string>Grid Width</string> |  | ||||||
|                     </property> |  | ||||||
|                    </widget> |  | ||||||
|                   </item> |  | ||||||
|                   <item row="1" column="0"> |  | ||||||
|                    <widget class="QLabel" name="label"> |  | ||||||
|                     <property name="text"> |  | ||||||
|                      <string>Default Radar Site</string> |  | ||||||
|                     </property> |  | ||||||
|                    </widget> |  | ||||||
|                   </item> |  | ||||||
|                   <item row="5" column="2"> |  | ||||||
|                    <widget class="QComboBox" name="mapProviderComboBox"/> |  | ||||||
|                   </item> |  | ||||||
|                   <item row="7" column="4"> |  | ||||||
|                    <widget class="QToolButton" name="resetMapTilerApiKeyButton"> |  | ||||||
|                     <property name="text"> |  | ||||||
|                      <string>...</string> |  | ||||||
|                     </property> |  | ||||||
|                     <property name="icon"> |  | ||||||
|                      <iconset resource="../../../../scwx-qt.qrc"> |  | ||||||
|                       <normaloff>:/res/icons/font-awesome-6/rotate-left-solid.svg</normaloff>:/res/icons/font-awesome-6/rotate-left-solid.svg</iconset> |  | ||||||
|                     </property> |  | ||||||
|                    </widget> |  | ||||||
|                   </item> |  | ||||||
|                   <item row="1" column="2"> |  | ||||||
|                    <widget class="QComboBox" name="radarSiteComboBox"/> |  | ||||||
|                   </item> |  | ||||||
|                   <item row="7" column="2"> |  | ||||||
|                    <widget class="QLineEdit" name="mapTilerApiKeyLineEdit"> |  | ||||||
|                     <property name="echoMode"> |  | ||||||
|                      <enum>QLineEdit::Password</enum> |  | ||||||
|                     </property> |  | ||||||
|                    </widget> |  | ||||||
|                   </item> |  | ||||||
|                   <item row="10" column="4"> |  | ||||||
|                    <widget class="QToolButton" name="resetDefaultTimeZoneButton"> |                    <widget class="QToolButton" name="resetDefaultTimeZoneButton"> | ||||||
|                     <property name="text"> |                     <property name="text"> | ||||||
|                      <string>...</string> |                      <string>...</string> | ||||||
|  | @ -394,7 +212,72 @@ | ||||||
|                     </property> |                     </property> | ||||||
|                    </widget> |                    </widget> | ||||||
|                   </item> |                   </item> | ||||||
|                   <item row="11" column="4"> |                   <item row="11" column="0"> | ||||||
|  |                    <widget class="QLabel" name="label_7"> | ||||||
|  |                     <property name="text"> | ||||||
|  |                      <string>Map Provider</string> | ||||||
|  |                     </property> | ||||||
|  |                    </widget> | ||||||
|  |                   </item> | ||||||
|  |                   <item row="4" column="2"> | ||||||
|  |                    <widget class="QComboBox" name="defaultTimeZoneComboBox"/> | ||||||
|  |                   </item> | ||||||
|  |                   <item row="18" column="4"> | ||||||
|  |                    <widget class="QToolButton" name="resetThemeButton"> | ||||||
|  |                     <property name="text"> | ||||||
|  |                      <string>...</string> | ||||||
|  |                     </property> | ||||||
|  |                     <property name="icon"> | ||||||
|  |                      <iconset resource="../../../../scwx-qt.qrc"> | ||||||
|  |                       <normaloff>:/res/icons/font-awesome-6/rotate-left-solid.svg</normaloff>:/res/icons/font-awesome-6/rotate-left-solid.svg</iconset> | ||||||
|  |                     </property> | ||||||
|  |                    </widget> | ||||||
|  |                   </item> | ||||||
|  |                   <item row="1" column="2"> | ||||||
|  |                    <widget class="QComboBox" name="clockFormatComboBox"/> | ||||||
|  |                   </item> | ||||||
|  |                   <item row="3" column="2"> | ||||||
|  |                    <widget class="QComboBox" name="radarSiteComboBox"/> | ||||||
|  |                   </item> | ||||||
|  |                   <item row="9" column="3"> | ||||||
|  |                    <widget class="QToolButton" name="gpsSourceSelectButton"> | ||||||
|  |                     <property name="text"> | ||||||
|  |                      <string>...</string> | ||||||
|  |                     </property> | ||||||
|  |                    </widget> | ||||||
|  |                   </item> | ||||||
|  |                   <item row="12" column="2"> | ||||||
|  |                    <widget class="QLineEdit" name="mapboxApiKeyLineEdit"> | ||||||
|  |                     <property name="echoMode"> | ||||||
|  |                      <enum>QLineEdit::EchoMode::Password</enum> | ||||||
|  |                     </property> | ||||||
|  |                    </widget> | ||||||
|  |                   </item> | ||||||
|  |                   <item row="8" column="0"> | ||||||
|  |                    <widget class="QLabel" name="label_23"> | ||||||
|  |                     <property name="text"> | ||||||
|  |                      <string>GPS Plugin</string> | ||||||
|  |                     </property> | ||||||
|  |                    </widget> | ||||||
|  |                   </item> | ||||||
|  |                   <item row="10" column="0"> | ||||||
|  |                    <widget class="QLabel" name="label_24"> | ||||||
|  |                     <property name="text"> | ||||||
|  |                      <string>GPS Baud Rate</string> | ||||||
|  |                     </property> | ||||||
|  |                    </widget> | ||||||
|  |                   </item> | ||||||
|  |                   <item row="5" column="2"> | ||||||
|  |                    <widget class="QSpinBox" name="gridWidthSpinBox"/> | ||||||
|  |                   </item> | ||||||
|  |                   <item row="3" column="3"> | ||||||
|  |                    <widget class="QToolButton" name="radarSiteSelectButton"> | ||||||
|  |                     <property name="text"> | ||||||
|  |                      <string>...</string> | ||||||
|  |                     </property> | ||||||
|  |                    </widget> | ||||||
|  |                   </item> | ||||||
|  |                   <item row="21" column="4"> | ||||||
|                    <widget class="QToolButton" name="resetWarningsProviderButton"> |                    <widget class="QToolButton" name="resetWarningsProviderButton"> | ||||||
|                     <property name="text"> |                     <property name="text"> | ||||||
|                      <string>...</string> |                      <string>...</string> | ||||||
|  | @ -405,6 +288,200 @@ | ||||||
|                     </property> |                     </property> | ||||||
|                    </widget> |                    </widget> | ||||||
|                   </item> |                   </item> | ||||||
|  |                   <item row="5" column="0"> | ||||||
|  |                    <widget class="QLabel" name="label_2"> | ||||||
|  |                     <property name="text"> | ||||||
|  |                      <string>Grid Width</string> | ||||||
|  |                     </property> | ||||||
|  |                    </widget> | ||||||
|  |                   </item> | ||||||
|  |                   <item row="9" column="2"> | ||||||
|  |                    <widget class="QLineEdit" name="nmeaSourceLineEdit"/> | ||||||
|  |                   </item> | ||||||
|  |                   <item row="18" column="2"> | ||||||
|  |                    <widget class="QComboBox" name="themeComboBox"/> | ||||||
|  |                   </item> | ||||||
|  |                   <item row="8" column="2"> | ||||||
|  |                    <widget class="QComboBox" name="positioningPluginComboBox"/> | ||||||
|  |                   </item> | ||||||
|  |                   <item row="9" column="0"> | ||||||
|  |                    <widget class="QLabel" name="label_25"> | ||||||
|  |                     <property name="text"> | ||||||
|  |                      <string>GPS Source</string> | ||||||
|  |                     </property> | ||||||
|  |                    </widget> | ||||||
|  |                   </item> | ||||||
|  |                   <item row="21" column="2"> | ||||||
|  |                    <widget class="QLineEdit" name="warningsProviderLineEdit"/> | ||||||
|  |                   </item> | ||||||
|  |                   <item row="3" column="0"> | ||||||
|  |                    <widget class="QLabel" name="label"> | ||||||
|  |                     <property name="text"> | ||||||
|  |                      <string>Default Radar Site</string> | ||||||
|  |                     </property> | ||||||
|  |                    </widget> | ||||||
|  |                   </item> | ||||||
|  |                   <item row="3" column="4"> | ||||||
|  |                    <widget class="QToolButton" name="resetRadarSiteButton"> | ||||||
|  |                     <property name="text"> | ||||||
|  |                      <string>...</string> | ||||||
|  |                     </property> | ||||||
|  |                     <property name="icon"> | ||||||
|  |                      <iconset resource="../../../../scwx-qt.qrc"> | ||||||
|  |                       <normaloff>:/res/icons/font-awesome-6/rotate-left-solid.svg</normaloff>:/res/icons/font-awesome-6/rotate-left-solid.svg</iconset> | ||||||
|  |                     </property> | ||||||
|  |                    </widget> | ||||||
|  |                   </item> | ||||||
|  |                   <item row="6" column="0"> | ||||||
|  |                    <widget class="QLabel" name="label_3"> | ||||||
|  |                     <property name="text"> | ||||||
|  |                      <string>Grid Height</string> | ||||||
|  |                     </property> | ||||||
|  |                    </widget> | ||||||
|  |                   </item> | ||||||
|  |                   <item row="2" column="4"> | ||||||
|  |                    <widget class="QToolButton" name="resetDefaultAlertActionButton"> | ||||||
|  |                     <property name="text"> | ||||||
|  |                      <string>...</string> | ||||||
|  |                     </property> | ||||||
|  |                     <property name="icon"> | ||||||
|  |                      <iconset resource="../../../../scwx-qt.qrc"> | ||||||
|  |                       <normaloff>:/res/icons/font-awesome-6/rotate-left-solid.svg</normaloff>:/res/icons/font-awesome-6/rotate-left-solid.svg</iconset> | ||||||
|  |                     </property> | ||||||
|  |                    </widget> | ||||||
|  |                   </item> | ||||||
|  |                   <item row="18" column="0"> | ||||||
|  |                    <widget class="QLabel" name="label_5"> | ||||||
|  |                     <property name="text"> | ||||||
|  |                      <string>Theme</string> | ||||||
|  |                     </property> | ||||||
|  |                    </widget> | ||||||
|  |                   </item> | ||||||
|  |                   <item row="1" column="4"> | ||||||
|  |                    <widget class="QToolButton" name="resetClockFormatButton"> | ||||||
|  |                     <property name="text"> | ||||||
|  |                      <string>...</string> | ||||||
|  |                     </property> | ||||||
|  |                     <property name="icon"> | ||||||
|  |                      <iconset resource="../../../../scwx-qt.qrc"> | ||||||
|  |                       <normaloff>:/res/icons/font-awesome-6/rotate-left-solid.svg</normaloff>:/res/icons/font-awesome-6/rotate-left-solid.svg</iconset> | ||||||
|  |                     </property> | ||||||
|  |                    </widget> | ||||||
|  |                   </item> | ||||||
|  |                   <item row="12" column="0"> | ||||||
|  |                    <widget class="QLabel" name="label_4"> | ||||||
|  |                     <property name="text"> | ||||||
|  |                      <string>Mapbox API Key</string> | ||||||
|  |                     </property> | ||||||
|  |                    </widget> | ||||||
|  |                   </item> | ||||||
|  |                   <item row="1" column="0"> | ||||||
|  |                    <widget class="QLabel" name="label_20"> | ||||||
|  |                     <property name="text"> | ||||||
|  |                      <string>Clock Format</string> | ||||||
|  |                     </property> | ||||||
|  |                    </widget> | ||||||
|  |                   </item> | ||||||
|  |                   <item row="13" column="2"> | ||||||
|  |                    <widget class="QLineEdit" name="mapTilerApiKeyLineEdit"> | ||||||
|  |                     <property name="echoMode"> | ||||||
|  |                      <enum>QLineEdit::EchoMode::Password</enum> | ||||||
|  |                     </property> | ||||||
|  |                    </widget> | ||||||
|  |                   </item> | ||||||
|  |                   <item row="5" column="4"> | ||||||
|  |                    <widget class="QToolButton" name="resetGridWidthButton"> | ||||||
|  |                     <property name="text"> | ||||||
|  |                      <string>...</string> | ||||||
|  |                     </property> | ||||||
|  |                     <property name="icon"> | ||||||
|  |                      <iconset resource="../../../../scwx-qt.qrc"> | ||||||
|  |                       <normaloff>:/res/icons/font-awesome-6/rotate-left-solid.svg</normaloff>:/res/icons/font-awesome-6/rotate-left-solid.svg</iconset> | ||||||
|  |                     </property> | ||||||
|  |                    </widget> | ||||||
|  |                   </item> | ||||||
|  |                   <item row="13" column="4"> | ||||||
|  |                    <widget class="QToolButton" name="resetMapTilerApiKeyButton"> | ||||||
|  |                     <property name="text"> | ||||||
|  |                      <string>...</string> | ||||||
|  |                     </property> | ||||||
|  |                     <property name="icon"> | ||||||
|  |                      <iconset resource="../../../../scwx-qt.qrc"> | ||||||
|  |                       <normaloff>:/res/icons/font-awesome-6/rotate-left-solid.svg</normaloff>:/res/icons/font-awesome-6/rotate-left-solid.svg</iconset> | ||||||
|  |                     </property> | ||||||
|  |                    </widget> | ||||||
|  |                   </item> | ||||||
|  |                   <item row="2" column="2"> | ||||||
|  |                    <widget class="QComboBox" name="defaultAlertActionComboBox"/> | ||||||
|  |                   </item> | ||||||
|  |                   <item row="21" column="0"> | ||||||
|  |                    <widget class="QLabel" name="label_22"> | ||||||
|  |                     <property name="text"> | ||||||
|  |                      <string>Warnings Provider</string> | ||||||
|  |                     </property> | ||||||
|  |                    </widget> | ||||||
|  |                   </item> | ||||||
|  |                   <item row="10" column="2"> | ||||||
|  |                    <widget class="QSpinBox" name="nmeaBaudRateSpinBox"> | ||||||
|  |                     <property name="minimum"> | ||||||
|  |                      <number>1</number> | ||||||
|  |                     </property> | ||||||
|  |                     <property name="maximum"> | ||||||
|  |                      <number>999999999</number> | ||||||
|  |                     </property> | ||||||
|  |                    </widget> | ||||||
|  |                   </item> | ||||||
|  |                   <item row="2" column="0"> | ||||||
|  |                    <widget class="QLabel" name="defaultAlertActionLabel"> | ||||||
|  |                     <property name="text"> | ||||||
|  |                      <string>Default Alert Action</string> | ||||||
|  |                     </property> | ||||||
|  |                    </widget> | ||||||
|  |                   </item> | ||||||
|  |                   <item row="11" column="4"> | ||||||
|  |                    <widget class="QToolButton" name="resetMapProviderButton"> | ||||||
|  |                     <property name="text"> | ||||||
|  |                      <string>...</string> | ||||||
|  |                     </property> | ||||||
|  |                     <property name="icon"> | ||||||
|  |                      <iconset resource="../../../../scwx-qt.qrc"> | ||||||
|  |                       <normaloff>:/res/icons/font-awesome-6/rotate-left-solid.svg</normaloff>:/res/icons/font-awesome-6/rotate-left-solid.svg</iconset> | ||||||
|  |                     </property> | ||||||
|  |                    </widget> | ||||||
|  |                   </item> | ||||||
|  |                   <item row="8" column="4"> | ||||||
|  |                    <widget class="QToolButton" name="resetPositioningPluginButton"> | ||||||
|  |                     <property name="text"> | ||||||
|  |                      <string>...</string> | ||||||
|  |                     </property> | ||||||
|  |                     <property name="icon"> | ||||||
|  |                      <iconset resource="../../../../scwx-qt.qrc"> | ||||||
|  |                       <normaloff>:/res/icons/font-awesome-6/rotate-left-solid.svg</normaloff>:/res/icons/font-awesome-6/rotate-left-solid.svg</iconset> | ||||||
|  |                     </property> | ||||||
|  |                    </widget> | ||||||
|  |                   </item> | ||||||
|  |                   <item row="9" column="4"> | ||||||
|  |                    <widget class="QToolButton" name="resetNmeaSourceButton"> | ||||||
|  |                     <property name="text"> | ||||||
|  |                      <string>...</string> | ||||||
|  |                     </property> | ||||||
|  |                     <property name="icon"> | ||||||
|  |                      <iconset resource="../../../../scwx-qt.qrc"> | ||||||
|  |                       <normaloff>:/res/icons/font-awesome-6/rotate-left-solid.svg</normaloff>:/res/icons/font-awesome-6/rotate-left-solid.svg</iconset> | ||||||
|  |                     </property> | ||||||
|  |                    </widget> | ||||||
|  |                   </item> | ||||||
|  |                   <item row="10" column="4"> | ||||||
|  |                    <widget class="QToolButton" name="resetNmeaBaudRateButton"> | ||||||
|  |                     <property name="text"> | ||||||
|  |                      <string>...</string> | ||||||
|  |                     </property> | ||||||
|  |                     <property name="icon"> | ||||||
|  |                      <iconset resource="../../../../scwx-qt.qrc"> | ||||||
|  |                       <normaloff>:/res/icons/font-awesome-6/rotate-left-solid.svg</normaloff>:/res/icons/font-awesome-6/rotate-left-solid.svg</iconset> | ||||||
|  |                     </property> | ||||||
|  |                    </widget> | ||||||
|  |                   </item> | ||||||
|                  </layout> |                  </layout> | ||||||
|                 </widget> |                 </widget> | ||||||
|                </item> |                </item> | ||||||
|  | @ -453,7 +530,7 @@ | ||||||
|                <item> |                <item> | ||||||
|                 <spacer name="verticalSpacer"> |                 <spacer name="verticalSpacer"> | ||||||
|                  <property name="orientation"> |                  <property name="orientation"> | ||||||
|                   <enum>Qt::Vertical</enum> |                   <enum>Qt::Orientation::Vertical</enum> | ||||||
|                  </property> |                  </property> | ||||||
|                  <property name="sizeHint" stdset="0"> |                  <property name="sizeHint" stdset="0"> | ||||||
|                   <size> |                   <size> | ||||||
|  | @ -491,15 +568,15 @@ | ||||||
|                    <rect> |                    <rect> | ||||||
|                     <x>0</x> |                     <x>0</x> | ||||||
|                     <y>0</y> |                     <y>0</y> | ||||||
|                     <width>506</width> |                     <width>63</width> | ||||||
|                     <height>383</height> |                     <height>18</height> | ||||||
|                    </rect> |                    </rect> | ||||||
|                   </property> |                   </property> | ||||||
|                   <layout class="QGridLayout" name="gridLayout_3"> |                   <layout class="QGridLayout" name="gridLayout_3"> | ||||||
|                    <item row="0" column="0"> |                    <item row="0" column="0"> | ||||||
|                     <spacer name="verticalSpacer_3"> |                     <spacer name="verticalSpacer_3"> | ||||||
|                      <property name="orientation"> |                      <property name="orientation"> | ||||||
|                       <enum>Qt::Vertical</enum> |                       <enum>Qt::Orientation::Vertical</enum> | ||||||
|                      </property> |                      </property> | ||||||
|                      <property name="sizeHint" stdset="0"> |                      <property name="sizeHint" stdset="0"> | ||||||
|                       <size> |                       <size> | ||||||
|  | @ -523,10 +600,10 @@ | ||||||
|                <item> |                <item> | ||||||
|                 <widget class="QFrame" name="alertsFrame"> |                 <widget class="QFrame" name="alertsFrame"> | ||||||
|                  <property name="frameShape"> |                  <property name="frameShape"> | ||||||
|                   <enum>QFrame::StyledPanel</enum> |                   <enum>QFrame::Shape::StyledPanel</enum> | ||||||
|                  </property> |                  </property> | ||||||
|                  <property name="frameShadow"> |                  <property name="frameShadow"> | ||||||
|                   <enum>QFrame::Raised</enum> |                   <enum>QFrame::Shadow::Raised</enum> | ||||||
|                  </property> |                  </property> | ||||||
|                  <layout class="QGridLayout" name="gridLayout_5"> |                  <layout class="QGridLayout" name="gridLayout_5"> | ||||||
|                   <property name="leftMargin"> |                   <property name="leftMargin"> | ||||||
|  | @ -547,7 +624,7 @@ | ||||||
|                <item> |                <item> | ||||||
|                 <spacer name="verticalSpacer_2"> |                 <spacer name="verticalSpacer_2"> | ||||||
|                  <property name="orientation"> |                  <property name="orientation"> | ||||||
|                   <enum>Qt::Vertical</enum> |                   <enum>Qt::Orientation::Vertical</enum> | ||||||
|                  </property> |                  </property> | ||||||
|                  <property name="sizeHint" stdset="0"> |                  <property name="sizeHint" stdset="0"> | ||||||
|                   <size> |                   <size> | ||||||
|  | @ -769,7 +846,7 @@ | ||||||
|            <item> |            <item> | ||||||
|             <spacer name="verticalSpacer_6"> |             <spacer name="verticalSpacer_6"> | ||||||
|              <property name="orientation"> |              <property name="orientation"> | ||||||
|               <enum>Qt::Vertical</enum> |               <enum>Qt::Orientation::Vertical</enum> | ||||||
|              </property> |              </property> | ||||||
|              <property name="sizeHint" stdset="0"> |              <property name="sizeHint" stdset="0"> | ||||||
|               <size> |               <size> | ||||||
|  | @ -786,19 +863,19 @@ | ||||||
|            <item> |            <item> | ||||||
|             <widget class="QFrame" name="frame_4"> |             <widget class="QFrame" name="frame_4"> | ||||||
|              <property name="frameShape"> |              <property name="frameShape"> | ||||||
|               <enum>QFrame::StyledPanel</enum> |               <enum>QFrame::Shape::StyledPanel</enum> | ||||||
|              </property> |              </property> | ||||||
|              <property name="frameShadow"> |              <property name="frameShadow"> | ||||||
|               <enum>QFrame::Plain</enum> |               <enum>QFrame::Shadow::Plain</enum> | ||||||
|              </property> |              </property> | ||||||
|              <layout class="QGridLayout" name="gridLayout_8" columnstretch="2,3"> |              <layout class="QGridLayout" name="gridLayout_8" columnstretch="2,3"> | ||||||
|               <item row="0" column="0"> |               <item row="0" column="0"> | ||||||
|                <widget class="QFrame" name="frame_5"> |                <widget class="QFrame" name="frame_5"> | ||||||
|                 <property name="frameShape"> |                 <property name="frameShape"> | ||||||
|                  <enum>QFrame::StyledPanel</enum> |                  <enum>QFrame::Shape::StyledPanel</enum> | ||||||
|                 </property> |                 </property> | ||||||
|                 <property name="frameShadow"> |                 <property name="frameShadow"> | ||||||
|                  <enum>QFrame::Raised</enum> |                  <enum>QFrame::Shadow::Raised</enum> | ||||||
|                 </property> |                 </property> | ||||||
|                 <layout class="QGridLayout" name="gridLayout_9"> |                 <layout class="QGridLayout" name="gridLayout_9"> | ||||||
|                  <property name="leftMargin"> |                  <property name="leftMargin"> | ||||||
|  | @ -829,10 +906,10 @@ | ||||||
|               <item row="0" column="1"> |               <item row="0" column="1"> | ||||||
|                <widget class="QFrame" name="frame_6"> |                <widget class="QFrame" name="frame_6"> | ||||||
|                 <property name="frameShape"> |                 <property name="frameShape"> | ||||||
|                  <enum>QFrame::StyledPanel</enum> |                  <enum>QFrame::Shape::StyledPanel</enum> | ||||||
|                 </property> |                 </property> | ||||||
|                 <property name="frameShadow"> |                 <property name="frameShadow"> | ||||||
|                  <enum>QFrame::Raised</enum> |                  <enum>QFrame::Shadow::Raised</enum> | ||||||
|                 </property> |                 </property> | ||||||
|                 <layout class="QGridLayout" name="gridLayout_6"> |                 <layout class="QGridLayout" name="gridLayout_6"> | ||||||
|                  <property name="leftMargin"> |                  <property name="leftMargin"> | ||||||
|  | @ -850,7 +927,7 @@ | ||||||
|                  <item row="8" column="0"> |                  <item row="8" column="0"> | ||||||
|                   <spacer name="verticalSpacer_5"> |                   <spacer name="verticalSpacer_5"> | ||||||
|                    <property name="orientation"> |                    <property name="orientation"> | ||||||
|                     <enum>Qt::Vertical</enum> |                     <enum>Qt::Orientation::Vertical</enum> | ||||||
|                    </property> |                    </property> | ||||||
|                    <property name="sizeHint" stdset="0"> |                    <property name="sizeHint" stdset="0"> | ||||||
|                     <size> |                     <size> | ||||||
|  | @ -863,10 +940,10 @@ | ||||||
|                  <item row="7" column="0" colspan="5"> |                  <item row="7" column="0" colspan="5"> | ||||||
|                   <widget class="QFrame" name="frame_7"> |                   <widget class="QFrame" name="frame_7"> | ||||||
|                    <property name="frameShape"> |                    <property name="frameShape"> | ||||||
|                     <enum>QFrame::Panel</enum> |                     <enum>QFrame::Shape::Panel</enum> | ||||||
|                    </property> |                    </property> | ||||||
|                    <property name="frameShadow"> |                    <property name="frameShadow"> | ||||||
|                     <enum>QFrame::Plain</enum> |                     <enum>QFrame::Shadow::Plain</enum> | ||||||
|                    </property> |                    </property> | ||||||
|                    <layout class="QVBoxLayout" name="verticalLayout_7"> |                    <layout class="QVBoxLayout" name="verticalLayout_7"> | ||||||
|                     <item> |                     <item> | ||||||
|  | @ -875,7 +952,7 @@ | ||||||
|                        <string>Tornado Warning expires in 15 minutes</string> |                        <string>Tornado Warning expires in 15 minutes</string> | ||||||
|                       </property> |                       </property> | ||||||
|                       <property name="alignment"> |                       <property name="alignment"> | ||||||
|                        <set>Qt::AlignCenter</set> |                        <set>Qt::AlignmentFlag::AlignCenter</set> | ||||||
|                       </property> |                       </property> | ||||||
|                       <property name="wordWrap"> |                       <property name="wordWrap"> | ||||||
|                        <bool>true</bool> |                        <bool>true</bool> | ||||||
|  | @ -944,7 +1021,7 @@ | ||||||
|                  <item row="6" column="2"> |                  <item row="6" column="2"> | ||||||
|                   <spacer name="horizontalSpacer"> |                   <spacer name="horizontalSpacer"> | ||||||
|                    <property name="orientation"> |                    <property name="orientation"> | ||||||
|                     <enum>Qt::Horizontal</enum> |                     <enum>Qt::Orientation::Horizontal</enum> | ||||||
|                    </property> |                    </property> | ||||||
|                    <property name="sizeHint" stdset="0"> |                    <property name="sizeHint" stdset="0"> | ||||||
|                     <size> |                     <size> | ||||||
|  | @ -974,10 +1051,10 @@ | ||||||
|            <item> |            <item> | ||||||
|             <widget class="QFrame" name="frame_3"> |             <widget class="QFrame" name="frame_3"> | ||||||
|              <property name="frameShape"> |              <property name="frameShape"> | ||||||
|               <enum>QFrame::StyledPanel</enum> |               <enum>QFrame::Shape::StyledPanel</enum> | ||||||
|              </property> |              </property> | ||||||
|              <property name="frameShadow"> |              <property name="frameShadow"> | ||||||
|               <enum>QFrame::Raised</enum> |               <enum>QFrame::Shadow::Raised</enum> | ||||||
|              </property> |              </property> | ||||||
|              <layout class="QGridLayout" name="gridLayout_7"> |              <layout class="QGridLayout" name="gridLayout_7"> | ||||||
|               <property name="leftMargin"> |               <property name="leftMargin"> | ||||||
|  | @ -1058,7 +1135,7 @@ | ||||||
|            <item> |            <item> | ||||||
|             <spacer name="verticalSpacer_4"> |             <spacer name="verticalSpacer_4"> | ||||||
|              <property name="orientation"> |              <property name="orientation"> | ||||||
|               <enum>Qt::Vertical</enum> |               <enum>Qt::Orientation::Vertical</enum> | ||||||
|              </property> |              </property> | ||||||
|              <property name="sizeHint" stdset="0"> |              <property name="sizeHint" stdset="0"> | ||||||
|               <size> |               <size> | ||||||
|  | @ -1080,10 +1157,10 @@ | ||||||
|    <item row="1" column="0"> |    <item row="1" column="0"> | ||||||
|     <widget class="QDialogButtonBox" name="buttonBox"> |     <widget class="QDialogButtonBox" name="buttonBox"> | ||||||
|      <property name="orientation"> |      <property name="orientation"> | ||||||
|       <enum>Qt::Horizontal</enum> |       <enum>Qt::Orientation::Horizontal</enum> | ||||||
|      </property> |      </property> | ||||||
|      <property name="standardButtons"> |      <property name="standardButtons"> | ||||||
|       <set>QDialogButtonBox::Apply|QDialogButtonBox::Cancel|QDialogButtonBox::Discard|QDialogButtonBox::Ok|QDialogButtonBox::RestoreDefaults</set> |       <set>QDialogButtonBox::StandardButton::Apply|QDialogButtonBox::StandardButton::Cancel|QDialogButtonBox::StandardButton::Discard|QDialogButtonBox::StandardButton::Ok|QDialogButtonBox::StandardButton::RestoreDefaults</set> | ||||||
|      </property> |      </property> | ||||||
|     </widget> |     </widget> | ||||||
|    </item> |    </item> | ||||||
|  |  | ||||||
|  | @ -1 +1 @@ | ||||||
| Subproject commit af115273844804d29c502b5ecbb94eee2b4b02f4 | Subproject commit 35e3e40d63bc020dfdc50c438c700c368fdf32fc | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Dan Paulat
						Dan Paulat