mirror of
				https://github.com/ciphervance/supercell-wx.git
				synced 2025-10-31 03:40:05 +00:00 
			
		
		
		
	Save radar presets to file
- Renamed from favorites
This commit is contained in:
		
							parent
							
								
									436a3e0a9f
								
							
						
					
					
						commit
						a76816b14c
					
				
					 5 changed files with 208 additions and 97 deletions
				
			
		|  | @ -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<model::RadarSiteModel> radarSiteModel_ { | ||||
|       model::RadarSiteModel::Instance()}; | ||||
|    std::map<std::string, std::shared_ptr<QAction>> radarSiteFavoriteActions_ {}; | ||||
|    QMenu* radarSiteFavoriteMenu_ {nullptr}; | ||||
|    std::map<std::string, std::shared_ptr<QAction>> radarSitePresetsActions_ {}; | ||||
|    QMenu* radarSitePresetsMenu_ {nullptr}; | ||||
| 
 | ||||
|    std::vector<map::MapWidget*> maps_; | ||||
|    std::vector<float>           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<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()); | ||||
|       }); | ||||
|               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<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) | ||||
| { | ||||
|    map::MapWidget* mapWidget = dynamic_cast<map::MapWidget*>(focused); | ||||
|  |  | |||
|  | @ -235,7 +235,7 @@ | |||
|                 </widget> | ||||
|                </item> | ||||
|                <item> | ||||
|                 <widget class="QToolButton" name="radarSiteFavoriteButton"> | ||||
|                 <widget class="QToolButton" name="radarSitePresetsButton"> | ||||
|                  <property name="maximumSize"> | ||||
|                   <size> | ||||
|                    <width>16777215</width> | ||||
|  |  | |||
|  | @ -2,10 +2,16 @@ | |||
| #include <scwx/qt/config/radar_site.hpp> | ||||
| #include <scwx/qt/types/qt_types.hpp> | ||||
| #include <scwx/qt/util/geographic_lib.hpp> | ||||
| #include <scwx/qt/util/json.hpp> | ||||
| #include <scwx/common/geographic.hpp> | ||||
| #include <scwx/util/logger.hpp> | ||||
| 
 | ||||
| #include <filesystem> | ||||
| 
 | ||||
| #include <boost/json.hpp> | ||||
| #include <boost/algorithm/string.hpp> | ||||
| #include <QIcon> | ||||
| #include <QStandardPaths> | ||||
| 
 | ||||
| namespace scwx | ||||
| { | ||||
|  | @ -20,17 +26,40 @@ static const auto        logger_    = scwx::util::Logger::Create(logPrefix_); | |||
| static constexpr int kFirstColumn = | ||||
|    static_cast<int>(RadarSiteModel::Column::SiteId); | ||||
| static constexpr int kLastColumn = | ||||
|    static_cast<int>(RadarSiteModel::Column::Favorite); | ||||
|    static_cast<int>(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<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; | ||||
| 
 | ||||
|    void InitializePresets(); | ||||
|    void ReadPresets(); | ||||
|    void WritePresets(); | ||||
| 
 | ||||
|    QList<std::shared_ptr<config::RadarSite>> radarSites_; | ||||
|    std::vector<bool>                         favorites_; | ||||
|    std::unordered_set<std::string>           presets_ {}; | ||||
| 
 | ||||
|    std::string presetsPath_ {}; | ||||
| 
 | ||||
|    const GeographicLib::Geodesic& geodesic_; | ||||
| 
 | ||||
|  | @ -44,8 +73,88 @@ public: | |||
| RadarSiteModel::RadarSiteModel(QObject* parent) : | ||||
|     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 | ||||
| { | ||||
|  | @ -121,10 +230,10 @@ QVariant RadarSiteModel::data(const QModelIndex& index, int role) const | |||
|          { | ||||
|             return p->distanceMap_.at(site->id()); | ||||
|          } | ||||
|       case static_cast<int>(Column::Favorite): | ||||
|       case static_cast<int>(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<int>(Column::Favorite): | ||||
|          if (p->favorites_.at(index.row())) | ||||
|       case static_cast<int>(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<int>(Column::Favorite): | ||||
|          case static_cast<int>(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<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 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<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); | ||||
|       Q_EMIT PresetToggled(siteId, isPreset); | ||||
|    } | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,6 +1,7 @@ | |||
| #pragma once | ||||
| 
 | ||||
| #include <memory> | ||||
| #include <unordered_set> | ||||
| 
 | ||||
| #include <QAbstractTableModel> | ||||
| 
 | ||||
|  | @ -28,12 +29,14 @@ public: | |||
|       Longitude = 5, | ||||
|       Type      = 6, | ||||
|       Distance  = 7, | ||||
|       Favorite  = 8 | ||||
|       Preset    = 8 | ||||
|    }; | ||||
| 
 | ||||
|    explicit RadarSiteModel(QObject* parent = nullptr); | ||||
|    ~RadarSiteModel(); | ||||
| 
 | ||||
|    std::unordered_set<std::string> 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<RadarSiteModel> Instance(); | ||||
| 
 | ||||
| signals: | ||||
|    void FavoriteToggled(const std::string& siteId, bool isFavorite); | ||||
|    void PresetToggled(const std::string& siteId, bool isPreset); | ||||
| 
 | ||||
| private: | ||||
|    std::unique_ptr<RadarSiteModelImpl> p; | ||||
|  |  | |||
|  | @ -82,9 +82,9 @@ RadarSiteDialog::RadarSiteDialog(QWidget* parent) : | |||
|               QModelIndex selectedIndex = p->proxyModel_->mapToSource(index); | ||||
| 
 | ||||
|               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( | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Dan Paulat
						Dan Paulat