271 lines
6.6 KiB
C++
271 lines
6.6 KiB
C++
#include "configobject.hpp"
|
|
|
|
#include <QJsonArray>
|
|
#include <QMetaObject>
|
|
#include <QStringList>
|
|
#include <QVariant>
|
|
|
|
namespace ZShell::config {
|
|
|
|
ConfigObject::ConfigObject(QObject* parent) : ConfigNode(parent) {}
|
|
|
|
void ConfigObject::loadFromJson(const QJsonValue& json) {
|
|
const auto obj = json.toObject();
|
|
const auto* meta = metaObject();
|
|
|
|
QSet<QString> known;
|
|
|
|
for (int i = basePropertyOffset(); i < meta->propertyCount(); ++i) {
|
|
auto prop = meta->property(i);
|
|
const auto key = QString::fromUtf8(prop.name());
|
|
|
|
known.insert(key);
|
|
|
|
if (!obj.contains(key)) continue;
|
|
|
|
const auto jsonVal = obj.value(key);
|
|
|
|
if (prop.metaType().flags().testFlag(QMetaType::PointerToQObject)) {
|
|
if (auto* const node = prop.read(this).value<ConfigNode*>()) {
|
|
node->loadFromJson(jsonVal);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (!prop.isWritable()) continue;
|
|
|
|
if (prop.metaType().id() == QMetaType::QStringList) {
|
|
QStringList list;
|
|
const auto jsonArr = jsonVal.toArray();
|
|
for (const auto& v : jsonArr)
|
|
list.append(v.toString());
|
|
prop.write(this, QVariant::fromValue(list));
|
|
m_loadedKeys.insert(key);
|
|
continue;
|
|
}
|
|
|
|
if (prop.metaType().id() == QMetaType::QVariantList) {
|
|
prop.write(this, jsonVal.toVariant());
|
|
m_loadedKeys.insert(key);
|
|
continue;
|
|
}
|
|
|
|
prop.write(this, jsonVal.toVariant());
|
|
m_loadedKeys.insert(key);
|
|
}
|
|
|
|
m_extras = {};
|
|
for (auto it = obj.begin(); it != obj.end(); ++it) {
|
|
if (!known.contains(it.key())) {
|
|
m_extras.insert(it.key(), it.value());
|
|
}
|
|
}
|
|
}
|
|
|
|
QJsonValue ConfigObject::toJson() const {
|
|
QJsonObject obj;
|
|
const auto* meta = metaObject();
|
|
|
|
for (int i = basePropertyOffset(); i < meta->propertyCount(); ++i) {
|
|
const auto prop = meta->property(i);
|
|
|
|
if (!prop.isReadable()) continue;
|
|
|
|
const auto key = QString::fromUtf8(prop.name());
|
|
|
|
const auto value = prop.read(this);
|
|
|
|
if (prop.metaType().flags().testFlag(QMetaType::PointerToQObject)) {
|
|
if (auto* const node = value.value<ConfigNode*>()) {
|
|
const auto childJson = node->toJson();
|
|
if (!childJson.isUndefined()) obj.insert(key, childJson);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (!m_loadedKeys.contains(key)) continue;
|
|
|
|
if (!prop.isWritable()) continue;
|
|
|
|
if (prop.metaType().id() == QMetaType::QStringList) {
|
|
QJsonArray arr;
|
|
const auto strList = value.toStringList();
|
|
for (const auto& s : strList)
|
|
arr.append(s);
|
|
obj.insert(key, arr);
|
|
continue;
|
|
}
|
|
|
|
if (prop.metaType().id() == QMetaType::QVariantList) {
|
|
obj.insert(key, QJsonArray::fromVariantList(value.toList()));
|
|
continue;
|
|
}
|
|
|
|
obj.insert(key, QJsonValue::fromVariant(value));
|
|
}
|
|
|
|
for (auto it = m_extras.begin(); it != m_extras.end(); ++it)
|
|
obj.insert(it.key(), it.value());
|
|
|
|
if (obj.isEmpty()) return QJsonValue::Undefined;
|
|
|
|
return obj;
|
|
}
|
|
|
|
void ConfigObject::clearLoadedKeys() {
|
|
m_loadedKeys.clear();
|
|
m_extras = {};
|
|
|
|
const auto* meta = metaObject();
|
|
for (int i = basePropertyOffset(); i < meta->propertyCount(); ++i) {
|
|
auto prop = meta->property(i);
|
|
if (auto* const node = prop.read(this).value<ConfigNode*>())
|
|
node->clearLoadedKeys();
|
|
}
|
|
}
|
|
|
|
QStringList ConfigObject::identityKeys() const {
|
|
return {};
|
|
}
|
|
|
|
QStringList ConfigObject::unknownKeys() const {
|
|
auto keys = m_extras.keys();
|
|
|
|
const auto* meta = metaObject();
|
|
for (int i = basePropertyOffset(); i < meta->propertyCount(); ++i) {
|
|
const auto prop = meta->property(i);
|
|
auto* const node = prop.read(this).value<ConfigNode*>();
|
|
if (!node) continue;
|
|
|
|
const auto childKeys = node->unknownKeys();
|
|
for (const auto& childKey : childKeys)
|
|
keys.append(joinPath(QString::fromUtf8(prop.name()), childKey));
|
|
}
|
|
|
|
return keys;
|
|
}
|
|
|
|
QList<ConfigNode*> ConfigObject::childNodes() const {
|
|
QList<ConfigNode*> nodes;
|
|
|
|
const auto* meta = metaObject();
|
|
for (int i = basePropertyOffset(); i < meta->propertyCount(); ++i) {
|
|
const auto prop = meta->property(i);
|
|
|
|
if (auto* const node = prop.read(this).value<ConfigNode*>())
|
|
nodes.append(node);
|
|
}
|
|
|
|
return nodes;
|
|
}
|
|
|
|
void ConfigObject::syncValuesFromGlobal() {
|
|
const auto* meta = metaObject();
|
|
|
|
for (int i = basePropertyOffset(); i < meta->propertyCount(); ++i) {
|
|
auto prop = meta->property(i);
|
|
const auto key = QString::fromUtf8(prop.name());
|
|
|
|
if (auto* const node = prop.read(this).value<ConfigNode*>()) {
|
|
if (auto* const globalNode =
|
|
prop.read(m_global).value<ConfigNode*>())
|
|
node->syncFromGlobal(globalNode);
|
|
continue;
|
|
}
|
|
|
|
if (!prop.isWritable()) continue;
|
|
|
|
if (!m_loadedKeys.contains(key)) {
|
|
auto val = prop.read(m_global);
|
|
prop.write(this, val);
|
|
m_loadedKeys.remove(key);
|
|
}
|
|
}
|
|
}
|
|
|
|
void ConfigObject::resyncFromGlobal() {
|
|
if (!m_global) return;
|
|
|
|
const auto* meta = metaObject();
|
|
for (int i = basePropertyOffset(); i < meta->propertyCount(); ++i) {
|
|
auto prop = meta->property(i);
|
|
const auto key = QString::fromUtf8(prop.name());
|
|
|
|
if (auto* const node = prop.read(this).value<ConfigNode*>()) {
|
|
node->resyncFromGlobal();
|
|
continue;
|
|
}
|
|
|
|
if (!prop.isWritable()) continue;
|
|
|
|
if (!m_loadedKeys.contains(key)) {
|
|
prop.write(this, prop.read(m_global));
|
|
m_loadedKeys.remove(key);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool ConfigObject::isPropertyLoaded(const QString& name) const {
|
|
return m_loadedKeys.contains(name);
|
|
}
|
|
|
|
bool ConfigObject::isGlobalOnly(const QString& name) const {
|
|
return m_globalOnlyKeys.contains(name);
|
|
}
|
|
|
|
QStringList ConfigObject::globalOnlyKeys() const {
|
|
return {m_globalOnlyKeys.begin(), m_globalOnlyKeys.end()};
|
|
}
|
|
|
|
void ConfigObject::resetOption(const QString& name) {
|
|
m_loadedKeys.remove(name);
|
|
|
|
const int idx = metaObject()->indexOfProperty(name.toUtf8().constData());
|
|
if (idx < 0) return;
|
|
|
|
const auto prop = metaObject()->property(idx);
|
|
|
|
if (auto* const node = prop.read(this).value<ConfigNode*>()) {
|
|
node->clearLoadedKeys();
|
|
node->resyncFromGlobal();
|
|
return;
|
|
}
|
|
|
|
if (m_global && prop.isWritable()) prop.write(this, prop.read(m_global));
|
|
}
|
|
|
|
void ConfigObject::onGlobalPropertiesChanged(
|
|
const QMap<QString, QVariant>& changed) {
|
|
for (auto it = changed.begin(); it != changed.end(); ++it) {
|
|
if (m_loadedKeys.contains(it.key()) || isGlobalOnly(it.key())) continue;
|
|
|
|
int idx = metaObject()->indexOfProperty(it.key().toUtf8().constData());
|
|
if (idx >= 0) {
|
|
metaObject()->property(idx).write(this, it.value());
|
|
m_loadedKeys.remove(it.key());
|
|
}
|
|
}
|
|
}
|
|
|
|
QString ConfigObject::childPath(const ConfigNode* child) const {
|
|
const auto* meta = metaObject();
|
|
for (int i = basePropertyOffset(); i < meta->propertyCount(); ++i) {
|
|
const auto prop = meta->property(i);
|
|
|
|
if (prop.read(this).value<ConfigNode*>() == child)
|
|
return QString::fromUtf8(prop.name());
|
|
}
|
|
|
|
return {};
|
|
}
|
|
|
|
void ConfigObject::markPropertyLoaded(const QString& name) {
|
|
m_loadedKeys.insert(name);
|
|
}
|
|
|
|
void ConfigObject::markGlobalOnly(const QString& name) {
|
|
m_globalOnlyKeys.insert(name);
|
|
}
|
|
|
|
} // namespace ZShell::config
|