/**************************************************************************** ** ScStw Libraries ** Copyright (C) 2020 Itsblue development ** ** This program is free software: you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation, either version 3 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program. If not, see . ****************************************************************************/ #include "../headers/scstwsettings.h" ScStwSettings::ScStwSettings(QObject *parent, bool overwriteFileOnErrors) : QObject(parent) { #ifdef RASPI QString path = "/root/.config/ScStwBasestation"; #else QString path = QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation); #endif QDir dir(path); if(!dir.mkpath(path)) qFatal("[FATAL] Failed to create writable directory for settings at %s", qPrintable(path)); this->settingsFile = new QFile(path + "/settings.json"); this->fileIsReadonly = false; if(!this->settingsFile->open(QFile::ReadWrite)) qFatal("[FATAL] Couldn't open settings file %s", qPrintable(path + "/settings.json")); if(this->settingsFile->size() != 0 && !this->loadSettingsFromFile()) { if(overwriteFileOnErrors) { this->writeSettingsToFile(); qWarning("[WARNING] Settings file (%s) was of invalid format and therefore overwritten!", qPrintable(path + "/settings.json")); } else { qWarning("[WARNING] Settings file (%s) was of invalid format!", qPrintable(path + "/settings.json")); this->fileIsReadonly = true; } } connect(this, &ScStwSettings::settingChanged, this, &ScStwSettings::writeSettingsToFile); this->registerKeyLevelConverters(ScStwSettings::KeyLevel, &ScStwSettings::keyToString, &ScStwSettings::keyToType); this->setDefaultSetting(ScStwSettings::BaseStationSetting::ReadySoundEnableSetting, false); this->setDefaultSetting(ScStwSettings::BaseStationSetting::ReadySoundDelaySetting, 0); this->setDefaultSetting(ScStwSettings::BaseStationSetting::AtYourMarksSoundEnableSetting, false); this->setDefaultSetting(ScStwSettings::BaseStationSetting::AtYourMarksSoundDelaySetting, 0); this->setDefaultSetting(ScStwSettings::SoundVolumeSetting, 1); this->setDefaultSetting(ScStwSettings::CompetitionModeSetting, false); } QVariant ScStwSettings::readSetting(BaseStationSetting key) { return this->readSetting(key, 0); } QVariant ScStwSettings::readSetting(int key, int keyLevel) { if(this->keyToStringConverters.contains(keyLevel)) return this->readSetting(this->keyToStringConverters[keyLevel](key), key, keyLevel); else return QVariant(); } bool ScStwSettings::writeSetting(BaseStationSetting key, QVariant value) { return this->writeSetting(key, 0, value); } bool ScStwSettings::writeSetting(int key, int keyLevel, QVariant value) { if(this->keyToStringConverters.contains(keyLevel)) return this->writeSetting(this->keyToStringConverters[keyLevel](key), value, key, keyLevel); else return false; } bool ScStwSettings::setDefaultSetting(BaseStationSetting key, QVariant defaultValue) { return this->setDefaultSetting(key, 0, defaultValue); } bool ScStwSettings::setDefaultSetting(int key, int keyLevel, QVariant defaultValue) { if(this->keyToStringConverters.contains(keyLevel)) return this->setDefaultSetting(this->keyToStringConverters[keyLevel](key), defaultValue, key, keyLevel); else return false; } QVariant ScStwSettings::readSetting(QString key, int keyInt, int keyLevel) { Q_UNUSED(keyInt) Q_UNUSED(keyLevel) if(this->settingsCache.contains(key)) return this->settingsCache[key]; else return QVariant(); } bool ScStwSettings::writeSetting(QString key, QVariant value, int keyInt, int keyLevel) { // check if the value type is valid if(!this->keyToTypeConverters.contains(keyLevel) || !value.convert(this->keyToTypeConverters[keyLevel](keyInt)) || value.type() == QVariant::Invalid) { return false; } // write the setting if(!this->settingsCache.contains(key)) this->settingsCache.insert(key, value); else if (this->settingsCache[key] == value) return true; else this->settingsCache[key] = value; emit this->settingChanged(keyInt, keyLevel, value); return true; } bool ScStwSettings::setDefaultSetting(QString key, QVariant defaultValue, int keyInt, int keyLevel) { if(!this->settingsCache.contains(key)) return this->writeSetting(key, defaultValue, keyInt, keyLevel); return true; } ScStwSetting * ScStwSettings::getSetting(int key, int keyLevel) { if(!this->internalSettingHandlers.contains(keyLevel)) this->internalSettingHandlers.insert(keyLevel, {}); if(!this->internalSettingHandlers[keyLevel].contains(key)) this->internalSettingHandlers[keyLevel].insert(key, new ScStwSetting(key, keyLevel, this, this)); return this->internalSettingHandlers[keyLevel][key]; } bool ScStwSettings::registerKeyLevelConverters(int keyLevel, keyToStringConverter keyToStringConverterFunction, keyToTypeConverter keyToTypeConverterFunction) { if(this->keyToStringConverters.contains(keyLevel)) return false; this->keyToStringConverters.insert(keyLevel, keyToStringConverterFunction); this->keyToTypeConverters.insert(keyLevel, keyToTypeConverterFunction); return true; } /* * File handling */ bool ScStwSettings::writeSettingsToFile() { if(this->fileIsReadonly) { qWarning() << "[WARNING][SETTINS] Cannot write changes to file! It has been marked readonly due to parse errors (see above)."; return false; } QJsonDocument doc = QJsonDocument::fromVariant(this->settingsCache); // overwrite file this->settingsFile->resize(0); this->settingsFile->reset(); this->settingsFile->write(doc.toJson(QJsonDocument::Indented)); this->settingsFile->flush(); return true; } bool ScStwSettings::loadSettingsFromFile() { QJsonParseError error; QJsonDocument doc = QJsonDocument::fromJson(this->settingsFile->readAll(), &error); if(error.error != QJsonParseError::NoError) { qWarning() << "[ERROR][SETTINGS] Error when parsing settings file: " << error.errorString() << " at offset " << error.offset; return false; } if(doc.toVariant().type() != QVariant::Map) return false; this->settingsCache = doc.toVariant().toMap(); return true; }