Files
swr-ini-tool/src/os_config_tab.cpp
Niklas Friesen 4095980023 Update source copyright headers to NiklasCFW.
Replace the project-owned 2026 copyright owner in src headers from Switchroot to NiklasCFW while leaving vendored third-party notices unchanged.
2026-04-23 17:02:38 +02:00

266 lines
8.0 KiB
C++

/*
SWR INI Tool - Switchroot INI Configuration Editor
Copyright (C) 2026 NiklasCFW
*/
#include "os_config_tab.h"
#include "i18n.h"
#include "oc_defs.h"
#include <algorithm>
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<uint32_t>& options)
{
// Build option list, including the current value if not in the predefined list
std::vector<uint32_t> 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<std::string> 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<uint32_t> 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<uint32_t>& options)
{
// Build option list, including the current value if not in the predefined list
std::vector<uint32_t> 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<std::string> 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<uint32_t> 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);
}