#include #include #include #include #include #include #include #include #include #include #include #include namespace scwx { namespace qt { namespace manager { static const std::string logPrefix_ = "scwx::qt::manager::marker_manager"; static const auto logger_ = scwx::util::Logger::Create(logPrefix_); static const std::string kNameName_ = "name"; static const std::string kLatitudeName_ = "latitude"; static const std::string kLongitudeName_ = "longitude"; class MarkerManager::Impl { public: class MarkerRecord; explicit Impl(MarkerManager* self) : self_ {self} {} ~Impl() { threadPool_.join(); } std::string markerSettingsPath_ {}; std::vector> markerRecords_ {}; MarkerManager* self_; boost::asio::thread_pool threadPool_ {1u}; void InitializeMarkerSettings(); void ReadMarkerSettings(); void WriteMarkerSettings(); std::shared_ptr GetMarkerByName(const std::string& name); }; class MarkerManager::Impl::MarkerRecord { public: MarkerRecord(const std::string& name, double latitude, double longitude) : markerInfo_ {types::MarkerInfo(name, latitude, longitude)} { } MarkerRecord(const types::MarkerInfo& info) : markerInfo_ {info} { } const types::MarkerInfo& toMarkerInfo() { return markerInfo_; } types::MarkerInfo markerInfo_; friend void tag_invoke(boost::json::value_from_tag, boost::json::value& jv, const std::shared_ptr& record) { jv = {{kNameName_, record->markerInfo_.name}, {kLatitudeName_, record->markerInfo_.latitude}, {kLongitudeName_, record->markerInfo_.longitude}}; } friend MarkerRecord tag_invoke(boost::json::value_to_tag, const boost::json::value& jv) { return MarkerRecord( boost::json::value_to(jv.at(kNameName_)), boost::json::value_to(jv.at(kLatitudeName_)), boost::json::value_to(jv.at(kLongitudeName_))); } }; void MarkerManager::Impl::InitializeMarkerSettings() { 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); } } markerSettingsPath_ = appDataPath + "/location-markers.json"; } void MarkerManager::Impl::ReadMarkerSettings() { logger_->info("Reading location marker settings"); boost::json::value markerJson = nullptr; // Determine if marker settings exists if (std::filesystem::exists(markerSettingsPath_)) { markerJson = util::json::ReadJsonFile(markerSettingsPath_); } if (markerJson != nullptr && markerJson.is_array()) { // For each marker entry auto& markerArray = markerJson.as_array(); markerRecords_.reserve(markerArray.size()); for (auto& markerEntry : markerArray) { try { MarkerRecord record = boost::json::value_to(markerEntry); if (!record.markerInfo_.name.empty()) { markerRecords_.emplace_back( std::make_shared(record.markerInfo_)); } } catch (const std::exception& ex) { logger_->warn("Invalid location marker entry: {}", ex.what()); } } logger_->debug("{} location marker entries", markerRecords_.size()); } Q_EMIT self_->MarkersUpdated(); } void MarkerManager::Impl::WriteMarkerSettings() { logger_->info("Saving location marker settings"); auto markerJson = boost::json::value_from(markerRecords_); util::json::WriteJsonFile(markerSettingsPath_, markerJson); } std::shared_ptr MarkerManager::Impl::GetMarkerByName(const std::string& name) { for (auto& markerRecord : markerRecords_) { if (markerRecord->markerInfo_.name == name) { return markerRecord; } } return nullptr; } MarkerManager::MarkerManager() : p(std::make_unique(this)) { boost::asio::post(p->threadPool_, [this]() { try { p->InitializeMarkerSettings(); // Read Marker settings on startup main::Application::WaitForInitialization(); p->ReadMarkerSettings(); } catch (const std::exception& ex) { logger_->error(ex.what()); } }); } MarkerManager::~MarkerManager() { p->WriteMarkerSettings(); } size_t MarkerManager::marker_count() { return p->markerRecords_.size(); } // TODO deal with out of range/not found const types::MarkerInfo& MarkerManager::get_marker(size_t index) { std::shared_ptr markerRecord = p->markerRecords_[index]; return markerRecord->toMarkerInfo(); } void MarkerManager::set_marker(size_t index, const types::MarkerInfo& marker) { std::shared_ptr markerRecord = p->markerRecords_[index]; markerRecord->markerInfo_ = marker; Q_EMIT MarkersUpdated(); } void MarkerManager::add_marker(const types::MarkerInfo& marker) { p->markerRecords_.emplace_back(std::make_shared(marker)); Q_EMIT MarkerAdded(); Q_EMIT MarkersUpdated(); } void MarkerManager::remove_marker(size_t index) { if (index >= p->markerRecords_.size()) { return; } for (size_t i = index; i < p->markerRecords_.size() - 1; i++) { p->markerRecords_[i] = p->markerRecords_[i + 1]; } p->markerRecords_.pop_back(); Q_EMIT MarkerRemoved(index); Q_EMIT MarkersUpdated(); } void MarkerManager::move_marker(size_t from, size_t to) { if (from >= p->markerRecords_.size() || to >= p->markerRecords_.size()) { return; } std::shared_ptr markerRecord = p->markerRecords_[from]; if (from == to) {} else if (from < to) { for (size_t i = from; i < to; i++) { p->markerRecords_[i] = p->markerRecords_[i + 1]; } p->markerRecords_[to] = markerRecord; } else { for (size_t i = from; i > to; i--) { p->markerRecords_[i] = p->markerRecords_[i - 1]; } p->markerRecords_[to] = markerRecord; } Q_EMIT MarkersUpdated(); } std::shared_ptr MarkerManager::Instance() { static std::weak_ptr markerManagerReference_ {}; static std::mutex instanceMutex_ {}; std::unique_lock lock(instanceMutex_); std::shared_ptr markerManager = markerManagerReference_.lock(); if (markerManager == nullptr) { markerManager = std::make_shared(); markerManagerReference_ = markerManager; } return markerManager; } } // namespace manager } // namespace qt } // namespace scwx