mirror of
https://github.com/ciphervance/supercell-wx.git
synced 2025-10-29 21:00:06 +00:00
1549 lines
57 KiB
C++
1549 lines
57 KiB
C++
#include "settings_dialog.hpp"
|
|
#include "ui_settings_dialog.h"
|
|
|
|
#include <scwx/awips/phenomenon.hpp>
|
|
#include <scwx/common/color_table.hpp>
|
|
#include <scwx/qt/config/county_database.hpp>
|
|
#include <scwx/qt/config/radar_site.hpp>
|
|
#include <scwx/qt/manager/media_manager.hpp>
|
|
#include <scwx/qt/manager/position_manager.hpp>
|
|
#include <scwx/qt/manager/settings_manager.hpp>
|
|
#include <scwx/qt/map/map_provider.hpp>
|
|
#include <scwx/qt/settings/audio_settings.hpp>
|
|
#include <scwx/qt/settings/general_settings.hpp>
|
|
#include <scwx/qt/settings/palette_settings.hpp>
|
|
#include <scwx/qt/settings/product_settings.hpp>
|
|
#include <scwx/qt/settings/settings_interface.hpp>
|
|
#include <scwx/qt/settings/text_settings.hpp>
|
|
#include <scwx/qt/settings/unit_settings.hpp>
|
|
#include <scwx/qt/types/alert_types.hpp>
|
|
#include <scwx/qt/types/font_types.hpp>
|
|
#include <scwx/qt/types/location_types.hpp>
|
|
#include <scwx/qt/types/qt_types.hpp>
|
|
#include <scwx/qt/types/text_types.hpp>
|
|
#include <scwx/qt/types/time_types.hpp>
|
|
#include <scwx/qt/types/unit_types.hpp>
|
|
#include <scwx/qt/ui/county_dialog.hpp>
|
|
#include <scwx/qt/ui/custom_layer_dialog.hpp>
|
|
#include <scwx/qt/ui/radar_site_dialog.hpp>
|
|
#include <scwx/qt/ui/serial_port_dialog.hpp>
|
|
#include <scwx/qt/ui/settings/alert_palette_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/wfo_dialog.hpp>
|
|
#include <scwx/qt/util/color.hpp>
|
|
#include <scwx/qt/util/file.hpp>
|
|
#include <scwx/util/logger.hpp>
|
|
#include <scwx/util/threads.hpp>
|
|
|
|
#include <boost/algorithm/string.hpp>
|
|
#include <fmt/format.h>
|
|
#include <QColorDialog>
|
|
#include <QFileDialog>
|
|
#include <QFontDatabase>
|
|
#include <QFontDialog>
|
|
#include <QGeoPositionInfo>
|
|
#include <QPushButton>
|
|
#include <QStandardItemModel>
|
|
#include <QToolButton>
|
|
#include <utility>
|
|
|
|
namespace scwx
|
|
{
|
|
namespace qt
|
|
{
|
|
namespace ui
|
|
{
|
|
|
|
static const std::string logPrefix_ = "scwx::qt::ui::settings_dialog";
|
|
static const auto logger_ = scwx::util::Logger::Create(logPrefix_);
|
|
|
|
struct ColorTableConversions
|
|
{
|
|
uint16_t rangeMin {0u};
|
|
uint16_t rangeMax {255u};
|
|
float offset {0.0f};
|
|
float scale {1.0f};
|
|
};
|
|
|
|
static const std::array<std::pair<std::string, std::string>, 16>
|
|
kColorTableTypes_ {std::pair {"BR", "BR"},
|
|
std::pair {"BV", "BV"},
|
|
std::pair {"SW", "SW"},
|
|
std::pair {"ZDR", "ZDR"},
|
|
std::pair {"PHI2", "KDP (L2)"},
|
|
std::pair {"CC", "CC"},
|
|
std::pair {"DOD", "DOD"},
|
|
std::pair {"DSD", "DSD"},
|
|
std::pair {"ET", "ET"},
|
|
std::pair {"HC", "HC"},
|
|
std::pair {"OHP", "OHP"},
|
|
std::pair {"PHI3", "KDP"},
|
|
std::pair {"SRV", "SRV"},
|
|
std::pair {"STP", "STP"},
|
|
std::pair {"VIL", "VIL"},
|
|
std::pair {"???", "Default"}};
|
|
|
|
// Color table conversions for display, roughly based upon:
|
|
// - ICD for RDA-RPG: Data Moment Characteristics and Conversion for Data Names
|
|
// - ICD for the RPG to Class 1 User
|
|
static const std::unordered_map<std::string, ColorTableConversions>
|
|
kColorTableConversions_ {{"BR", {0u, 255u, 66.0f, 2.0f}},
|
|
{"BV", {0u, 255u, 129.0f, 2.0f}},
|
|
{"SW", {0u, 255u, 129.0f, 2.0f}},
|
|
{"ZDR", {0u, 1058u, 418.0f, 32.0f}},
|
|
{"PHI2", {0u, 1023u, 2.0f, 2.8361f}},
|
|
{"CC", {0u, 255u, -60.5f, 300.0f}},
|
|
{"DOD", {0u, 255u, 128.0f, 1.5f}},
|
|
{"DSD", {0u, 255u, 128.0f, 1.5f}},
|
|
{"ET", {0u, 255u, 2.0f, 1.0f}},
|
|
{"HC", {10u, 160u, 0.0f, 1.0f}},
|
|
{"OHP", {0u, 255u, 0.0f, 2.5f}},
|
|
{"PHI3", {0u, 255u, 43.0f, 20.0f}},
|
|
{"SRV", {0u, 255u, 128.0f, 2.0f}},
|
|
{"STP", {0u, 255u, 0.0f, 1.25f}},
|
|
{"VIL", {0u, 255u, 1.0f, 2.5f}},
|
|
{"???", {0u, 15u, 0.0f, 1.0f}}};
|
|
|
|
class SettingsDialogImpl
|
|
{
|
|
public:
|
|
explicit SettingsDialogImpl(SettingsDialog* self,
|
|
QMapLibre::Settings& mapSettings) :
|
|
self_ {self},
|
|
radarSiteDialog_ {new RadarSiteDialog(self)},
|
|
alertAudioRadarSiteDialog_ {new RadarSiteDialog(self)},
|
|
gpsSourceDialog_ {new SerialPortDialog(self)},
|
|
countyDialog_ {new CountyDialog(self)},
|
|
wfoDialog_ {new WFODialog(self)},
|
|
fontDialog_ {new QFontDialog(self)},
|
|
mapSettings_ {mapSettings},
|
|
fontCategoryModel_ {new QStandardItemModel(self)},
|
|
settings_ {std::initializer_list<settings::SettingsInterfaceBase*> {
|
|
&defaultRadarSite_,
|
|
&gridWidth_,
|
|
&gridHeight_,
|
|
&mapProvider_,
|
|
&mapboxApiKey_,
|
|
&mapTilerApiKey_,
|
|
&theme_,
|
|
&themeFile_,
|
|
&defaultAlertAction_,
|
|
&clockFormat_,
|
|
&customStyleDrawLayer_,
|
|
&customStyleUrl_,
|
|
&defaultTimeZone_,
|
|
&positioningPlugin_,
|
|
&nmeaBaudRate_,
|
|
&nmeaSource_,
|
|
&warningsProvider_,
|
|
&radarSiteThreshold_,
|
|
&antiAliasingEnabled_,
|
|
¢erOnRadarSelection_,
|
|
&showMapAttribution_,
|
|
&showMapCenter_,
|
|
&showMapLogo_,
|
|
&showSmoothedRangeFolding_,
|
|
&updateNotificationsEnabled_,
|
|
&cursorIconAlwaysOn_,
|
|
&cursorIconScale_,
|
|
&debugEnabled_,
|
|
&alertAudioSoundFile_,
|
|
&alertAudioLocationMethod_,
|
|
&alertAudioLatitude_,
|
|
&alertAudioLongitude_,
|
|
&alertAudioRadarSite_,
|
|
&alertAudioRadius_,
|
|
&alertAudioCounty_,
|
|
&alertAudioWFO_,
|
|
&hoverTextWrap_,
|
|
&tooltipMethod_,
|
|
&placefileTextDropShadowEnabled_,
|
|
&radarSiteHoverTextEnabled_}}
|
|
{
|
|
// Configure default alert phenomena colors
|
|
auto& paletteSettings = settings::PaletteSettings::Instance();
|
|
int index = 0;
|
|
|
|
for (auto& phenomenon : settings::PaletteSettings::alert_phenomena())
|
|
{
|
|
QColorDialog::setCustomColor(
|
|
index++,
|
|
QColor(QString::fromStdString(
|
|
paletteSettings.alert_color(phenomenon, true).GetDefault())));
|
|
QColorDialog::setCustomColor(
|
|
index++,
|
|
QColor(QString::fromStdString(
|
|
paletteSettings.alert_color(phenomenon, false).GetDefault())));
|
|
}
|
|
|
|
// Configure font dialog
|
|
fontDialog_->setOptions(
|
|
QFontDialog::FontDialogOption::DontUseNativeDialog |
|
|
QFontDialog::FontDialogOption::ScalableFonts);
|
|
fontDialog_->setWindowModality(Qt::WindowModality::WindowModal);
|
|
}
|
|
~SettingsDialogImpl() = default;
|
|
|
|
void ConnectSignals();
|
|
void SetupGeneralTab();
|
|
void SetupPalettesColorTablesTab();
|
|
void SetupPalettesAlertsTab();
|
|
void SetupUnitsTab();
|
|
void SetupAudioTab();
|
|
void SetupTextTab();
|
|
void SetupHotkeysTab();
|
|
|
|
void UpdateRadarDialogLocation(const std::string& id);
|
|
void UpdateAlertRadarDialogLocation(const std::string& id);
|
|
|
|
QFont GetSelectedFont();
|
|
void SelectFontCategory(types::FontCategory fontCategory);
|
|
void UpdateFontDisplayData();
|
|
|
|
void ApplyChanges();
|
|
void DiscardChanges();
|
|
void ResetToDefault();
|
|
|
|
static QImage
|
|
GenerateColorTableImage(std::shared_ptr<common::ColorTable> colorTable,
|
|
std::uint16_t min,
|
|
std::uint16_t max,
|
|
float offset,
|
|
float scale);
|
|
static void LoadColorTablePreview(const std::string& key,
|
|
const std::string& value,
|
|
QLabel* imageLabel);
|
|
static std::string
|
|
RadarSiteLabel(std::shared_ptr<config::RadarSite>& radarSite);
|
|
|
|
SettingsDialog* self_;
|
|
RadarSiteDialog* radarSiteDialog_;
|
|
RadarSiteDialog* alertAudioRadarSiteDialog_;
|
|
SerialPortDialog* gpsSourceDialog_;
|
|
CountyDialog* countyDialog_;
|
|
WFODialog* wfoDialog_;
|
|
QFontDialog* fontDialog_;
|
|
|
|
QMapLibre::Settings& mapSettings_;
|
|
|
|
QStandardItemModel* fontCategoryModel_;
|
|
|
|
types::FontCategory selectedFontCategory_ {types::FontCategory::Unknown};
|
|
|
|
std::shared_ptr<manager::MediaManager> mediaManager_ {
|
|
manager::MediaManager::Instance()};
|
|
std::shared_ptr<manager::PositionManager> positionManager_ {
|
|
manager::PositionManager::Instance()};
|
|
|
|
std::vector<SettingsPageWidget*> settingsPages_ {};
|
|
AlertPaletteSettingsWidget* alertPaletteSettingsWidget_ {};
|
|
HotkeySettingsWidget* hotkeySettingsWidget_ {};
|
|
UnitSettingsWidget* unitSettingsWidget_ {};
|
|
|
|
settings::SettingsInterface<std::string> defaultRadarSite_ {};
|
|
settings::SettingsInterface<std::int64_t> gridWidth_ {};
|
|
settings::SettingsInterface<std::int64_t> gridHeight_ {};
|
|
settings::SettingsInterface<std::string> mapProvider_ {};
|
|
settings::SettingsInterface<std::string> mapboxApiKey_ {};
|
|
settings::SettingsInterface<std::string> mapTilerApiKey_ {};
|
|
settings::SettingsInterface<std::string> defaultAlertAction_ {};
|
|
settings::SettingsInterface<std::string> clockFormat_ {};
|
|
settings::SettingsInterface<std::string> customStyleDrawLayer_ {};
|
|
settings::SettingsInterface<std::string> customStyleUrl_ {};
|
|
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> themeFile_ {};
|
|
settings::SettingsInterface<std::string> warningsProvider_ {};
|
|
settings::SettingsInterface<double> radarSiteThreshold_ {};
|
|
settings::SettingsInterface<bool> antiAliasingEnabled_ {};
|
|
settings::SettingsInterface<bool> centerOnRadarSelection_ {};
|
|
settings::SettingsInterface<bool> showMapAttribution_ {};
|
|
settings::SettingsInterface<bool> showMapCenter_ {};
|
|
settings::SettingsInterface<bool> showMapLogo_ {};
|
|
settings::SettingsInterface<bool> showSmoothedRangeFolding_ {};
|
|
settings::SettingsInterface<bool> updateNotificationsEnabled_ {};
|
|
settings::SettingsInterface<bool> cursorIconAlwaysOn_ {};
|
|
settings::SettingsInterface<double> cursorIconScale_ {};
|
|
settings::SettingsInterface<bool> debugEnabled_ {};
|
|
|
|
std::unordered_map<std::string, settings::SettingsInterface<std::string>>
|
|
colorTables_ {};
|
|
|
|
settings::SettingsInterface<std::string> alertAudioSoundFile_ {};
|
|
settings::SettingsInterface<std::string> alertAudioLocationMethod_ {};
|
|
settings::SettingsInterface<double> alertAudioLatitude_ {};
|
|
settings::SettingsInterface<double> alertAudioLongitude_ {};
|
|
settings::SettingsInterface<std::string> alertAudioRadarSite_ {};
|
|
settings::SettingsInterface<double> alertAudioRadius_ {};
|
|
settings::SettingsInterface<std::string> alertAudioCounty_ {};
|
|
settings::SettingsInterface<std::string> alertAudioWFO_ {};
|
|
|
|
std::unordered_map<awips::Phenomenon, settings::SettingsInterface<bool>>
|
|
alertAudioEnabled_ {};
|
|
|
|
std::unordered_map<types::FontCategory,
|
|
settings::SettingsInterface<std::string>>
|
|
fontFamilies_ {};
|
|
std::unordered_map<types::FontCategory,
|
|
settings::SettingsInterface<std::string>>
|
|
fontStyles_ {};
|
|
std::unordered_map<types::FontCategory, settings::SettingsInterface<double>>
|
|
fontPointSizes_ {};
|
|
|
|
settings::SettingsInterface<std::int64_t> hoverTextWrap_ {};
|
|
settings::SettingsInterface<std::string> tooltipMethod_ {};
|
|
settings::SettingsInterface<bool> placefileTextDropShadowEnabled_ {};
|
|
settings::SettingsInterface<bool> radarSiteHoverTextEnabled_ {};
|
|
|
|
std::vector<settings::SettingsInterfaceBase*> settings_;
|
|
};
|
|
|
|
SettingsDialog::SettingsDialog(QMapLibre::Settings& mapSettings,
|
|
QWidget* parent) :
|
|
QDialog(parent),
|
|
p {std::make_unique<SettingsDialogImpl>(this, mapSettings)},
|
|
ui(new Ui::SettingsDialog)
|
|
{
|
|
ui->setupUi(this);
|
|
|
|
// Set OK as default
|
|
ui->buttonBox->button(QDialogButtonBox::StandardButton::Ok)
|
|
->setDefault(true);
|
|
|
|
// General
|
|
p->SetupGeneralTab();
|
|
|
|
// Palettes > Color Tables
|
|
p->SetupPalettesColorTablesTab();
|
|
|
|
// Palettes > Alerts
|
|
p->SetupPalettesAlertsTab();
|
|
|
|
// Units
|
|
p->SetupUnitsTab();
|
|
|
|
// Audio
|
|
p->SetupAudioTab();
|
|
|
|
// Text
|
|
p->SetupTextTab();
|
|
|
|
// Hotkeys
|
|
p->SetupHotkeysTab();
|
|
|
|
p->ConnectSignals();
|
|
}
|
|
|
|
SettingsDialog::~SettingsDialog()
|
|
{
|
|
delete ui;
|
|
}
|
|
|
|
void SettingsDialogImpl::ConnectSignals()
|
|
{
|
|
QObject::connect(self_->ui->listWidget,
|
|
&QListWidget::currentRowChanged,
|
|
self_->ui->stackedWidget,
|
|
&QStackedWidget::setCurrentIndex);
|
|
|
|
QObject::connect(self_->ui->radarSiteSelectButton,
|
|
&QAbstractButton::clicked,
|
|
self_,
|
|
[this]() { radarSiteDialog_->show(); });
|
|
|
|
QObject::connect(radarSiteDialog_,
|
|
&RadarSiteDialog::accepted,
|
|
self_,
|
|
[this]()
|
|
{
|
|
std::string id = radarSiteDialog_->radar_site();
|
|
|
|
std::shared_ptr<config::RadarSite> radarSite =
|
|
config::RadarSite::Get(id);
|
|
|
|
if (radarSite != nullptr)
|
|
{
|
|
self_->ui->radarSiteComboBox->setCurrentText(
|
|
QString::fromStdString(RadarSiteLabel(radarSite)));
|
|
}
|
|
});
|
|
|
|
QObject::connect(self_->ui->alertAudioRadarSiteSelectButton,
|
|
&QAbstractButton::clicked,
|
|
self_,
|
|
[this]() { alertAudioRadarSiteDialog_->show(); });
|
|
|
|
QObject::connect(
|
|
alertAudioRadarSiteDialog_,
|
|
&RadarSiteDialog::accepted,
|
|
self_,
|
|
[this]()
|
|
{
|
|
std::string id = alertAudioRadarSiteDialog_->radar_site();
|
|
|
|
std::shared_ptr<config::RadarSite> radarSite =
|
|
config::RadarSite::Get(id);
|
|
|
|
if (radarSite != nullptr)
|
|
{
|
|
self_->ui->alertAudioRadarSiteComboBox->setCurrentText(
|
|
QString::fromStdString(RadarSiteLabel(radarSite)));
|
|
}
|
|
});
|
|
|
|
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
|
|
// radar site
|
|
auto& defaultRadarSite = *defaultRadarSite_.GetSettingsVariable();
|
|
defaultRadarSite.RegisterValueStagedCallback(
|
|
[this](const std::string& newValue)
|
|
{ UpdateRadarDialogLocation(newValue); });
|
|
|
|
QObject::connect(
|
|
self_->ui->alertAudioSoundTestButton,
|
|
&QAbstractButton::clicked,
|
|
self_,
|
|
[this]()
|
|
{
|
|
mediaManager_->Play(
|
|
self_->ui->alertAudioSoundLineEdit->text().toStdString());
|
|
});
|
|
QObject::connect(self_->ui->alertAudioSoundStopButton,
|
|
&QAbstractButton::clicked,
|
|
self_,
|
|
[this]() { mediaManager_->Stop(); });
|
|
|
|
QObject::connect(
|
|
self_->ui->fontListView->selectionModel(),
|
|
&QItemSelectionModel::selectionChanged,
|
|
self_,
|
|
[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;
|
|
}
|
|
|
|
if (selected.size() > 0)
|
|
{
|
|
QModelIndex selectedIndex = selected[0].indexes()[0];
|
|
QVariant variantData =
|
|
self_->ui->fontListView->model()->data(selectedIndex);
|
|
if (variantData.typeId() == QMetaType::QString)
|
|
{
|
|
types::FontCategory fontCategory =
|
|
types::GetFontCategory(variantData.toString().toStdString());
|
|
SelectFontCategory(fontCategory);
|
|
UpdateFontDisplayData();
|
|
}
|
|
}
|
|
});
|
|
|
|
QObject::connect(self_->ui->fontSelectButton,
|
|
&QAbstractButton::clicked,
|
|
self_,
|
|
[this]()
|
|
{
|
|
fontDialog_->setCurrentFont(GetSelectedFont());
|
|
fontDialog_->show();
|
|
});
|
|
|
|
QObject::connect(fontDialog_,
|
|
&QFontDialog::fontSelected,
|
|
self_,
|
|
[this](const QFont& font)
|
|
{
|
|
fontFamilies_.at(selectedFontCategory_)
|
|
.StageValue(font.family().toStdString());
|
|
fontStyles_.at(selectedFontCategory_)
|
|
.StageValue(font.styleName().toStdString());
|
|
fontPointSizes_.at(selectedFontCategory_)
|
|
.StageValue(font.pointSizeF());
|
|
|
|
UpdateFontDisplayData();
|
|
});
|
|
|
|
QObject::connect(self_->ui->resetFontButton,
|
|
&QAbstractButton::clicked,
|
|
self_,
|
|
[this]()
|
|
{
|
|
fontFamilies_.at(selectedFontCategory_).StageDefault();
|
|
fontStyles_.at(selectedFontCategory_).StageDefault();
|
|
fontPointSizes_.at(selectedFontCategory_).StageDefault();
|
|
|
|
UpdateFontDisplayData();
|
|
});
|
|
|
|
QObject::connect(
|
|
self_->ui->buttonBox,
|
|
&QDialogButtonBox::clicked,
|
|
self_,
|
|
[this](QAbstractButton* button)
|
|
{
|
|
QDialogButtonBox::ButtonRole role =
|
|
self_->ui->buttonBox->buttonRole(button);
|
|
|
|
switch (role)
|
|
{
|
|
case QDialogButtonBox::ButtonRole::AcceptRole: // OK
|
|
case QDialogButtonBox::ButtonRole::ApplyRole: // Apply
|
|
ApplyChanges();
|
|
break;
|
|
|
|
case QDialogButtonBox::ButtonRole::DestructiveRole: // Discard
|
|
case QDialogButtonBox::ButtonRole::RejectRole: // Cancel
|
|
DiscardChanges();
|
|
break;
|
|
|
|
case QDialogButtonBox::ButtonRole::ResetRole: // Restore Defaults
|
|
ResetToDefault();
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
});
|
|
}
|
|
|
|
void SettingsDialogImpl::SetupGeneralTab()
|
|
{
|
|
settings::GeneralSettings& generalSettings =
|
|
settings::GeneralSettings::Instance();
|
|
settings::ProductSettings& productSettings =
|
|
settings::ProductSettings::Instance();
|
|
|
|
QObject::connect(
|
|
self_->ui->themeComboBox,
|
|
&QComboBox::currentTextChanged,
|
|
self_,
|
|
[this](const QString& text)
|
|
{
|
|
const types::UiStyle style = types::GetUiStyle(text.toStdString());
|
|
const bool themeFileEnabled = style == types::UiStyle::FusionCustom;
|
|
|
|
self_->ui->themeFileLineEdit->setEnabled(themeFileEnabled);
|
|
self_->ui->themeFileSelectButton->setEnabled(themeFileEnabled);
|
|
self_->ui->resetThemeFileButton->setEnabled(themeFileEnabled);
|
|
});
|
|
|
|
theme_.SetSettingsVariable(generalSettings.theme());
|
|
SCWX_SETTINGS_COMBO_BOX(theme_,
|
|
self_->ui->themeComboBox,
|
|
types::UiStyleIterator(),
|
|
types::GetUiStyleName);
|
|
theme_.SetResetButton(self_->ui->resetThemeButton);
|
|
|
|
themeFile_.SetSettingsVariable(generalSettings.theme_file());
|
|
themeFile_.SetEditWidget(self_->ui->themeFileLineEdit);
|
|
themeFile_.SetResetButton(self_->ui->resetThemeFileButton);
|
|
themeFile_.EnableTrimming();
|
|
|
|
QObject::connect(
|
|
self_->ui->themeFileSelectButton,
|
|
&QAbstractButton::clicked,
|
|
self_,
|
|
[this]()
|
|
{
|
|
static const std::string themeFilter = "Qt6Ct Theme File (*.conf)";
|
|
static const std::string allFilter = "All Files (*)";
|
|
|
|
QFileDialog* dialog = new QFileDialog(self_);
|
|
|
|
dialog->setFileMode(QFileDialog::ExistingFile);
|
|
|
|
dialog->setNameFilters(
|
|
{QObject::tr(themeFilter.c_str()), QObject::tr(allFilter.c_str())});
|
|
dialog->setAttribute(Qt::WA_DeleteOnClose);
|
|
|
|
QObject::connect(
|
|
dialog,
|
|
&QFileDialog::fileSelected,
|
|
self_,
|
|
[this](const QString& file)
|
|
{
|
|
QString path = QDir::toNativeSeparators(file);
|
|
logger_->info("Selected theme file: {}", path.toStdString());
|
|
self_->ui->themeFileLineEdit->setText(path);
|
|
|
|
// setText does not emit the textEdited signal
|
|
Q_EMIT self_->ui->themeFileLineEdit->textEdited(path);
|
|
});
|
|
|
|
dialog->open();
|
|
});
|
|
|
|
auto radarSites = config::RadarSite::GetAll();
|
|
|
|
// Sort radar sites by ID
|
|
std::sort(radarSites.begin(),
|
|
radarSites.end(),
|
|
[](const std::shared_ptr<config::RadarSite>& a,
|
|
const std::shared_ptr<config::RadarSite>& b)
|
|
{ return a->id() < b->id(); });
|
|
|
|
// Add default and follow options to radar sites
|
|
self_->ui->alertAudioRadarSiteComboBox->addItem(
|
|
QString::fromStdString("default"));
|
|
self_->ui->alertAudioRadarSiteComboBox->addItem(
|
|
QString::fromStdString("follow"));
|
|
|
|
// Add sorted radar sites
|
|
for (std::shared_ptr<config::RadarSite>& radarSite : radarSites)
|
|
{
|
|
QString text = QString::fromStdString(RadarSiteLabel(radarSite));
|
|
self_->ui->radarSiteComboBox->addItem(text);
|
|
self_->ui->alertAudioRadarSiteComboBox->addItem(text);
|
|
}
|
|
|
|
defaultRadarSite_.SetSettingsVariable(generalSettings.default_radar_site());
|
|
defaultRadarSite_.SetMapFromValueFunction(
|
|
[](const std::string& id) -> std::string
|
|
{
|
|
// Get the radar site associated with the ID
|
|
std::shared_ptr<config::RadarSite> radarSite =
|
|
config::RadarSite::Get(id);
|
|
|
|
if (radarSite == nullptr)
|
|
{
|
|
// No radar site found, just return the ID
|
|
return id;
|
|
}
|
|
|
|
// Add location details to the radar site
|
|
return RadarSiteLabel(radarSite);
|
|
});
|
|
defaultRadarSite_.SetMapToValueFunction(
|
|
[](const std::string& text) -> std::string
|
|
{
|
|
// Find the position of location details
|
|
size_t pos = text.find(" (");
|
|
|
|
if (pos == std::string::npos)
|
|
{
|
|
// No location details found, just return the text
|
|
return text;
|
|
}
|
|
|
|
// Remove location details from the radar site
|
|
return text.substr(0, pos);
|
|
});
|
|
defaultRadarSite_.SetEditWidget(self_->ui->radarSiteComboBox);
|
|
defaultRadarSite_.SetResetButton(self_->ui->resetRadarSiteButton);
|
|
UpdateRadarDialogLocation(generalSettings.default_radar_site().GetValue());
|
|
|
|
gridWidth_.SetSettingsVariable(generalSettings.grid_width());
|
|
gridWidth_.SetEditWidget(self_->ui->gridWidthSpinBox);
|
|
gridWidth_.SetResetButton(self_->ui->resetGridWidthButton);
|
|
|
|
gridHeight_.SetSettingsVariable(generalSettings.grid_height());
|
|
gridHeight_.SetEditWidget(self_->ui->gridHeightSpinBox);
|
|
gridHeight_.SetResetButton(self_->ui->resetGridHeightButton);
|
|
|
|
mapProvider_.SetSettingsVariable(generalSettings.map_provider());
|
|
SCWX_SETTINGS_COMBO_BOX(mapProvider_,
|
|
self_->ui->mapProviderComboBox,
|
|
map::MapProviderIterator(),
|
|
map::GetMapProviderName);
|
|
mapProvider_.SetResetButton(self_->ui->resetMapProviderButton);
|
|
|
|
self_->ui->mapboxApiKeyLineEdit->setMapProvider(map::MapProvider::Mapbox);
|
|
mapboxApiKey_.SetSettingsVariable(generalSettings.mapbox_api_key());
|
|
mapboxApiKey_.SetEditWidget(self_->ui->mapboxApiKeyLineEdit);
|
|
mapboxApiKey_.SetResetButton(self_->ui->resetMapboxApiKeyButton);
|
|
mapboxApiKey_.EnableTrimming();
|
|
|
|
self_->ui->mapTilerApiKeyLineEdit->setMapProvider(
|
|
map::MapProvider::MapTiler);
|
|
mapTilerApiKey_.SetSettingsVariable(generalSettings.maptiler_api_key());
|
|
mapTilerApiKey_.SetEditWidget(self_->ui->mapTilerApiKeyLineEdit);
|
|
mapTilerApiKey_.SetResetButton(self_->ui->resetMapTilerApiKeyButton);
|
|
mapTilerApiKey_.EnableTrimming();
|
|
|
|
customStyleUrl_.SetSettingsVariable(generalSettings.custom_style_url());
|
|
customStyleUrl_.SetEditWidget(self_->ui->customMapUrlLineEdit);
|
|
customStyleUrl_.SetResetButton(self_->ui->resetCustomMapUrlButton);
|
|
customStyleUrl_.SetInvalidTooltip(
|
|
"Remove anything following \"?key=\" in the URL");
|
|
customStyleUrl_.EnableTrimming();
|
|
|
|
customStyleDrawLayer_.SetSettingsVariable(
|
|
generalSettings.custom_style_draw_layer());
|
|
customStyleDrawLayer_.SetEditWidget(self_->ui->customMapLayerLineEdit);
|
|
customStyleDrawLayer_.SetResetButton(self_->ui->resetCustomMapLayerButton);
|
|
QObject::connect(
|
|
self_->ui->customMapLayerToolButton,
|
|
&QAbstractButton::clicked,
|
|
self_,
|
|
[this]()
|
|
{
|
|
// WA_DeleteOnClose manages memory
|
|
// NOLINTNEXTLINE(cppcoreguidelines-owning-memory)
|
|
auto* customLayerDialog = new ui::CustomLayerDialog(mapSettings_);
|
|
customLayerDialog->setAttribute(Qt::WA_DeleteOnClose);
|
|
QObject::connect(
|
|
customLayerDialog,
|
|
&QDialog::accepted,
|
|
self_,
|
|
[this, customLayerDialog]()
|
|
{
|
|
auto newLayer = customLayerDialog->selected_layer();
|
|
self_->ui->customMapLayerLineEdit->setText(newLayer.c_str());
|
|
// setText does not emit the textEdited signal
|
|
Q_EMIT
|
|
self_->ui->customMapLayerLineEdit->textEdited(newLayer.c_str());
|
|
});
|
|
|
|
customLayerDialog->open();
|
|
});
|
|
|
|
defaultAlertAction_.SetSettingsVariable(
|
|
generalSettings.default_alert_action());
|
|
SCWX_SETTINGS_COMBO_BOX(defaultAlertAction_,
|
|
self_->ui->defaultAlertActionComboBox,
|
|
types::AlertActionIterator(),
|
|
types::GetAlertActionName);
|
|
defaultAlertAction_.SetResetButton(self_->ui->resetDefaultAlertActionButton);
|
|
|
|
clockFormat_.SetSettingsVariable(generalSettings.clock_format());
|
|
SCWX_SETTINGS_COMBO_BOX(clockFormat_,
|
|
self_->ui->clockFormatComboBox,
|
|
scwx::util::ClockFormatIterator(),
|
|
scwx::util::GetClockFormatName);
|
|
clockFormat_.SetResetButton(self_->ui->resetClockFormatButton);
|
|
|
|
defaultTimeZone_.SetSettingsVariable(generalSettings.default_time_zone());
|
|
SCWX_SETTINGS_COMBO_BOX(defaultTimeZone_,
|
|
self_->ui->defaultTimeZoneComboBox,
|
|
types::DefaultTimeZoneIterator(),
|
|
types::GetDefaultTimeZoneName);
|
|
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);
|
|
nmeaSource_.EnableTrimming();
|
|
|
|
warningsProvider_.SetSettingsVariable(generalSettings.warnings_provider());
|
|
warningsProvider_.SetEditWidget(self_->ui->warningsProviderLineEdit);
|
|
warningsProvider_.SetResetButton(self_->ui->resetWarningsProviderButton);
|
|
warningsProvider_.EnableTrimming();
|
|
|
|
radarSiteThreshold_.SetSettingsVariable(
|
|
generalSettings.radar_site_threshold());
|
|
radarSiteThreshold_.SetEditWidget(self_->ui->radarSiteThresholdSpinBox);
|
|
radarSiteThreshold_.SetResetButton(self_->ui->resetRadarSiteThresholdButton);
|
|
radarSiteThreshold_.SetUnitLabel(self_->ui->radarSiteThresholdUnitLabel);
|
|
auto radarSiteThresholdUpdateUnits = [this](const std::string& newValue)
|
|
{
|
|
const types::DistanceUnits radiusUnits =
|
|
types::GetDistanceUnitsFromName(newValue);
|
|
const double radiusScale = types::GetDistanceUnitsScale(radiusUnits);
|
|
const std::string abbreviation =
|
|
types::GetDistanceUnitsAbbreviation(radiusUnits);
|
|
|
|
radarSiteThreshold_.SetUnit(radiusScale, abbreviation);
|
|
};
|
|
settings::UnitSettings::Instance()
|
|
.distance_units()
|
|
.RegisterValueStagedCallback(radarSiteThresholdUpdateUnits);
|
|
radarSiteThresholdUpdateUnits(
|
|
settings::UnitSettings::Instance().distance_units().GetValue());
|
|
|
|
cursorIconScale_.SetSettingsVariable(generalSettings.cursor_icon_scale());
|
|
cursorIconScale_.SetEditWidget(self_->ui->cursorIconScaleSpinBox);
|
|
cursorIconScale_.SetResetButton(self_->ui->resetCursorIconScaleButton);
|
|
|
|
antiAliasingEnabled_.SetSettingsVariable(
|
|
generalSettings.anti_aliasing_enabled());
|
|
antiAliasingEnabled_.SetEditWidget(self_->ui->antiAliasingEnabledCheckBox);
|
|
|
|
centerOnRadarSelection_.SetSettingsVariable(
|
|
generalSettings.center_on_radar_selection());
|
|
centerOnRadarSelection_.SetEditWidget(
|
|
self_->ui->centerOnRadarSelectionCheckBox);
|
|
|
|
showMapAttribution_.SetSettingsVariable(
|
|
generalSettings.show_map_attribution());
|
|
showMapAttribution_.SetEditWidget(self_->ui->showMapAttributionCheckBox);
|
|
|
|
showMapCenter_.SetSettingsVariable(generalSettings.show_map_center());
|
|
showMapCenter_.SetEditWidget(self_->ui->showMapCenterCheckBox);
|
|
|
|
showMapLogo_.SetSettingsVariable(generalSettings.show_map_logo());
|
|
showMapLogo_.SetEditWidget(self_->ui->showMapLogoCheckBox);
|
|
|
|
showSmoothedRangeFolding_.SetSettingsVariable(
|
|
productSettings.show_smoothed_range_folding());
|
|
showSmoothedRangeFolding_.SetEditWidget(
|
|
self_->ui->showSmoothedRangeFoldingCheckBox);
|
|
|
|
updateNotificationsEnabled_.SetSettingsVariable(
|
|
generalSettings.update_notifications_enabled());
|
|
updateNotificationsEnabled_.SetEditWidget(
|
|
self_->ui->enableUpdateNotificationsCheckBox);
|
|
|
|
cursorIconAlwaysOn_.SetSettingsVariable(
|
|
generalSettings.cursor_icon_always_on());
|
|
cursorIconAlwaysOn_.SetEditWidget(self_->ui->cursorIconAlwaysOnCheckBox);
|
|
|
|
debugEnabled_.SetSettingsVariable(generalSettings.debug_enabled());
|
|
debugEnabled_.SetEditWidget(self_->ui->debugEnabledCheckBox);
|
|
}
|
|
|
|
void SettingsDialogImpl::SetupPalettesColorTablesTab()
|
|
{
|
|
settings::PaletteSettings& paletteSettings =
|
|
settings::PaletteSettings::Instance();
|
|
|
|
// Palettes > Color Tables
|
|
QGridLayout* colorTableLayout =
|
|
reinterpret_cast<QGridLayout*>(self_->ui->colorTableContents->layout());
|
|
|
|
colorTables_.reserve(kColorTableTypes_.size());
|
|
|
|
int colorTableRow = 0;
|
|
for (auto& colorTableType : kColorTableTypes_)
|
|
{
|
|
QLabel* imageLabel = new QLabel(self_);
|
|
QLineEdit* lineEdit = new QLineEdit(self_);
|
|
QToolButton* openFileButton = new QToolButton(self_);
|
|
QToolButton* resetButton = new QToolButton(self_);
|
|
|
|
openFileButton->setText(QObject::tr("..."));
|
|
|
|
resetButton->setIcon(
|
|
QIcon {":/res/icons/font-awesome-6/rotate-left-solid.svg"});
|
|
resetButton->setVisible(false);
|
|
|
|
imageLabel->setFrameShape(QFrame::Shape::Box);
|
|
imageLabel->setFrameShadow(QFrame::Shadow::Plain);
|
|
imageLabel->setVisible(false);
|
|
|
|
colorTableLayout->addWidget(
|
|
new QLabel(colorTableType.second.c_str(), self_), colorTableRow, 0);
|
|
colorTableLayout->addWidget(imageLabel, colorTableRow, 1);
|
|
colorTableLayout->addWidget(lineEdit, colorTableRow, 2);
|
|
colorTableLayout->addWidget(openFileButton, colorTableRow, 3);
|
|
colorTableLayout->addWidget(resetButton, colorTableRow, 4);
|
|
++colorTableRow;
|
|
|
|
// Create settings interface
|
|
auto result = colorTables_.emplace(
|
|
colorTableType.first, settings::SettingsInterface<std::string> {});
|
|
auto& pair = *result.first;
|
|
auto& colorTable = pair.second;
|
|
|
|
// Add to settings list
|
|
settings_.push_back(&colorTable);
|
|
|
|
auto& colorTableVariable = paletteSettings.palette(colorTableType.first);
|
|
colorTable.SetSettingsVariable(colorTableVariable);
|
|
colorTable.SetEditWidget(lineEdit);
|
|
colorTable.SetResetButton(resetButton);
|
|
colorTable.EnableTrimming();
|
|
|
|
colorTableVariable.RegisterValueStagedCallback(
|
|
[colorTableType, imageLabel](const std::string& value)
|
|
{ LoadColorTablePreview(colorTableType.first, value, imageLabel); });
|
|
|
|
LoadColorTablePreview(
|
|
colorTableType.first, colorTableVariable.GetValue(), imageLabel);
|
|
|
|
QObject::connect(
|
|
openFileButton,
|
|
&QAbstractButton::clicked,
|
|
self_,
|
|
[this, lineEdit]()
|
|
{
|
|
static const std::string paletteFilter = "Color Palettes (*.pal)";
|
|
static const std::string allFilter = "All Files (*)";
|
|
|
|
QFileDialog* dialog = new QFileDialog(self_);
|
|
|
|
dialog->setFileMode(QFileDialog::ExistingFile);
|
|
dialog->setNameFilters({QObject::tr(paletteFilter.c_str()),
|
|
QObject::tr(allFilter.c_str())});
|
|
dialog->setAttribute(Qt::WA_DeleteOnClose);
|
|
|
|
QObject::connect(dialog,
|
|
&QFileDialog::fileSelected,
|
|
self_,
|
|
[lineEdit](const QString& file)
|
|
{
|
|
QString path = QDir::toNativeSeparators(file);
|
|
|
|
logger_->info("Selected palette: {}",
|
|
path.toStdString());
|
|
lineEdit->setText(path);
|
|
|
|
// setText does not emit the textEdited signal
|
|
Q_EMIT lineEdit->textEdited(path);
|
|
});
|
|
|
|
dialog->open();
|
|
});
|
|
}
|
|
}
|
|
|
|
void SettingsDialogImpl::SetupPalettesAlertsTab()
|
|
{
|
|
// Palettes > Alerts
|
|
QVBoxLayout* layout = new QVBoxLayout(self_->ui->alertsPalette);
|
|
|
|
alertPaletteSettingsWidget_ =
|
|
new AlertPaletteSettingsWidget(self_->ui->hotkeys);
|
|
layout->addWidget(alertPaletteSettingsWidget_);
|
|
|
|
settingsPages_.push_back(alertPaletteSettingsWidget_);
|
|
}
|
|
|
|
void SettingsDialogImpl::SetupUnitsTab()
|
|
{
|
|
QVBoxLayout* layout = new QVBoxLayout(self_->ui->units);
|
|
|
|
unitSettingsWidget_ = new UnitSettingsWidget(self_->ui->units);
|
|
layout->addWidget(unitSettingsWidget_);
|
|
|
|
settingsPages_.push_back(unitSettingsWidget_);
|
|
}
|
|
|
|
void SettingsDialogImpl::SetupAudioTab()
|
|
{
|
|
QObject::connect(
|
|
self_->ui->alertAudioLocationMethodComboBox,
|
|
&QComboBox::currentTextChanged,
|
|
self_,
|
|
[this](const QString& text)
|
|
{
|
|
types::LocationMethod locationMethod =
|
|
types::GetLocationMethod(text.toStdString());
|
|
|
|
bool coordinateEntryEnabled =
|
|
locationMethod == types::LocationMethod::Fixed;
|
|
bool radarSiteEntryEnable =
|
|
locationMethod == types::LocationMethod::RadarSite;
|
|
bool radiusEntryEnable =
|
|
locationMethod == types::LocationMethod::Fixed ||
|
|
locationMethod == types::LocationMethod::Track ||
|
|
locationMethod == types::LocationMethod::RadarSite;
|
|
bool countyEntryEnabled =
|
|
locationMethod == types::LocationMethod::County;
|
|
bool wfoEntryEnabled = locationMethod == types::LocationMethod::WFO;
|
|
|
|
self_->ui->alertAudioLatitudeSpinBox->setEnabled(
|
|
coordinateEntryEnabled);
|
|
self_->ui->alertAudioLongitudeSpinBox->setEnabled(
|
|
coordinateEntryEnabled);
|
|
self_->ui->resetAlertAudioLatitudeButton->setEnabled(
|
|
coordinateEntryEnabled);
|
|
self_->ui->resetAlertAudioLongitudeButton->setEnabled(
|
|
coordinateEntryEnabled);
|
|
|
|
self_->ui->alertAudioRadarSiteComboBox->setEnabled(
|
|
radarSiteEntryEnable);
|
|
self_->ui->alertAudioRadarSiteSelectButton->setEnabled(
|
|
radarSiteEntryEnable);
|
|
self_->ui->resetAlertAudioRadarSiteButton->setEnabled(
|
|
radarSiteEntryEnable);
|
|
|
|
self_->ui->alertAudioRadiusSpinBox->setEnabled(radiusEntryEnable);
|
|
self_->ui->resetAlertAudioRadiusButton->setEnabled(radiusEntryEnable);
|
|
|
|
self_->ui->alertAudioCountyLineEdit->setEnabled(countyEntryEnabled);
|
|
self_->ui->alertAudioCountySelectButton->setEnabled(
|
|
countyEntryEnabled);
|
|
self_->ui->resetAlertAudioCountyButton->setEnabled(countyEntryEnabled);
|
|
|
|
self_->ui->alertAudioWFOLineEdit->setEnabled(wfoEntryEnabled);
|
|
self_->ui->alertAudioWFOSelectButton->setEnabled(wfoEntryEnabled);
|
|
self_->ui->resetAlertAudioWFOButton->setEnabled(wfoEntryEnabled);
|
|
});
|
|
|
|
settings::AudioSettings& audioSettings = settings::AudioSettings::Instance();
|
|
|
|
alertAudioSoundFile_.SetSettingsVariable(audioSettings.alert_sound_file());
|
|
alertAudioSoundFile_.SetEditWidget(self_->ui->alertAudioSoundLineEdit);
|
|
alertAudioSoundFile_.SetResetButton(self_->ui->resetAlertAudioSoundButton);
|
|
alertAudioSoundFile_.EnableTrimming();
|
|
|
|
QObject::connect(
|
|
self_->ui->alertAudioSoundSelectButton,
|
|
&QAbstractButton::clicked,
|
|
self_,
|
|
[this]()
|
|
{
|
|
static const std::string audioFilter =
|
|
"Audio Files (*.3ga *.669 *.a52 *.aac *.ac3 *.adt *.adts *.aif "
|
|
"*.aifc *.aiff *.amb *.amr *.aob *.ape *.au *.awb *.caf *.dts "
|
|
"*.flac *.it *.kar *.m4a *.m4b *.m4p *.m5p *.mid *.mka *.mlp *.mod "
|
|
"*.mpa *.mp1 *.mp2 *.mp3 *.mpc *.mpga *.mus *.oga *.ogg *.oma "
|
|
"*.opus *.qcp *.ra *.rmi *.s3m *.sid *.spx *.tak *.thd *.tta *.voc "
|
|
"*.vqf *.w64 *.wav *.wma *.wv *.xa *.xm)";
|
|
static const std::string allFilter = "All Files (*)";
|
|
|
|
QFileDialog* dialog = new QFileDialog(self_);
|
|
|
|
dialog->setFileMode(QFileDialog::ExistingFile);
|
|
dialog->setNameFilters(
|
|
{QObject::tr(audioFilter.c_str()), QObject::tr(allFilter.c_str())});
|
|
dialog->setAttribute(Qt::WA_DeleteOnClose);
|
|
|
|
QObject::connect(
|
|
dialog,
|
|
&QFileDialog::fileSelected,
|
|
self_,
|
|
[this](const QString& file)
|
|
{
|
|
QString path = QDir::toNativeSeparators(file);
|
|
|
|
logger_->info("Selected alert sound file: {}",
|
|
path.toStdString());
|
|
self_->ui->alertAudioSoundLineEdit->setText(path);
|
|
|
|
// setText does not emit the textEdited signal
|
|
Q_EMIT self_->ui->alertAudioSoundLineEdit->textEdited(path);
|
|
});
|
|
|
|
dialog->open();
|
|
});
|
|
|
|
alertAudioLocationMethod_.SetSettingsVariable(
|
|
audioSettings.alert_location_method());
|
|
SCWX_SETTINGS_COMBO_BOX(alertAudioLocationMethod_,
|
|
self_->ui->alertAudioLocationMethodComboBox,
|
|
types::LocationMethodIterator(),
|
|
types::GetLocationMethodName);
|
|
alertAudioLocationMethod_.SetResetButton(
|
|
self_->ui->resetAlertAudioLocationMethodButton);
|
|
|
|
alertAudioLatitude_.SetSettingsVariable(audioSettings.alert_latitude());
|
|
alertAudioLatitude_.SetEditWidget(self_->ui->alertAudioLatitudeSpinBox);
|
|
alertAudioLatitude_.SetResetButton(self_->ui->resetAlertAudioLatitudeButton);
|
|
|
|
alertAudioLongitude_.SetSettingsVariable(audioSettings.alert_longitude());
|
|
alertAudioLongitude_.SetEditWidget(self_->ui->alertAudioLongitudeSpinBox);
|
|
alertAudioLongitude_.SetResetButton(
|
|
self_->ui->resetAlertAudioLongitudeButton);
|
|
|
|
alertAudioRadarSite_.SetSettingsVariable(audioSettings.alert_radar_site());
|
|
alertAudioRadarSite_.SetMapFromValueFunction(
|
|
[](const std::string& id) -> std::string
|
|
{
|
|
// Get the radar site associated with the ID
|
|
std::shared_ptr<config::RadarSite> radarSite =
|
|
config::RadarSite::Get(id);
|
|
|
|
if (radarSite == nullptr)
|
|
{
|
|
// No radar site found, just return the ID
|
|
return id;
|
|
}
|
|
|
|
// Add location details to the radar site
|
|
return RadarSiteLabel(radarSite);
|
|
});
|
|
alertAudioRadarSite_.SetMapToValueFunction(
|
|
[](const std::string& text) -> std::string
|
|
{
|
|
// Find the position of location details
|
|
size_t pos = text.find(" (");
|
|
|
|
if (pos == std::string::npos)
|
|
{
|
|
// No location details found, just return the text
|
|
return text;
|
|
}
|
|
|
|
// Remove location details from the radar site
|
|
return text.substr(0, pos);
|
|
});
|
|
alertAudioRadarSite_.SetEditWidget(self_->ui->alertAudioRadarSiteComboBox);
|
|
alertAudioRadarSite_.SetResetButton(
|
|
self_->ui->resetAlertAudioRadarSiteButton);
|
|
UpdateAlertRadarDialogLocation(audioSettings.alert_radar_site().GetValue());
|
|
|
|
alertAudioRadius_.SetSettingsVariable(audioSettings.alert_radius());
|
|
alertAudioRadius_.SetEditWidget(self_->ui->alertAudioRadiusSpinBox);
|
|
alertAudioRadius_.SetResetButton(self_->ui->resetAlertAudioRadiusButton);
|
|
alertAudioRadius_.SetUnitLabel(self_->ui->alertAudioRadiusUnitsLabel);
|
|
auto alertAudioRadiusUpdateUnits = [this](const std::string& newValue)
|
|
{
|
|
const types::DistanceUnits radiusUnits =
|
|
types::GetDistanceUnitsFromName(newValue);
|
|
const double radiusScale = types::GetDistanceUnitsScale(radiusUnits);
|
|
const std::string abbreviation =
|
|
types::GetDistanceUnitsAbbreviation(radiusUnits);
|
|
|
|
alertAudioRadius_.SetUnit(radiusScale, abbreviation);
|
|
};
|
|
settings::UnitSettings::Instance()
|
|
.distance_units()
|
|
.RegisterValueStagedCallback(alertAudioRadiusUpdateUnits);
|
|
alertAudioRadiusUpdateUnits(
|
|
settings::UnitSettings::Instance().distance_units().GetValue());
|
|
|
|
auto& alertAudioPhenomena = types::GetAlertAudioPhenomena();
|
|
auto alertAudioLayout =
|
|
static_cast<QGridLayout*>(self_->ui->alertAudioGroupBox->layout());
|
|
|
|
alertAudioEnabled_.reserve(alertAudioPhenomena.size());
|
|
|
|
for (const auto& phenomenon : alertAudioPhenomena)
|
|
{
|
|
QCheckBox* alertAudioCheckbox = new QCheckBox(self_);
|
|
alertAudioCheckbox->setText(
|
|
QString::fromStdString(awips::GetPhenomenonText(phenomenon)));
|
|
|
|
static_cast<QGridLayout*>(self_->ui->alertAudioGroupBox->layout())
|
|
->addWidget(
|
|
alertAudioCheckbox, alertAudioLayout->rowCount(), 0, 1, -1);
|
|
|
|
// Create settings interface
|
|
auto result = alertAudioEnabled_.emplace(
|
|
phenomenon, settings::SettingsInterface<bool> {});
|
|
auto& alertAudioEnabled = result.first->second;
|
|
|
|
// Add to settings list
|
|
settings_.push_back(&alertAudioEnabled);
|
|
|
|
alertAudioEnabled.SetSettingsVariable(
|
|
audioSettings.alert_enabled(phenomenon));
|
|
alertAudioEnabled.SetEditWidget(alertAudioCheckbox);
|
|
}
|
|
|
|
QObject::connect(
|
|
positionManager_.get(),
|
|
&manager::PositionManager::PositionUpdated,
|
|
self_,
|
|
[this](const QGeoPositionInfo& info)
|
|
{
|
|
settings::AudioSettings& audioSettings =
|
|
settings::AudioSettings::Instance();
|
|
|
|
if (info.isValid() &&
|
|
types::GetLocationMethod(
|
|
audioSettings.alert_location_method().GetValue()) ==
|
|
types::LocationMethod::Track)
|
|
{
|
|
QGeoCoordinate coordinate = info.coordinate();
|
|
self_->ui->alertAudioLatitudeSpinBox->setValue(
|
|
coordinate.latitude());
|
|
self_->ui->alertAudioLongitudeSpinBox->setValue(
|
|
coordinate.longitude());
|
|
}
|
|
});
|
|
|
|
QObject::connect(
|
|
self_->ui->alertAudioCountySelectButton,
|
|
&QAbstractButton::clicked,
|
|
self_,
|
|
[this]()
|
|
{
|
|
std::string countyId =
|
|
self_->ui->alertAudioCountyLineEdit->text().toStdString();
|
|
|
|
if (countyId.length() >= 2)
|
|
{
|
|
countyDialog_->SelectState(countyId.substr(0, 2));
|
|
}
|
|
|
|
countyDialog_->show();
|
|
});
|
|
QObject::connect(countyDialog_,
|
|
&CountyDialog::accepted,
|
|
self_,
|
|
[this]()
|
|
{
|
|
std::string countyId = countyDialog_->county_fips_id();
|
|
QString qCountyId = QString::fromStdString(countyId);
|
|
self_->ui->alertAudioCountyLineEdit->setText(qCountyId);
|
|
|
|
// setText does not emit the textEdited signal
|
|
Q_EMIT self_->ui->alertAudioCountyLineEdit->textEdited(
|
|
qCountyId);
|
|
});
|
|
QObject::connect(self_->ui->alertAudioCountyLineEdit,
|
|
&QLineEdit::textChanged,
|
|
self_,
|
|
[this](const QString& text)
|
|
{
|
|
std::string countyName =
|
|
config::CountyDatabase::GetCountyName(
|
|
text.toStdString());
|
|
self_->ui->alertAudioCountyLabel->setText(
|
|
QString::fromStdString(countyName));
|
|
});
|
|
|
|
alertAudioCounty_.SetSettingsVariable(audioSettings.alert_county());
|
|
alertAudioCounty_.SetEditWidget(self_->ui->alertAudioCountyLineEdit);
|
|
alertAudioCounty_.SetResetButton(self_->ui->resetAlertAudioCountyButton);
|
|
alertAudioCounty_.EnableTrimming();
|
|
|
|
QObject::connect(self_->ui->alertAudioWFOSelectButton,
|
|
&QAbstractButton::clicked,
|
|
self_,
|
|
[this]() { wfoDialog_->show(); });
|
|
QObject::connect(wfoDialog_,
|
|
&WFODialog::accepted,
|
|
self_,
|
|
[this]()
|
|
{
|
|
std::string wfoId = wfoDialog_->wfo_id();
|
|
QString qWFOId = QString::fromStdString(wfoId);
|
|
self_->ui->alertAudioWFOLineEdit->setText(qWFOId);
|
|
|
|
// setText does not emit the textEdited signal
|
|
Q_EMIT self_->ui->alertAudioWFOLineEdit->textEdited(
|
|
qWFOId);
|
|
});
|
|
QObject::connect(self_->ui->alertAudioWFOLineEdit,
|
|
&QLineEdit::textChanged,
|
|
self_,
|
|
[this](const QString& text)
|
|
{
|
|
std::string wfoName = config::CountyDatabase::GetWFOName(
|
|
text.toStdString());
|
|
self_->ui->alertAudioWFOLabel->setText(
|
|
QString::fromStdString(wfoName));
|
|
});
|
|
|
|
alertAudioWFO_.SetSettingsVariable(audioSettings.alert_wfo());
|
|
alertAudioWFO_.SetEditWidget(self_->ui->alertAudioWFOLineEdit);
|
|
alertAudioWFO_.SetResetButton(self_->ui->resetAlertAudioWFOButton);
|
|
alertAudioWFO_.EnableTrimming();
|
|
}
|
|
|
|
void SettingsDialogImpl::SetupTextTab()
|
|
{
|
|
settings::TextSettings& textSettings = settings::TextSettings::Instance();
|
|
|
|
self_->ui->fontListView->setModel(fontCategoryModel_);
|
|
|
|
const auto fontCategoryCount = types::FontCategoryIterator().count();
|
|
fontFamilies_.reserve(fontCategoryCount);
|
|
fontStyles_.reserve(fontCategoryCount);
|
|
fontPointSizes_.reserve(fontCategoryCount);
|
|
|
|
for (const auto& fontCategory : types::FontCategoryIterator())
|
|
{
|
|
// Add font category to list view
|
|
fontCategoryModel_->appendRow(new QStandardItem(
|
|
QString::fromStdString(types::GetFontCategoryName(fontCategory))));
|
|
|
|
// Create settings interface
|
|
auto fontFamilyResult = fontFamilies_.emplace(
|
|
fontCategory, settings::SettingsInterface<std::string> {});
|
|
auto fontStyleResult = fontStyles_.emplace(
|
|
fontCategory, settings::SettingsInterface<std::string> {});
|
|
auto fontSizeResult = fontPointSizes_.emplace(
|
|
fontCategory, settings::SettingsInterface<double> {});
|
|
|
|
auto& fontFamily = (*fontFamilyResult.first).second;
|
|
auto& fontStyle = (*fontStyleResult.first).second;
|
|
auto& fontSize = (*fontSizeResult.first).second;
|
|
|
|
// Add to settings list
|
|
settings_.push_back(&fontFamily);
|
|
settings_.push_back(&fontStyle);
|
|
settings_.push_back(&fontSize);
|
|
|
|
// Set settings variables
|
|
fontFamily.SetSettingsVariable(textSettings.font_family(fontCategory));
|
|
fontStyle.SetSettingsVariable(textSettings.font_style(fontCategory));
|
|
fontSize.SetSettingsVariable(textSettings.font_point_size(fontCategory));
|
|
}
|
|
self_->ui->fontListView->setCurrentIndex(fontCategoryModel_->index(0, 0));
|
|
SelectFontCategory(*types::FontCategoryIterator().begin());
|
|
UpdateFontDisplayData();
|
|
|
|
hoverTextWrap_.SetSettingsVariable(textSettings.hover_text_wrap());
|
|
hoverTextWrap_.SetEditWidget(self_->ui->hoverTextWrapSpinBox);
|
|
hoverTextWrap_.SetResetButton(self_->ui->resetHoverTextWrapButton);
|
|
|
|
tooltipMethod_.SetSettingsVariable(textSettings.tooltip_method());
|
|
SCWX_SETTINGS_COMBO_BOX(tooltipMethod_,
|
|
self_->ui->tooltipMethodComboBox,
|
|
types::TooltipMethodIterator(),
|
|
types::GetTooltipMethodName);
|
|
tooltipMethod_.SetResetButton(self_->ui->resetTooltipMethodButton);
|
|
|
|
placefileTextDropShadowEnabled_.SetSettingsVariable(
|
|
textSettings.placefile_text_drop_shadow_enabled());
|
|
placefileTextDropShadowEnabled_.SetEditWidget(
|
|
self_->ui->placefileTextDropShadowCheckBox);
|
|
|
|
radarSiteHoverTextEnabled_.SetSettingsVariable(
|
|
textSettings.radar_site_hover_text_enabled());
|
|
radarSiteHoverTextEnabled_.SetEditWidget(
|
|
self_->ui->radarSiteHoverTextCheckBox);
|
|
}
|
|
|
|
void SettingsDialogImpl::SetupHotkeysTab()
|
|
{
|
|
QVBoxLayout* layout = new QVBoxLayout(self_->ui->hotkeys);
|
|
|
|
hotkeySettingsWidget_ = new HotkeySettingsWidget(self_->ui->hotkeys);
|
|
layout->addWidget(hotkeySettingsWidget_);
|
|
|
|
settingsPages_.push_back(hotkeySettingsWidget_);
|
|
}
|
|
|
|
QImage SettingsDialogImpl::GenerateColorTableImage(
|
|
std::shared_ptr<common::ColorTable> colorTable,
|
|
std::uint16_t min,
|
|
std::uint16_t max,
|
|
float offset,
|
|
float scale)
|
|
{
|
|
std::size_t width = max - min + 1u;
|
|
std::size_t height = 1u;
|
|
QImage image(static_cast<int>(width),
|
|
static_cast<int>(height),
|
|
QImage::Format::Format_ARGB32);
|
|
|
|
for (std::size_t i = min; i <= max; ++i)
|
|
{
|
|
const float value = (i - offset) / scale;
|
|
boost::gil::rgba8_pixel_t pixel = colorTable->Color(value);
|
|
image.setPixel(static_cast<int>(i - min),
|
|
0,
|
|
qRgba(static_cast<int>(pixel[0]),
|
|
static_cast<int>(pixel[1]),
|
|
static_cast<int>(pixel[2]),
|
|
static_cast<int>(pixel[3])));
|
|
}
|
|
|
|
return image;
|
|
}
|
|
|
|
void SettingsDialogImpl::LoadColorTablePreview(const std::string& key,
|
|
const std::string& value,
|
|
QLabel* imageLabel)
|
|
{
|
|
scwx::util::async(
|
|
[key, value, imageLabel]()
|
|
{
|
|
std::unique_ptr<std::istream> is = util::OpenFile(value);
|
|
std::shared_ptr<common::ColorTable> colorTable =
|
|
common::ColorTable::Load(*is);
|
|
|
|
if (colorTable->IsValid())
|
|
{
|
|
auto& conversions = kColorTableConversions_.at(key);
|
|
QPixmap image =
|
|
QPixmap::fromImage(GenerateColorTableImage(colorTable,
|
|
conversions.rangeMin,
|
|
conversions.rangeMax,
|
|
conversions.offset,
|
|
conversions.scale))
|
|
.scaled(64, 20);
|
|
imageLabel->setPixmap(image);
|
|
|
|
QMetaObject::invokeMethod(
|
|
imageLabel, [imageLabel] { imageLabel->setVisible(true); });
|
|
}
|
|
else
|
|
{
|
|
imageLabel->clear();
|
|
|
|
QMetaObject::invokeMethod(
|
|
imageLabel, [imageLabel] { imageLabel->setVisible(false); });
|
|
}
|
|
});
|
|
}
|
|
|
|
void SettingsDialogImpl::UpdateRadarDialogLocation(const std::string& id)
|
|
{
|
|
std::shared_ptr<config::RadarSite> radarSite = config::RadarSite::Get(id);
|
|
|
|
if (radarSite != nullptr)
|
|
{
|
|
radarSiteDialog_->HandleMapUpdate(radarSite->latitude(),
|
|
radarSite->longitude());
|
|
}
|
|
}
|
|
|
|
void SettingsDialogImpl::UpdateAlertRadarDialogLocation(const std::string& id)
|
|
{
|
|
std::shared_ptr<config::RadarSite> radarSite = config::RadarSite::Get(id);
|
|
|
|
if (radarSite != nullptr)
|
|
{
|
|
alertAudioRadarSiteDialog_->HandleMapUpdate(radarSite->latitude(),
|
|
radarSite->longitude());
|
|
}
|
|
}
|
|
|
|
QFont SettingsDialogImpl::GetSelectedFont()
|
|
{
|
|
std::string fontFamily = fontFamilies_.at(selectedFontCategory_)
|
|
.GetSettingsVariable()
|
|
->GetStagedOrValue();
|
|
std::string fontStyle = fontStyles_.at(selectedFontCategory_)
|
|
.GetSettingsVariable()
|
|
->GetStagedOrValue();
|
|
units::font_size::points<double> fontSize {
|
|
fontPointSizes_.at(selectedFontCategory_)
|
|
.GetSettingsVariable()
|
|
->GetStagedOrValue()};
|
|
|
|
QFont font = QFontDatabase::font(QString::fromStdString(fontFamily),
|
|
QString::fromStdString(fontStyle),
|
|
static_cast<int>(fontSize.value()));
|
|
font.setPointSizeF(fontSize.value());
|
|
|
|
return font;
|
|
}
|
|
|
|
void SettingsDialogImpl::SelectFontCategory(types::FontCategory fontCategory)
|
|
{
|
|
selectedFontCategory_ = fontCategory;
|
|
}
|
|
|
|
void SettingsDialogImpl::UpdateFontDisplayData()
|
|
{
|
|
QFont font = GetSelectedFont();
|
|
|
|
self_->ui->fontNameLabel->setText(font.family());
|
|
self_->ui->fontStyleLabel->setText(font.styleName());
|
|
self_->ui->fontSizeLabel->setText(QString::number(font.pointSizeF()));
|
|
|
|
self_->ui->fontPreviewLabel->setFont(font);
|
|
|
|
if (selectedFontCategory_ != types::FontCategory::Unknown)
|
|
{
|
|
auto& fontFamily = fontFamilies_.at(selectedFontCategory_);
|
|
auto& fontStyle = fontStyles_.at(selectedFontCategory_);
|
|
auto& fontSize = fontPointSizes_.at(selectedFontCategory_);
|
|
|
|
self_->ui->resetFontButton->setVisible(!fontFamily.IsDefault() ||
|
|
!fontStyle.IsDefault() ||
|
|
!fontSize.IsDefault());
|
|
}
|
|
}
|
|
|
|
void SettingsDialogImpl::ApplyChanges()
|
|
{
|
|
logger_->info("Applying settings changes");
|
|
|
|
bool committed = false;
|
|
|
|
for (auto& setting : settings_)
|
|
{
|
|
committed |= setting->Commit();
|
|
}
|
|
|
|
for (auto& page : settingsPages_)
|
|
{
|
|
committed |= page->CommitChanges();
|
|
}
|
|
|
|
if (committed)
|
|
{
|
|
manager::SettingsManager::Instance().SaveSettings();
|
|
}
|
|
}
|
|
|
|
void SettingsDialogImpl::DiscardChanges()
|
|
{
|
|
logger_->info("Discarding settings changes");
|
|
|
|
for (auto& setting : settings_)
|
|
{
|
|
setting->Reset();
|
|
}
|
|
|
|
for (auto& page : settingsPages_)
|
|
{
|
|
page->DiscardChanges();
|
|
}
|
|
}
|
|
|
|
void SettingsDialogImpl::ResetToDefault()
|
|
{
|
|
logger_->info("Restoring settings to default");
|
|
|
|
for (auto& setting : settings_)
|
|
{
|
|
setting->StageDefault();
|
|
}
|
|
|
|
for (auto& page : settingsPages_)
|
|
{
|
|
page->ResetToDefault();
|
|
}
|
|
}
|
|
|
|
std::string SettingsDialogImpl::RadarSiteLabel(
|
|
std::shared_ptr<config::RadarSite>& radarSite)
|
|
{
|
|
return fmt::format("{} ({})", radarSite->id(), radarSite->location_name());
|
|
}
|
|
|
|
} // namespace ui
|
|
} // namespace qt
|
|
} // namespace scwx
|