Save radar presets to file

- Renamed from favorites
This commit is contained in:
Dan Paulat 2023-12-20 12:49:47 -06:00
parent 436a3e0a9f
commit a76816b14c
5 changed files with 208 additions and 97 deletions

View file

@ -130,6 +130,7 @@ public:
} }
~MainWindowImpl() { threadPool_.join(); } ~MainWindowImpl() { threadPool_.join(); }
void AddRadarSitePreset(const std::string& id);
void AsyncSetup(); void AsyncSetup();
void ConfigureMapLayout(); void ConfigureMapLayout();
void ConfigureMapStyles(); void ConfigureMapStyles();
@ -190,8 +191,8 @@ public:
std::shared_ptr<model::RadarSiteModel> radarSiteModel_ { std::shared_ptr<model::RadarSiteModel> radarSiteModel_ {
model::RadarSiteModel::Instance()}; model::RadarSiteModel::Instance()};
std::map<std::string, std::shared_ptr<QAction>> radarSiteFavoriteActions_ {}; std::map<std::string, std::shared_ptr<QAction>> radarSitePresetsActions_ {};
QMenu* radarSiteFavoriteMenu_ {nullptr}; QMenu* radarSitePresetsMenu_ {nullptr};
std::vector<map::MapWidget*> maps_; std::vector<map::MapWidget*> maps_;
std::vector<float> elevationCuts_; std::vector<float> elevationCuts_;
@ -223,10 +224,17 @@ MainWindow::MainWindow(QWidget* parent) :
ui->vcpLabel->setVisible(false); ui->vcpLabel->setVisible(false);
ui->vcpValueLabel->setVisible(false); ui->vcpValueLabel->setVisible(false);
ui->vcpDescriptionLabel->setVisible(false); ui->vcpDescriptionLabel->setVisible(false);
ui->radarSiteFavoriteButton->setVisible(false);
p->radarSiteFavoriteMenu_ = new QMenu(this); p->radarSitePresetsMenu_ = new QMenu(this);
ui->radarSiteFavoriteButton->setMenu(p->radarSiteFavoriteMenu_); 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 // Configure Alert Dock
p->alertDockWidget_ = new ui::AlertDockWidget(this); p->alertDockWidget_ = new ui::AlertDockWidget(this);
@ -955,58 +963,27 @@ void MainWindowImpl::ConnectOtherSignals()
UpdateRadarSite(); UpdateRadarSite();
}); });
connect( connect(radarSiteModel_.get(),
radarSiteModel_.get(), &model::RadarSiteModel::PresetToggled,
&model::RadarSiteModel::FavoriteToggled, [this](const std::string& siteId, bool isPreset)
[this](const std::string& siteId, bool isFavorite) {
{ if (isPreset && !radarSitePresetsActions_.contains(siteId))
if (isFavorite && !radarSiteFavoriteActions_.contains(siteId)) {
{ AddRadarSitePreset(siteId);
auto radarSite = config::RadarSite::Get(siteId); }
std::string actionText = else if (!isPreset)
fmt::format("{}: {}", siteId, radarSite->location_name()); {
auto entry = radarSitePresetsActions_.find(siteId);
if (entry != radarSitePresetsActions_.cend())
{
radarSitePresetsMenu_->removeAction(entry->second.get());
radarSitePresetsActions_.erase(entry);
}
}
auto pair = radarSiteFavoriteActions_.emplace( mainWindow_->ui->radarSitePresetsButton->setVisible(
siteId, !radarSitePresetsActions_.empty());
std::make_shared<QAction>(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());
});
connect(updateManager_.get(), connect(updateManager_.get(),
&manager::UpdateManager::UpdateAvailable, &manager::UpdateManager::UpdateAvailable,
this, 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<QAction>(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) void MainWindowImpl::HandleFocusChange(QWidget* focused)
{ {
map::MapWidget* mapWidget = dynamic_cast<map::MapWidget*>(focused); map::MapWidget* mapWidget = dynamic_cast<map::MapWidget*>(focused);

View file

@ -235,7 +235,7 @@
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QToolButton" name="radarSiteFavoriteButton"> <widget class="QToolButton" name="radarSitePresetsButton">
<property name="maximumSize"> <property name="maximumSize">
<size> <size>
<width>16777215</width> <width>16777215</width>

View file

@ -2,10 +2,16 @@
#include <scwx/qt/config/radar_site.hpp> #include <scwx/qt/config/radar_site.hpp>
#include <scwx/qt/types/qt_types.hpp> #include <scwx/qt/types/qt_types.hpp>
#include <scwx/qt/util/geographic_lib.hpp> #include <scwx/qt/util/geographic_lib.hpp>
#include <scwx/qt/util/json.hpp>
#include <scwx/common/geographic.hpp> #include <scwx/common/geographic.hpp>
#include <scwx/util/logger.hpp> #include <scwx/util/logger.hpp>
#include <filesystem>
#include <boost/json.hpp>
#include <boost/algorithm/string.hpp>
#include <QIcon> #include <QIcon>
#include <QStandardPaths>
namespace scwx namespace scwx
{ {
@ -20,17 +26,40 @@ static const auto logger_ = scwx::util::Logger::Create(logPrefix_);
static constexpr int kFirstColumn = static constexpr int kFirstColumn =
static_cast<int>(RadarSiteModel::Column::SiteId); static_cast<int>(RadarSiteModel::Column::SiteId);
static constexpr int kLastColumn = static constexpr int kLastColumn =
static_cast<int>(RadarSiteModel::Column::Favorite); static_cast<int>(RadarSiteModel::Column::Preset);
static constexpr int kNumColumns = kLastColumn - kFirstColumn + 1; static constexpr int kNumColumns = kLastColumn - kFirstColumn + 1;
class RadarSiteModelImpl class RadarSiteModelImpl
{ {
public: public:
explicit RadarSiteModelImpl(); explicit RadarSiteModelImpl() :
radarSites_ {},
geodesic_(util::GeographicLib::DefaultGeodesic()),
distanceMap_ {},
distanceDisplay_ {scwx::common::DistanceType::Miles},
previousPosition_ {}
{
// Get all loaded radar sites
std::vector<std::shared_ptr<config::RadarSite>> 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; ~RadarSiteModelImpl() = default;
void InitializePresets();
void ReadPresets();
void WritePresets();
QList<std::shared_ptr<config::RadarSite>> radarSites_; QList<std::shared_ptr<config::RadarSite>> radarSites_;
std::vector<bool> favorites_; std::unordered_set<std::string> presets_ {};
std::string presetsPath_ {};
const GeographicLib::Geodesic& geodesic_; const GeographicLib::Geodesic& geodesic_;
@ -44,8 +73,88 @@ public:
RadarSiteModel::RadarSiteModel(QObject* parent) : RadarSiteModel::RadarSiteModel(QObject* parent) :
QAbstractTableModel(parent), p(std::make_unique<RadarSiteModelImpl>()) QAbstractTableModel(parent), p(std::make_unique<RadarSiteModelImpl>())
{ {
p->InitializePresets();
p->ReadPresets();
}
RadarSiteModel::~RadarSiteModel()
{
// Write presets on shutdown
p->WritePresets();
};
std::unordered_set<std::string> 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<std::string>(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<config::RadarSite>& 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 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()); return p->distanceMap_.at(site->id());
} }
case static_cast<int>(Column::Favorite): case static_cast<int>(Column::Preset):
if (role == types::SortRole) if (role == types::SortRole)
{ {
return QVariant(p->favorites_.at(index.row())); return QVariant(p->presets_.contains(site->id()));
} }
break; break;
default: default:
@ -135,8 +244,8 @@ QVariant RadarSiteModel::data(const QModelIndex& index, int role) const
{ {
switch (index.column()) switch (index.column())
{ {
case static_cast<int>(Column::Favorite): case static_cast<int>(Column::Preset):
if (p->favorites_.at(index.row())) if (p->presets_.contains(site->id()))
{ {
return p->starIcon_; return p->starIcon_;
} }
@ -186,7 +295,7 @@ QVariant RadarSiteModel::headerData(int section,
{ {
switch (section) switch (section)
{ {
case static_cast<int>(Column::Favorite): case static_cast<int>(Column::Preset):
return p->starIcon_; return p->starIcon_;
default: default:
break; break;
@ -220,37 +329,25 @@ void RadarSiteModel::HandleMapUpdate(double latitude, double longitude)
Q_EMIT dataChanged(topLeft, bottomRight); 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); std::string siteId = p->radarSites_.at(row)->id();
p->favorites_.at(row) = isFavorite; bool isPreset = false;
QModelIndex index = createIndex(row, static_cast<int>(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<int>(Column::Preset));
Q_EMIT dataChanged(index, index); Q_EMIT dataChanged(index, index);
Q_EMIT FavoriteToggled(p->radarSites_.at(row)->id(), isFavorite); Q_EMIT PresetToggled(siteId, isPreset);
}
}
RadarSiteModelImpl::RadarSiteModelImpl() :
radarSites_ {},
geodesic_(util::GeographicLib::DefaultGeodesic()),
distanceMap_ {},
distanceDisplay_ {scwx::common::DistanceType::Miles},
previousPosition_ {}
{
// Get all loaded radar sites
std::vector<std::shared_ptr<config::RadarSite>> 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);
} }
} }

View file

@ -1,6 +1,7 @@
#pragma once #pragma once
#include <memory> #include <memory>
#include <unordered_set>
#include <QAbstractTableModel> #include <QAbstractTableModel>
@ -28,12 +29,14 @@ public:
Longitude = 5, Longitude = 5,
Type = 6, Type = 6,
Distance = 7, Distance = 7,
Favorite = 8 Preset = 8
}; };
explicit RadarSiteModel(QObject* parent = nullptr); explicit RadarSiteModel(QObject* parent = nullptr);
~RadarSiteModel(); ~RadarSiteModel();
std::unordered_set<std::string> presets() const;
int rowCount(const QModelIndex& parent = QModelIndex()) const override; int rowCount(const QModelIndex& parent = QModelIndex()) const override;
int columnCount(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; int role = Qt::DisplayRole) const override;
void HandleMapUpdate(double latitude, double longitude); void HandleMapUpdate(double latitude, double longitude);
void ToggleFavorite(int row); void TogglePreset(int row);
static std::shared_ptr<RadarSiteModel> Instance(); static std::shared_ptr<RadarSiteModel> Instance();
signals: signals:
void FavoriteToggled(const std::string& siteId, bool isFavorite); void PresetToggled(const std::string& siteId, bool isPreset);
private: private:
std::unique_ptr<RadarSiteModelImpl> p; std::unique_ptr<RadarSiteModelImpl> p;

View file

@ -82,9 +82,9 @@ RadarSiteDialog::RadarSiteDialog(QWidget* parent) :
QModelIndex selectedIndex = p->proxyModel_->mapToSource(index); QModelIndex selectedIndex = p->proxyModel_->mapToSource(index);
if (selectedIndex.column() == if (selectedIndex.column() ==
static_cast<int>(model::RadarSiteModel::Column::Favorite)) static_cast<int>(model::RadarSiteModel::Column::Preset))
{ {
p->radarSiteModel_->ToggleFavorite(selectedIndex.row()); p->radarSiteModel_->TogglePreset(selectedIndex.row());
} }
}); });
connect( connect(