#include "core/SettingsStore.h" #include #include #include #include namespace { constexpr auto kGroupVaults = "vaults"; constexpr auto kKeyVaults = "vaults/vaultList"; constexpr auto kKeyCurrent = "vaults/current"; constexpr auto kKeyProviders = "sync/providers"; constexpr auto kKeyTheme = "ui/theme"; constexpr auto kKeyLeftW = "ui/leftPanelWidth"; constexpr auto kKeyRightW = "ui/rightPanelWidth"; constexpr auto kKeyViewModes = "ui/viewModes"; constexpr auto kKeyAutosavePreset = "autosave/preset"; QString encodeKey(const QString &s) { // Процент-кодирование чтобы избежать интерпретации "/" как разделителя групп в QSettings Ini return QString::fromLatin1(QUrl::toPercentEncoding(s, QByteArray(), QByteArray("/"))); } QString encodeRel(const QString &rel) { // Для относительных путей тоже кодируем "/" как %2F чтобы ключ остался плоским return QString::fromLatin1(QUrl::toPercentEncoding(rel)); } QString decodeKey(const QString &s) { return QUrl::fromPercentEncoding(s.toLatin1()); } } SettingsStore::SettingsStore(QObject *parent) : QObject(parent) { QSettings s; m_providers = s.value(kKeyProviders).toList(); } QStringList SettingsStore::vaults() const { QSettings s; return s.value(kKeyVaults).toStringList(); } QString SettingsStore::currentVault() const { QSettings s; return s.value(kKeyCurrent).toString(); } void SettingsStore::setCurrentVault(const QString &path) { QSettings s; s.setValue(kKeyCurrent, QDir::cleanPath(path)); s.sync(); emit currentVaultChanged(); } QVariantList SettingsStore::providers() const { return m_providers; } void SettingsStore::addVault(const QString &path) { const QString clean = QDir::cleanPath(path); if (clean.isEmpty()) return; QStringList list = vaults(); // нормализуем существующие записи for (QString &v : list) v = QDir::cleanPath(v); list.removeAll(clean); list.prepend(clean); QSettings s; s.setValue(kKeyVaults, list); s.setValue(kKeyCurrent, clean); s.sync(); emit vaultsChanged(); emit currentVaultChanged(); } void SettingsStore::addProvider(const QString &provider, const QVariantMap &config) { if (provider.isEmpty()) return; QVariantList list = m_providers; for (auto &item : list) { auto m = item.toMap(); if (m.value("provider") == provider) { m = config; m["provider"] = provider; item = m; m_providers = list; goto save; } } { auto m = config; m["provider"] = provider; list.append(m); m_providers = list; } save: QSettings s; s.setValue(kKeyProviders, m_providers); s.sync(); emit providersChanged(); } bool SettingsStore::hasVault(const QString &path) const { const QString clean = QDir::cleanPath(path); const QStringList list = vaults(); for (const QString &v : list) if (QDir::cleanPath(v) == clean) return true; return false; } void SettingsStore::clearProvider(const QString &provider) { QVariantList list = m_providers; list.erase(std::remove_if(list.begin(), list.end(), [&provider](const QVariant &v) { return v.toMap().value("provider") == provider; }), list.end()); m_providers = list; QSettings s; s.setValue(kKeyProviders, m_providers); s.sync(); emit providersChanged(); } QString SettingsStore::theme() const { QSettings s; const QString t = s.value(kKeyTheme, QStringLiteral("light")).toString(); return (t == "dark" || t == "light") ? t : QStringLiteral("light"); } void SettingsStore::setTheme(const QString &t) { const QString clean = (t == "dark") ? QStringLiteral("dark") : QStringLiteral("light"); QSettings s; if (s.value(kKeyTheme).toString() == clean) return; s.setValue(kKeyTheme, clean); s.sync(); emit themeChanged(); } int SettingsStore::leftPanelWidth() const { QSettings s; return s.value(kKeyLeftW, 220).toInt(); } void SettingsStore::setLeftPanelWidth(int w) { if (w < 100) w = 100; if (w > 800) w = 800; QSettings s; if (s.value(kKeyLeftW).toInt() == w) return; s.setValue(kKeyLeftW, w); s.sync(); emit leftPanelWidthChanged(); } int SettingsStore::rightPanelWidth() const { QSettings s; return s.value(kKeyRightW, 220).toInt(); } void SettingsStore::setRightPanelWidth(int w) { if (w < 100) w = 100; if (w > 800) w = 800; QSettings s; if (s.value(kKeyRightW).toInt() == w) return; s.setValue(kKeyRightW, w); s.sync(); emit rightPanelWidthChanged(); } QString SettingsStore::viewModeForFile(const QString &vault, const QString &filePath) const { if (vault.isEmpty() || filePath.isEmpty()) return QStringLiteral("edit"); QSettings s; const QVariantMap root = s.value(kKeyViewModes).toMap(); const QString cleanVault = QDir::cleanPath(vault); const QString cleanPath = QDir::cleanPath(filePath); QString rel = cleanPath; if (cleanPath.startsWith(cleanVault)) { rel = cleanPath.mid(cleanVault.length()); while (rel.startsWith("/") || rel.startsWith("\\")) rel.remove(0,1); } else { rel = cleanPath; } const QString oldKey = cleanVault + "|" + rel; if (root.contains(oldKey)) { const QString m = root.value(oldKey).toString(); if (m == "preview" || m == "live" || m == "edit") return m; } const QString encVault = encodeKey(cleanVault); const QString encRel = encodeRel(rel); const QString encKey = encVault + "|" + encRel; if (root.contains(encKey)) { const QString m = root.value(encKey).toString(); if (m == "preview" || m == "live" || m == "edit") return m; } // Обратная совместимость: старый вложенный формат QVariantMap vaultMap = root.value(encVault).toMap(); if (vaultMap.isEmpty()) vaultMap = root.value(cleanVault).toMap(); QString mode = vaultMap.value(encRel).toString(); if (mode.isEmpty()) mode = vaultMap.value(rel).toString(); if (mode == "preview" || mode == "live" || mode == "edit") return mode; return QStringLiteral("edit"); } void SettingsStore::setViewModeForFile(const QString &vault, const QString &filePath, const QString &mode) { if (vault.isEmpty() || filePath.isEmpty()) return; QString cleanMode = mode; if (cleanMode != "preview" && cleanMode != "live") cleanMode = "edit"; QSettings s; QVariantMap root = s.value(kKeyViewModes).toMap(); const QString cleanVault = QDir::cleanPath(vault); const QString cleanPath = QDir::cleanPath(filePath); QString rel = cleanPath; if (cleanPath.startsWith(cleanVault)) { rel = cleanPath.mid(cleanVault.length()); while (rel.startsWith("/") || rel.startsWith("\\")) rel.remove(0,1); } else { rel = cleanPath; } const QString encVault = encodeKey(cleanVault); const QString encRel = encodeRel(rel); const QString encKey = encVault + "|" + encRel; // мигрируем старые ключи const QString oldKey = cleanVault + "|" + rel; if (root.contains(oldKey)) root.remove(oldKey); // мигрируем старый вложенный формат если есть if (root.contains(encVault) && root.value(encVault).typeId() == QMetaType::QVariantMap) { QVariantMap vm = root.value(encVault).toMap(); if (!vm.isEmpty()) { // переносим все вложенные записи в плоский формат for (auto it = vm.constBegin(); it != vm.constEnd(); ++it) { const QString innerKey = encVault + "|" + it.key(); if (!root.contains(innerKey)) root.insert(innerKey, it.value()); } root.remove(encVault); } } if (root.contains(cleanVault) && root.value(cleanVault).typeId() == QMetaType::QVariantMap) { QVariantMap vm = root.value(cleanVault).toMap(); root.remove(cleanVault); for (auto it = vm.constBegin(); it != vm.constEnd(); ++it) { const QString k = encodeKey(cleanVault) + "|" + encodeRel(it.key()); if (!root.contains(k)) root.insert(k, it.value()); } } if (root.value(encKey).toString() == cleanMode) return; root.insert(encKey, cleanMode); s.setValue(kKeyViewModes, root); s.sync(); } QString SettingsStore::autosavePreset() const { QSettings s; const QString p = s.value(kKeyAutosavePreset, QStringLiteral("balanced")).toString(); if (p == "safe" || p == "balanced" || p == "eco" || p == "manual") return p; return QStringLiteral("balanced"); } void SettingsStore::setAutosavePreset(const QString &p) { QString clean = p.trimmed(); if (clean != "safe" && clean != "balanced" && clean != "eco" && clean != "manual") clean = QStringLiteral("balanced"); QSettings s; if (s.value(kKeyAutosavePreset).toString() == clean) return; s.setValue(kKeyAutosavePreset, clean); s.sync(); emit autosavePresetChanged(); } int SettingsStore::autosaveDiskMs() const { const QString p = autosavePreset(); if (p == "safe") return 2000; if (p == "eco") return 10000; if (p == "manual") return 0; return 5000; // balanced } int SettingsStore::autocommitIdleMs() const { const QString p = autosavePreset(); if (p == "safe") return 5000; if (p == "eco") return 60000; if (p == "manual") return 0; return 15000; // balanced } bool SettingsStore::autosaveEnabled() const { return autosavePreset() != "manual"; } bool SettingsStore::autocommitEnabled() const { return autosavePreset() != "manual"; }