/* SWR INI Tool - Switchroot INI Configuration Editor Copyright (C) 2026 NiklasCFW */ #include "os_config_tab.h" #include "i18n.h" #include "oc_defs.h" #include OsConfigTab::OsConfigTab(const std::string& osName, const std::string& iniPath) : osName(osName), iniPath(iniPath) { if (!this->ini.load(iniPath)) { buildErrorUI(I18n::errorListItemTitleIni(), I18n::iniLoadErrorBody(iniPath)); return; } this->osSection = this->ini.findOsSection(); if (this->osSection.empty()) { buildErrorUI(I18n::errorListItemTitleIni(), I18n::iniSectionErrorBody(iniPath, osName)); return; } buildUI(); } void OsConfigTab::reload(const std::string& newIniPath) { this->iniPath = newIniPath; this->osSection.clear(); // Remove all child views this->clear(); if (!this->ini.load(iniPath)) { buildErrorUI(I18n::errorListItemTitleIni(), I18n::iniLoadErrorBody(iniPath)); return; } this->osSection = this->ini.findOsSection(); if (this->osSection.empty()) { buildErrorUI(I18n::errorListItemTitleIni(), I18n::iniSectionErrorBody(iniPath, osName)); return; } buildUI(); } void OsConfigTab::buildErrorUI(const std::string& title, const std::string& message) { this->setSpacing(15); // Use a ListItem (focusable) so borealis doesn't crash on controller nav brls::ListItem *errorItem = new brls::ListItem(title, message); this->addView(errorItem); } void OsConfigTab::buildUI() { this->setSpacing(2); this->setMarginBottom(20); // Section name info brls::Label *sectionInfo = new brls::Label( brls::LabelStyle::DESCRIPTION, I18n::sectionEditLine(this->osSection, this->iniPath), true ); this->addView(sectionInfo); // ── Toggle Options ── this->addView(new brls::Header(I18n::headerToggleOptions())); for (const auto& def : OC_BOOL_KEYS) { bool val = this->ini.getBool(this->osSection, def.key, false); addBooleanToggle(I18n::ocBoolLabel(def.key), "", def.key, val); } // ── Frequency Settings ── brls::Rectangle* spacer1 = new brls::Rectangle(nvgRGBA(0, 0, 0, 0)); spacer1->setHeight(30); this->addView(spacer1); this->addView(new brls::Header(I18n::headerFrequencySettings())); for (const auto& def : OC_FREQ_KEYS) { int defVal = def.options.empty() ? 0 : (int)def.options.front(); uint32_t val = (uint32_t)this->ini.getInt(this->osSection, def.key, defVal); addFreqDropdown(I18n::ocFreqLabel(def.key), "", def.key, val, def.options); } // ── Voltage Settings ── brls::Rectangle* spacer2 = new brls::Rectangle(nvgRGBA(0, 0, 0, 0)); spacer2->setHeight(30); this->addView(spacer2); this->addView(new brls::Header(I18n::headerVoltageSettings())); for (const auto& def : OC_VOLTAGE_KEYS) { int defVal = def.options.empty() ? 0 : (int)def.options.front(); uint32_t val = (uint32_t)this->ini.getInt(this->osSection, def.key, defVal); addVoltageDropdown(I18n::ocVoltageLabel(def.key), "", def.key, val, def.options); } // ── Other Keys (read-only info) ── // Show any keys that aren't covered by the known definitions above auto allKeys = this->ini.getKeys(this->osSection); bool hasOtherKeys = false; for (const auto& kv : allKeys) { bool isKnown = false; for (const auto& def : OC_BOOL_KEYS) if (def.key == kv.first) { isKnown = true; break; } if (!isKnown) for (const auto& def : OC_FREQ_KEYS) if (def.key == kv.first) { isKnown = true; break; } if (!isKnown) for (const auto& def : OC_VOLTAGE_KEYS) if (def.key == kv.first) { isKnown = true; break; } if (!isKnown) { if (!hasOtherKeys) { brls::Rectangle* spacerOther = new brls::Rectangle(nvgRGBA(0, 0, 0, 0)); spacerOther->setHeight(30); this->addView(spacerOther); this->addView(new brls::Header(I18n::headerOther())); hasOtherKeys = true; } brls::ListItem *item = new brls::ListItem(kv.first); item->setValue(kv.second); this->addView(item); } } } void OsConfigTab::saveAndNotify() { if (this->ini.save()) { brls::Application::notify(I18n::notifyConfigSaved()); } else { brls::Application::notify(I18n::notifyConfigSaveError()); } } void OsConfigTab::addBooleanToggle(const std::string& label, const std::string& description, const std::string& key, bool currentValue) { brls::ToggleListItem *toggle = new brls::ToggleListItem( label, currentValue, description, I18n::wordOn(), I18n::wordOff() ); std::string keyCopy = key; toggle->getClickEvent()->subscribe([this, toggle, keyCopy](brls::View* view) { bool newVal = toggle->getToggleState(); this->ini.setBool(this->osSection, keyCopy, newVal); this->saveAndNotify(); }); this->addView(toggle); } void OsConfigTab::addFreqDropdown(const std::string& label, const std::string& description, const std::string& key, uint32_t currentValue, const std::vector& options) { // Build option list, including the current value if not in the predefined list std::vector allOptions = options; bool currentFound = false; for (uint32_t opt : allOptions) { if (opt == currentValue) { currentFound = true; break; } } if (!currentFound && currentValue > 0) { allOptions.push_back(currentValue); std::sort(allOptions.begin(), allOptions.end()); } // Build display strings std::vector displayOptions; size_t selectedIdx = 0; for (size_t i = 0; i < allOptions.size(); i++) { displayOptions.push_back(formatFreqKHz(allOptions[i])); if (allOptions[i] == currentValue) selectedIdx = i; } brls::SelectListItem *item = new brls::SelectListItem(label, displayOptions, selectedIdx); std::string keyCopy = key; std::vector optionsCopy = allOptions; item->getValueSelectedEvent()->subscribe([this, keyCopy, optionsCopy](int result) { if (result >= 0 && result < (int)optionsCopy.size()) { this->ini.setInt(this->osSection, keyCopy, (int)optionsCopy[result]); this->saveAndNotify(); } }); this->addView(item); } void OsConfigTab::addVoltageDropdown(const std::string& label, const std::string& description, const std::string& key, uint32_t currentValue, const std::vector& options) { // Build option list, including the current value if not in the predefined list std::vector allOptions = options; bool currentFound = false; for (uint32_t opt : allOptions) { if (opt == currentValue) { currentFound = true; break; } } if (!currentFound && currentValue > 0) { allOptions.push_back(currentValue); std::sort(allOptions.begin(), allOptions.end()); } // Build display strings std::vector displayOptions; size_t selectedIdx = 0; for (size_t i = 0; i < allOptions.size(); i++) { displayOptions.push_back(formatVoltageMv(allOptions[i])); if (allOptions[i] == currentValue) selectedIdx = i; } brls::SelectListItem *item = new brls::SelectListItem(label, displayOptions, selectedIdx); std::string keyCopy = key; std::vector optionsCopy = allOptions; item->getValueSelectedEvent()->subscribe([this, keyCopy, optionsCopy](int result) { if (result >= 0 && result < (int)optionsCopy.size()) { this->ini.setInt(this->osSection, keyCopy, (int)optionsCopy[result]); this->saveAndNotify(); } }); this->addView(item); }