diff --git a/scwx-qt/source/scwx/qt/main/main_window.cpp b/scwx-qt/source/scwx/qt/main/main_window.cpp index 61cc1cd6..03a43fff 100644 --- a/scwx-qt/source/scwx/qt/main/main_window.cpp +++ b/scwx-qt/source/scwx/qt/main/main_window.cpp @@ -130,6 +130,7 @@ public: } ~MainWindowImpl() { threadPool_.join(); } + void AddRadarSitePreset(const std::string& id); void AsyncSetup(); void ConfigureMapLayout(); void ConfigureMapStyles(); @@ -190,8 +191,8 @@ public: std::shared_ptr radarSiteModel_ { model::RadarSiteModel::Instance()}; - std::map> radarSiteFavoriteActions_ {}; - QMenu* radarSiteFavoriteMenu_ {nullptr}; + std::map> radarSitePresetsActions_ {}; + QMenu* radarSitePresetsMenu_ {nullptr}; std::vector maps_; std::vector elevationCuts_; @@ -223,10 +224,17 @@ MainWindow::MainWindow(QWidget* parent) : ui->vcpLabel->setVisible(false); ui->vcpValueLabel->setVisible(false); ui->vcpDescriptionLabel->setVisible(false); - ui->radarSiteFavoriteButton->setVisible(false); - p->radarSiteFavoriteMenu_ = new QMenu(this); - ui->radarSiteFavoriteButton->setMenu(p->radarSiteFavoriteMenu_); + p->radarSitePresetsMenu_ = new QMenu(this); + ui->radarSitePresetsButton->setMenu(p->radarSitePresetsMenu_); + + auto radarSitePresets = p->radarSiteModel_->presets(); + for (auto preset : radarSitePresets) + { + p->AddRadarSitePreset(preset); + } + + ui->radarSitePresetsButton->setVisible(!radarSitePresets.empty()); // Configure Alert Dock p->alertDockWidget_ = new ui::AlertDockWidget(this); @@ -955,58 +963,27 @@ void MainWindowImpl::ConnectOtherSignals() UpdateRadarSite(); }); - connect( - radarSiteModel_.get(), - &model::RadarSiteModel::FavoriteToggled, - [this](const std::string& siteId, bool isFavorite) - { - if (isFavorite && !radarSiteFavoriteActions_.contains(siteId)) - { - auto radarSite = config::RadarSite::Get(siteId); - std::string actionText = - fmt::format("{}: {}", siteId, radarSite->location_name()); + connect(radarSiteModel_.get(), + &model::RadarSiteModel::PresetToggled, + [this](const std::string& siteId, bool isPreset) + { + if (isPreset && !radarSitePresetsActions_.contains(siteId)) + { + AddRadarSitePreset(siteId); + } + else if (!isPreset) + { + auto entry = radarSitePresetsActions_.find(siteId); + if (entry != radarSitePresetsActions_.cend()) + { + radarSitePresetsMenu_->removeAction(entry->second.get()); + radarSitePresetsActions_.erase(entry); + } + } - auto pair = radarSiteFavoriteActions_.emplace( - siteId, - std::make_shared(QString::fromStdString(actionText))); - auto& action = pair.first->second; - - QAction* before = nullptr; - - // If the radar site is not at the end - if (pair.first != std::prev(radarSiteFavoriteActions_.cend())) - { - // Insert before the next entry in the list - before = std::next(pair.first)->second.get(); - } - - radarSiteFavoriteMenu_->insertAction(before, action.get()); - - connect(action.get(), - &QAction::triggered, - [this, siteId]() - { - for (map::MapWidget* map : maps_) - { - map->SelectRadarSite(siteId); - } - - UpdateRadarSite(); - }); - } - else if (!isFavorite) - { - auto entry = radarSiteFavoriteActions_.find(siteId); - if (entry != radarSiteFavoriteActions_.cend()) - { - radarSiteFavoriteMenu_->removeAction(entry->second.get()); - radarSiteFavoriteActions_.erase(entry); - } - } - - mainWindow_->ui->radarSiteFavoriteButton->setVisible( - !radarSiteFavoriteActions_.empty()); - }); + mainWindow_->ui->radarSitePresetsButton->setVisible( + !radarSitePresetsActions_.empty()); + }); connect(updateManager_.get(), &manager::UpdateManager::UpdateAvailable, this, @@ -1018,6 +995,40 @@ void MainWindowImpl::ConnectOtherSignals() }); } +void MainWindowImpl::AddRadarSitePreset(const std::string& siteId) +{ + auto radarSite = config::RadarSite::Get(siteId); + std::string actionText = + fmt::format("{}: {}", siteId, radarSite->location_name()); + + auto pair = radarSitePresetsActions_.emplace( + siteId, std::make_shared(QString::fromStdString(actionText))); + auto& action = pair.first->second; + + QAction* before = nullptr; + + // If the radar site is not at the end + if (pair.first != std::prev(radarSitePresetsActions_.cend())) + { + // Insert before the next entry in the list + before = std::next(pair.first)->second.get(); + } + + radarSitePresetsMenu_->insertAction(before, action.get()); + + connect(action.get(), + &QAction::triggered, + [this, siteId]() + { + for (map::MapWidget* map : maps_) + { + map->SelectRadarSite(siteId); + } + + UpdateRadarSite(); + }); +} + void MainWindowImpl::HandleFocusChange(QWidget* focused) { map::MapWidget* mapWidget = dynamic_cast(focused); diff --git a/scwx-qt/source/scwx/qt/main/main_window.ui b/scwx-qt/source/scwx/qt/main/main_window.ui index 2cc0cbed..4bd9b09c 100644 --- a/scwx-qt/source/scwx/qt/main/main_window.ui +++ b/scwx-qt/source/scwx/qt/main/main_window.ui @@ -235,7 +235,7 @@ - + 16777215 diff --git a/scwx-qt/source/scwx/qt/model/radar_site_model.cpp b/scwx-qt/source/scwx/qt/model/radar_site_model.cpp index c5638f2d..66683ae3 100644 --- a/scwx-qt/source/scwx/qt/model/radar_site_model.cpp +++ b/scwx-qt/source/scwx/qt/model/radar_site_model.cpp @@ -2,10 +2,16 @@ #include #include #include +#include #include #include +#include + +#include +#include #include +#include namespace scwx { @@ -20,17 +26,40 @@ static const auto logger_ = scwx::util::Logger::Create(logPrefix_); static constexpr int kFirstColumn = static_cast(RadarSiteModel::Column::SiteId); static constexpr int kLastColumn = - static_cast(RadarSiteModel::Column::Favorite); + static_cast(RadarSiteModel::Column::Preset); static constexpr int kNumColumns = kLastColumn - kFirstColumn + 1; class RadarSiteModelImpl { public: - explicit RadarSiteModelImpl(); + explicit RadarSiteModelImpl() : + radarSites_ {}, + geodesic_(util::GeographicLib::DefaultGeodesic()), + distanceMap_ {}, + distanceDisplay_ {scwx::common::DistanceType::Miles}, + previousPosition_ {} + { + // Get all loaded radar sites + std::vector> radarSites = + config::RadarSite::GetAll(); + + // Setup radar site list + for (auto& site : radarSites) + { + distanceMap_[site->id()] = 0.0; + radarSites_.emplace_back(std::move(site)); + } + } ~RadarSiteModelImpl() = default; + void InitializePresets(); + void ReadPresets(); + void WritePresets(); + QList> radarSites_; - std::vector favorites_; + std::unordered_set presets_ {}; + + std::string presetsPath_ {}; const GeographicLib::Geodesic& geodesic_; @@ -44,8 +73,88 @@ public: RadarSiteModel::RadarSiteModel(QObject* parent) : QAbstractTableModel(parent), p(std::make_unique()) { + p->InitializePresets(); + p->ReadPresets(); +} + +RadarSiteModel::~RadarSiteModel() +{ + // Write presets on shutdown + p->WritePresets(); +}; + +std::unordered_set RadarSiteModel::presets() const +{ + return p->presets_; +} + +void RadarSiteModelImpl::InitializePresets() +{ + std::string appDataPath { + QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + .toStdString()}; + + if (!std::filesystem::exists(appDataPath)) + { + if (!std::filesystem::create_directories(appDataPath)) + { + logger_->error("Unable to create application data directory: \"{}\"", + appDataPath); + } + } + + presetsPath_ = appDataPath + "/radar-presets.json"; +} + +void RadarSiteModelImpl::ReadPresets() +{ + logger_->info("Reading presets"); + + boost::json::value presetsJson = nullptr; + + // Determine if presets exists + if (std::filesystem::exists(presetsPath_)) + { + presetsJson = util::json::ReadJsonFile(presetsPath_); + } + + // If presets was successfully read + if (presetsJson != nullptr && presetsJson.is_array()) + { + auto& presetsArray = presetsJson.as_array(); + for (auto& presetsEntry : presetsArray) + { + if (presetsEntry.is_string()) + { + // Get radar site ID from JSON value + std::string preset = + boost::json::value_to(presetsEntry); + boost::to_upper(preset); + + // Find the preset in the list of radar sites + auto it = std::find_if( + radarSites_.cbegin(), + radarSites_.cend(), + [&preset](const std::shared_ptr& radarSite) + { return (radarSite->id() == preset); }); + + // If a match, add to the presets + if (it != radarSites_.cend()) + { + presets_.insert(preset); + } + } + } + } +} + +void RadarSiteModelImpl::WritePresets() +{ + logger_->info("Saving presets"); + + auto presetsJson = boost::json::value_from(presets_); + util::json::WriteJsonFile(presetsPath_, presetsJson); } -RadarSiteModel::~RadarSiteModel() = default; int RadarSiteModel::rowCount(const QModelIndex& parent) const { @@ -121,10 +230,10 @@ QVariant RadarSiteModel::data(const QModelIndex& index, int role) const { return p->distanceMap_.at(site->id()); } - case static_cast(Column::Favorite): + case static_cast(Column::Preset): if (role == types::SortRole) { - return QVariant(p->favorites_.at(index.row())); + return QVariant(p->presets_.contains(site->id())); } break; default: @@ -135,8 +244,8 @@ QVariant RadarSiteModel::data(const QModelIndex& index, int role) const { switch (index.column()) { - case static_cast(Column::Favorite): - if (p->favorites_.at(index.row())) + case static_cast(Column::Preset): + if (p->presets_.contains(site->id())) { return p->starIcon_; } @@ -186,7 +295,7 @@ QVariant RadarSiteModel::headerData(int section, { switch (section) { - case static_cast(Column::Favorite): + case static_cast(Column::Preset): return p->starIcon_; default: break; @@ -220,37 +329,25 @@ void RadarSiteModel::HandleMapUpdate(double latitude, double longitude) Q_EMIT dataChanged(topLeft, bottomRight); } -void RadarSiteModel::ToggleFavorite(int row) +void RadarSiteModel::TogglePreset(int row) { - if (row >= 0 && row < p->favorites_.size()) + if (row >= 0 && row < p->radarSites_.size()) { - bool isFavorite = !p->favorites_.at(row); - p->favorites_.at(row) = isFavorite; + std::string siteId = p->radarSites_.at(row)->id(); + bool isPreset = false; - QModelIndex index = createIndex(row, static_cast(Column::Favorite)); + // Attempt to erase the radar site from presets + if (p->presets_.erase(siteId) == 0) + { + // If the radar site did not exist, add it + p->presets_.insert(siteId); + isPreset = true; + } + + QModelIndex index = createIndex(row, static_cast(Column::Preset)); Q_EMIT dataChanged(index, index); - Q_EMIT FavoriteToggled(p->radarSites_.at(row)->id(), isFavorite); - } -} - -RadarSiteModelImpl::RadarSiteModelImpl() : - radarSites_ {}, - geodesic_(util::GeographicLib::DefaultGeodesic()), - distanceMap_ {}, - distanceDisplay_ {scwx::common::DistanceType::Miles}, - previousPosition_ {} -{ - // Get all loaded radar sites - std::vector> radarSites = - config::RadarSite::GetAll(); - - // Setup radar site list - for (auto& site : radarSites) - { - distanceMap_[site->id()] = 0.0; - radarSites_.emplace_back(std::move(site)); - favorites_.emplace_back(false); + Q_EMIT PresetToggled(siteId, isPreset); } } diff --git a/scwx-qt/source/scwx/qt/model/radar_site_model.hpp b/scwx-qt/source/scwx/qt/model/radar_site_model.hpp index 22e1deb5..73e3d425 100644 --- a/scwx-qt/source/scwx/qt/model/radar_site_model.hpp +++ b/scwx-qt/source/scwx/qt/model/radar_site_model.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include #include @@ -28,12 +29,14 @@ public: Longitude = 5, Type = 6, Distance = 7, - Favorite = 8 + Preset = 8 }; explicit RadarSiteModel(QObject* parent = nullptr); ~RadarSiteModel(); + std::unordered_set presets() const; + int rowCount(const QModelIndex& parent = QModelIndex()) const override; int columnCount(const QModelIndex& parent = QModelIndex()) const override; @@ -44,12 +47,12 @@ public: int role = Qt::DisplayRole) const override; void HandleMapUpdate(double latitude, double longitude); - void ToggleFavorite(int row); + void TogglePreset(int row); static std::shared_ptr Instance(); signals: - void FavoriteToggled(const std::string& siteId, bool isFavorite); + void PresetToggled(const std::string& siteId, bool isPreset); private: std::unique_ptr p; diff --git a/scwx-qt/source/scwx/qt/ui/radar_site_dialog.cpp b/scwx-qt/source/scwx/qt/ui/radar_site_dialog.cpp index fff1b0ce..d80a9990 100644 --- a/scwx-qt/source/scwx/qt/ui/radar_site_dialog.cpp +++ b/scwx-qt/source/scwx/qt/ui/radar_site_dialog.cpp @@ -82,9 +82,9 @@ RadarSiteDialog::RadarSiteDialog(QWidget* parent) : QModelIndex selectedIndex = p->proxyModel_->mapToSource(index); if (selectedIndex.column() == - static_cast(model::RadarSiteModel::Column::Favorite)) + static_cast(model::RadarSiteModel::Column::Preset)) { - p->radarSiteModel_->ToggleFavorite(selectedIndex.row()); + p->radarSiteModel_->TogglePreset(selectedIndex.row()); } }); connect(