Files
Flora/src/core/SettingsStore.cpp
T

279 lines
8.6 KiB
C++
Raw Normal View History

#include "core/SettingsStore.h"
#include <QDir>
#include <QSettings>
#include <QUrl>
#include <algorithm>
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";
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();
}