aboutsummaryrefslogtreecommitdiff
path: root/src/qt/optionsmodel.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/qt/optionsmodel.cpp')
-rw-r--r--src/qt/optionsmodel.cpp782
1 files changed, 427 insertions, 355 deletions
diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp
index 40b9ed5483..0b4359a917 100644
--- a/src/qt/optionsmodel.cpp
+++ b/src/qt/optionsmodel.cpp
@@ -19,6 +19,7 @@
#include <txdb.h> // for -dbcache defaults
#include <util/string.h>
#include <validation.h> // For DEFAULT_SCRIPTCHECK_THREADS
+#include <wallet/wallet.h> // For DEFAULT_SPEND_ZEROCONF_CHANGE
#include <QDebug>
#include <QLatin1Char>
@@ -26,14 +27,99 @@
#include <QStringList>
#include <QVariant>
+#include <univalue.h>
+
const char *DEFAULT_GUI_PROXY_HOST = "127.0.0.1";
static const QString GetDefaultProxyAddress();
-OptionsModel::OptionsModel(QObject *parent, bool resetSettings) :
- QAbstractListModel(parent)
+/** Map GUI option ID to node setting name. */
+static const char* SettingName(OptionsModel::OptionID option)
+{
+ switch (option) {
+ case OptionsModel::DatabaseCache: return "dbcache";
+ case OptionsModel::ThreadsScriptVerif: return "par";
+ case OptionsModel::SpendZeroConfChange: return "spendzeroconfchange";
+ case OptionsModel::ExternalSignerPath: return "signer";
+ case OptionsModel::MapPortUPnP: return "upnp";
+ case OptionsModel::MapPortNatpmp: return "natpmp";
+ case OptionsModel::Listen: return "listen";
+ case OptionsModel::Server: return "server";
+ case OptionsModel::PruneSize: return "prune";
+ case OptionsModel::Prune: return "prune";
+ case OptionsModel::ProxyIP: return "proxy";
+ case OptionsModel::ProxyPort: return "proxy";
+ case OptionsModel::ProxyUse: return "proxy";
+ case OptionsModel::ProxyIPTor: return "onion";
+ case OptionsModel::ProxyPortTor: return "onion";
+ case OptionsModel::ProxyUseTor: return "onion";
+ case OptionsModel::Language: return "lang";
+ default: throw std::logic_error(strprintf("GUI option %i has no corresponding node setting.", option));
+ }
+}
+
+/** Call node.updateRwSetting() with Bitcoin 22.x workaround. */
+static void UpdateRwSetting(interfaces::Node& node, OptionsModel::OptionID option, const util::SettingsValue& value)
+{
+ if (value.isNum() &&
+ (option == OptionsModel::DatabaseCache ||
+ option == OptionsModel::ThreadsScriptVerif ||
+ option == OptionsModel::Prune ||
+ option == OptionsModel::PruneSize)) {
+ // Write certain old settings as strings, even though they are numbers,
+ // because Bitcoin 22.x releases try to read these specific settings as
+ // strings in addOverriddenOption() calls at startup, triggering
+ // uncaught exceptions in UniValue::get_str(). These errors were fixed
+ // in later releases by https://github.com/bitcoin/bitcoin/pull/24498.
+ // If new numeric settings are added, they can be written as numbers
+ // instead of strings, because bitcoin 22.x will not try to read these.
+ node.updateRwSetting(SettingName(option), value.getValStr());
+ } else {
+ node.updateRwSetting(SettingName(option), value);
+ }
+}
+
+//! Convert enabled/size values to bitcoin -prune setting.
+static util::SettingsValue PruneSetting(bool prune_enabled, int prune_size_gb)
+{
+ assert(!prune_enabled || prune_size_gb >= 1); // PruneSizeGB and ParsePruneSizeGB never return less
+ return prune_enabled ? PruneGBtoMiB(prune_size_gb) : 0;
+}
+
+//! Get pruning enabled value to show in GUI from bitcoin -prune setting.
+static bool PruneEnabled(const util::SettingsValue& prune_setting)
+{
+ // -prune=1 setting is manual pruning mode, so disabled for purposes of the gui
+ return SettingToInt(prune_setting, 0) > 1;
+}
+
+//! Get pruning size value to show in GUI from bitcoin -prune setting. If
+//! pruning is not enabled, just show default recommended pruning size (2GB).
+static int PruneSizeGB(const util::SettingsValue& prune_setting)
+{
+ int value = SettingToInt(prune_setting, 0);
+ return value > 1 ? PruneMiBtoGB(value) : DEFAULT_PRUNE_TARGET_GB;
+}
+
+//! Parse pruning size value provided by user in GUI or loaded from QSettings
+//! (windows registry key or qt .conf file). Smallest value that the GUI can
+//! display is 1 GB, so round up if anything less is parsed.
+static int ParsePruneSizeGB(const QVariant& prune_size)
+{
+ return std::max(1, prune_size.toInt());
+}
+
+struct ProxySetting {
+ bool is_set;
+ QString ip;
+ QString port;
+};
+static ProxySetting ParseProxyString(const std::string& proxy);
+static std::string ProxyString(bool is_set, QString ip, QString port);
+
+OptionsModel::OptionsModel(interfaces::Node& node, QObject *parent) :
+ QAbstractListModel(parent), m_node{node}
{
- Init(resetSettings);
}
void OptionsModel::addOverriddenOption(const std::string &option)
@@ -42,10 +128,17 @@ void OptionsModel::addOverriddenOption(const std::string &option)
}
// Writes all missing QSettings with their default values
-void OptionsModel::Init(bool resetSettings)
+bool OptionsModel::Init(bilingual_str& error)
{
- if (resetSettings)
- Reset();
+ // Initialize display settings from stored settings.
+ m_prune_size_gb = PruneSizeGB(node().getPersistentSetting("prune"));
+ ProxySetting proxy = ParseProxyString(SettingToString(node().getPersistentSetting("proxy"), GetDefaultProxyAddress().toStdString()));
+ m_proxy_ip = proxy.ip;
+ m_proxy_port = proxy.port;
+ ProxySetting onion = ParseProxyString(SettingToString(node().getPersistentSetting("onion"), GetDefaultProxyAddress().toStdString()));
+ m_onion_ip = onion.ip;
+ m_onion_port = onion.port;
+ language = QString::fromStdString(SettingToString(node().getPersistentSetting("lang"), ""));
checkAndMigrate();
@@ -98,130 +191,43 @@ void OptionsModel::Init(bool resetSettings)
// These are shared with the core or have a command-line parameter
// and we want command-line parameters to overwrite the GUI settings.
- //
+ for (OptionID option : {DatabaseCache, ThreadsScriptVerif, SpendZeroConfChange, ExternalSignerPath, MapPortUPnP,
+ MapPortNatpmp, Listen, Server, Prune, ProxyUse, ProxyUseTor, Language}) {
+ std::string setting = SettingName(option);
+ if (node().isSettingIgnored(setting)) addOverriddenOption("-" + setting);
+ try {
+ getOption(option);
+ } catch (const std::exception& e) {
+ // This handles exceptions thrown by univalue that can happen if
+ // settings in settings.json don't have the expected types.
+ error.original = strprintf("Could not read setting \"%s\", %s.", setting, e.what());
+ error.translated = tr("Could not read setting \"%1\", %2.").arg(QString::fromStdString(setting), e.what()).toStdString();
+ return false;
+ }
+ }
+
// If setting doesn't exist create it with defaults.
- //
- // If gArgs.SoftSetArg() or gArgs.SoftSetBoolArg() return false we were overridden
- // by command-line and show this in the UI.
// Main
- if (!settings.contains("bPrune"))
- settings.setValue("bPrune", false);
- if (!settings.contains("nPruneSize"))
- settings.setValue("nPruneSize", DEFAULT_PRUNE_TARGET_GB);
- SetPruneEnabled(settings.value("bPrune").toBool());
-
- if (!settings.contains("nDatabaseCache"))
- settings.setValue("nDatabaseCache", (qint64)nDefaultDbCache);
- if (!gArgs.SoftSetArg("-dbcache", settings.value("nDatabaseCache").toString().toStdString()))
- addOverriddenOption("-dbcache");
-
- if (!settings.contains("nThreadsScriptVerif"))
- settings.setValue("nThreadsScriptVerif", DEFAULT_SCRIPTCHECK_THREADS);
- if (!gArgs.SoftSetArg("-par", settings.value("nThreadsScriptVerif").toString().toStdString()))
- addOverriddenOption("-par");
-
if (!settings.contains("strDataDir"))
settings.setValue("strDataDir", GUIUtil::getDefaultDataDirectory());
// Wallet
#ifdef ENABLE_WALLET
- if (!settings.contains("bSpendZeroConfChange"))
- settings.setValue("bSpendZeroConfChange", true);
- if (!gArgs.SoftSetBoolArg("-spendzeroconfchange", settings.value("bSpendZeroConfChange").toBool()))
- addOverriddenOption("-spendzeroconfchange");
-
- if (!settings.contains("external_signer_path"))
- settings.setValue("external_signer_path", "");
-
- if (!gArgs.SoftSetArg("-signer", settings.value("external_signer_path").toString().toStdString())) {
- addOverriddenOption("-signer");
- }
-
if (!settings.contains("SubFeeFromAmount")) {
settings.setValue("SubFeeFromAmount", false);
}
m_sub_fee_from_amount = settings.value("SubFeeFromAmount", false).toBool();
#endif
- // Network
- if (!settings.contains("fUseUPnP"))
- settings.setValue("fUseUPnP", DEFAULT_UPNP);
- if (!gArgs.SoftSetBoolArg("-upnp", settings.value("fUseUPnP").toBool()))
- addOverriddenOption("-upnp");
-
- if (!settings.contains("fUseNatpmp")) {
- settings.setValue("fUseNatpmp", DEFAULT_NATPMP);
- }
- if (!gArgs.SoftSetBoolArg("-natpmp", settings.value("fUseNatpmp").toBool())) {
- addOverriddenOption("-natpmp");
- }
-
- if (!settings.contains("fListen"))
- settings.setValue("fListen", DEFAULT_LISTEN);
- const bool listen{settings.value("fListen").toBool()};
- if (!gArgs.SoftSetBoolArg("-listen", listen)) {
- addOverriddenOption("-listen");
- } else if (!listen) {
- // We successfully set -listen=0, thus mimic the logic from InitParameterInteraction():
- // "parameter interaction: -listen=0 -> setting -listenonion=0".
- //
- // Both -listen and -listenonion default to true.
- //
- // The call order is:
- //
- // InitParameterInteraction()
- // would set -listenonion=0 if it sees -listen=0, but for bitcoin-qt with
- // fListen=false -listen is 1 at this point
- //
- // OptionsModel::Init()
- // (this method) can flip -listen from 1 to 0 if fListen=false
- //
- // AppInitParameterInteraction()
- // raises an error if -listen=0 and -listenonion=1
- gArgs.SoftSetBoolArg("-listenonion", false);
- }
-
- if (!settings.contains("server")) {
- settings.setValue("server", false);
- }
- if (!gArgs.SoftSetBoolArg("-server", settings.value("server").toBool())) {
- addOverriddenOption("-server");
- }
-
- if (!settings.contains("fUseProxy"))
- settings.setValue("fUseProxy", false);
- if (!settings.contains("addrProxy"))
- settings.setValue("addrProxy", GetDefaultProxyAddress());
- // Only try to set -proxy, if user has enabled fUseProxy
- if ((settings.value("fUseProxy").toBool() && !gArgs.SoftSetArg("-proxy", settings.value("addrProxy").toString().toStdString())))
- addOverriddenOption("-proxy");
- else if(!settings.value("fUseProxy").toBool() && !gArgs.GetArg("-proxy", "").empty())
- addOverriddenOption("-proxy");
-
- if (!settings.contains("fUseSeparateProxyTor"))
- settings.setValue("fUseSeparateProxyTor", false);
- if (!settings.contains("addrSeparateProxyTor"))
- settings.setValue("addrSeparateProxyTor", GetDefaultProxyAddress());
- // Only try to set -onion, if user has enabled fUseSeparateProxyTor
- if ((settings.value("fUseSeparateProxyTor").toBool() && !gArgs.SoftSetArg("-onion", settings.value("addrSeparateProxyTor").toString().toStdString())))
- addOverriddenOption("-onion");
- else if(!settings.value("fUseSeparateProxyTor").toBool() && !gArgs.GetArg("-onion", "").empty())
- addOverriddenOption("-onion");
-
// Display
- if (!settings.contains("language"))
- settings.setValue("language", "");
- if (!gArgs.SoftSetArg("-lang", settings.value("language").toString().toStdString()))
- addOverriddenOption("-lang");
-
- language = settings.value("language").toString();
-
if (!settings.contains("UseEmbeddedMonospacedFont")) {
settings.setValue("UseEmbeddedMonospacedFont", "true");
}
m_use_embedded_monospaced_font = settings.value("UseEmbeddedMonospacedFont").toBool();
Q_EMIT useEmbeddedMonospacedFontChanged(m_use_embedded_monospaced_font);
+
+ return true;
}
/** Helper function to copy contents from one QSettings to another.
@@ -245,6 +251,9 @@ static void BackupSettings(const fs::path& filename, const QSettings& src)
void OptionsModel::Reset()
{
+ // Backup and reset settings.json
+ node().resetSettings();
+
QSettings settings;
// Backup old settings to chain-specific datadir for troubleshooting
@@ -273,21 +282,15 @@ int OptionsModel::rowCount(const QModelIndex & parent) const
return OptionIDRowCount;
}
-struct ProxySetting {
- bool is_set;
- QString ip;
- QString port;
-};
-
-static ProxySetting GetProxySetting(QSettings &settings, const QString &name)
+static ProxySetting ParseProxyString(const QString& proxy)
{
static const ProxySetting default_val = {false, DEFAULT_GUI_PROXY_HOST, QString("%1").arg(DEFAULT_GUI_PROXY_PORT)};
// Handle the case that the setting is not set at all
- if (!settings.contains(name)) {
+ if (proxy.isEmpty()) {
return default_val;
}
// contains IP at index 0 and port at index 1
- QStringList ip_port = GUIUtil::SplitSkipEmptyParts(settings.value(name).toString(), ":");
+ QStringList ip_port = GUIUtil::SplitSkipEmptyParts(proxy, ":");
if (ip_port.size() == 2) {
return {true, ip_port.at(0), ip_port.at(1)};
} else { // Invalid: return default
@@ -295,9 +298,14 @@ static ProxySetting GetProxySetting(QSettings &settings, const QString &name)
}
}
-static void SetProxySetting(QSettings &settings, const QString &name, const ProxySetting &ip_port)
+static ProxySetting ParseProxyString(const std::string& proxy)
+{
+ return ParseProxyString(QString::fromStdString(proxy));
+}
+
+static std::string ProxyString(bool is_set, QString ip, QString port)
{
- settings.setValue(name, QString{ip_port.ip + QLatin1Char(':') + ip_port.port});
+ return is_set ? QString(ip + ":" + port).toStdString() : "";
}
static const QString GetDefaultProxyAddress()
@@ -305,290 +313,309 @@ static const QString GetDefaultProxyAddress()
return QString("%1:%2").arg(DEFAULT_GUI_PROXY_HOST).arg(DEFAULT_GUI_PROXY_PORT);
}
-void OptionsModel::SetPruneEnabled(bool prune, bool force)
+void OptionsModel::SetPruneTargetGB(int prune_target_gb)
{
- QSettings settings;
- settings.setValue("bPrune", prune);
- const int64_t prune_target_mib = PruneGBtoMiB(settings.value("nPruneSize").toInt());
- std::string prune_val = prune ? ToString(prune_target_mib) : "0";
- if (force) {
- gArgs.ForceSetArg("-prune", prune_val);
- return;
- }
- if (!gArgs.SoftSetArg("-prune", prune_val)) {
- addOverriddenOption("-prune");
+ const util::SettingsValue cur_value = node().getPersistentSetting("prune");
+ const util::SettingsValue new_value = PruneSetting(prune_target_gb > 0, prune_target_gb);
+
+ m_prune_size_gb = prune_target_gb;
+
+ // Force setting to take effect. It is still safe to change the value at
+ // this point because this function is only called after the intro screen is
+ // shown, before the node starts.
+ node().forceSetting("prune", new_value);
+
+ // Update settings.json if value configured in intro screen is different
+ // from saved value. Avoid writing settings.json if bitcoin.conf value
+ // doesn't need to be overridden.
+ if (PruneEnabled(cur_value) != PruneEnabled(new_value) ||
+ PruneSizeGB(cur_value) != PruneSizeGB(new_value)) {
+ // Call UpdateRwSetting() instead of setOption() to avoid setting
+ // RestartRequired flag
+ UpdateRwSetting(node(), Prune, new_value);
}
}
-void OptionsModel::SetPruneTargetGB(int prune_target_gb, bool force)
+// read QSettings values and return them
+QVariant OptionsModel::data(const QModelIndex & index, int role) const
{
- const bool prune = prune_target_gb > 0;
- if (prune) {
- QSettings settings;
- settings.setValue("nPruneSize", prune_target_gb);
+ if(role == Qt::EditRole)
+ {
+ return getOption(OptionID(index.row()));
}
- SetPruneEnabled(prune, force);
+ return QVariant();
}
-// read QSettings values and return them
-QVariant OptionsModel::data(const QModelIndex & index, int role) const
+// write QSettings values
+bool OptionsModel::setData(const QModelIndex & index, const QVariant & value, int role)
{
+ bool successful = true; /* set to false on parse error */
if(role == Qt::EditRole)
{
- QSettings settings;
- switch(index.row())
- {
- case StartAtStartup:
- return GUIUtil::GetStartOnSystemStartup();
- case ShowTrayIcon:
- return m_show_tray_icon;
- case MinimizeToTray:
- return fMinimizeToTray;
- case MapPortUPnP:
+ successful = setOption(OptionID(index.row()), value);
+ }
+
+ Q_EMIT dataChanged(index, index);
+
+ return successful;
+}
+
+QVariant OptionsModel::getOption(OptionID option) const
+{
+ auto setting = [&]{ return node().getPersistentSetting(SettingName(option)); };
+
+ QSettings settings;
+ switch (option) {
+ case StartAtStartup:
+ return GUIUtil::GetStartOnSystemStartup();
+ case ShowTrayIcon:
+ return m_show_tray_icon;
+ case MinimizeToTray:
+ return fMinimizeToTray;
+ case MapPortUPnP:
#ifdef USE_UPNP
- return settings.value("fUseUPnP");
+ return SettingToBool(setting(), DEFAULT_UPNP);
#else
- return false;
+ return false;
#endif // USE_UPNP
- case MapPortNatpmp:
+ case MapPortNatpmp:
#ifdef USE_NATPMP
- return settings.value("fUseNatpmp");
+ return SettingToBool(setting(), DEFAULT_NATPMP);
#else
- return false;
+ return false;
#endif // USE_NATPMP
- case MinimizeOnClose:
- return fMinimizeOnClose;
-
- // default proxy
- case ProxyUse:
- return settings.value("fUseProxy", false);
- case ProxyIP:
- return GetProxySetting(settings, "addrProxy").ip;
- case ProxyPort:
- return GetProxySetting(settings, "addrProxy").port;
-
- // separate Tor proxy
- case ProxyUseTor:
- return settings.value("fUseSeparateProxyTor", false);
- case ProxyIPTor:
- return GetProxySetting(settings, "addrSeparateProxyTor").ip;
- case ProxyPortTor:
- return GetProxySetting(settings, "addrSeparateProxyTor").port;
+ case MinimizeOnClose:
+ return fMinimizeOnClose;
+
+ // default proxy
+ case ProxyUse:
+ return ParseProxyString(SettingToString(setting(), "")).is_set;
+ case ProxyIP:
+ return m_proxy_ip;
+ case ProxyPort:
+ return m_proxy_port;
+
+ // separate Tor proxy
+ case ProxyUseTor:
+ return ParseProxyString(SettingToString(setting(), "")).is_set;
+ case ProxyIPTor:
+ return m_onion_ip;
+ case ProxyPortTor:
+ return m_onion_port;
#ifdef ENABLE_WALLET
- case SpendZeroConfChange:
- return settings.value("bSpendZeroConfChange");
- case ExternalSignerPath:
- return settings.value("external_signer_path");
- case SubFeeFromAmount:
- return m_sub_fee_from_amount;
+ case SpendZeroConfChange:
+ return SettingToBool(setting(), wallet::DEFAULT_SPEND_ZEROCONF_CHANGE);
+ case ExternalSignerPath:
+ return QString::fromStdString(SettingToString(setting(), ""));
+ case SubFeeFromAmount:
+ return m_sub_fee_from_amount;
#endif
- case DisplayUnit:
- return QVariant::fromValue(m_display_bitcoin_unit);
- case ThirdPartyTxUrls:
- return strThirdPartyTxUrls;
- case Language:
- return settings.value("language");
- case UseEmbeddedMonospacedFont:
- return m_use_embedded_monospaced_font;
- case CoinControlFeatures:
- return fCoinControlFeatures;
- case EnablePSBTControls:
- return settings.value("enable_psbt_controls");
- case Prune:
- return settings.value("bPrune");
- case PruneSize:
- return settings.value("nPruneSize");
- case DatabaseCache:
- return settings.value("nDatabaseCache");
- case ThreadsScriptVerif:
- return settings.value("nThreadsScriptVerif");
- case Listen:
- return settings.value("fListen");
- case Server:
- return settings.value("server");
- default:
- return QVariant();
- }
+ case DisplayUnit:
+ return QVariant::fromValue(m_display_bitcoin_unit);
+ case ThirdPartyTxUrls:
+ return strThirdPartyTxUrls;
+ case Language:
+ return QString::fromStdString(SettingToString(setting(), ""));
+ case UseEmbeddedMonospacedFont:
+ return m_use_embedded_monospaced_font;
+ case CoinControlFeatures:
+ return fCoinControlFeatures;
+ case EnablePSBTControls:
+ return settings.value("enable_psbt_controls");
+ case Prune:
+ return PruneEnabled(setting());
+ case PruneSize:
+ return m_prune_size_gb;
+ case DatabaseCache:
+ return qlonglong(SettingToInt(setting(), nDefaultDbCache));
+ case ThreadsScriptVerif:
+ return qlonglong(SettingToInt(setting(), DEFAULT_SCRIPTCHECK_THREADS));
+ case Listen:
+ return SettingToBool(setting(), DEFAULT_LISTEN);
+ case Server:
+ return SettingToBool(setting(), false);
+ default:
+ return QVariant();
}
- return QVariant();
}
-// write QSettings values
-bool OptionsModel::setData(const QModelIndex & index, const QVariant & value, int role)
+bool OptionsModel::setOption(OptionID option, const QVariant& value)
{
+ auto changed = [&] { return value.isValid() && value != getOption(option); };
+ auto update = [&](const util::SettingsValue& value) { return UpdateRwSetting(node(), option, value); };
+
bool successful = true; /* set to false on parse error */
- if(role == Qt::EditRole)
- {
- QSettings settings;
- switch(index.row())
- {
- case StartAtStartup:
- successful = GUIUtil::SetStartOnSystemStartup(value.toBool());
- break;
- case ShowTrayIcon:
- m_show_tray_icon = value.toBool();
- settings.setValue("fHideTrayIcon", !m_show_tray_icon);
- Q_EMIT showTrayIconChanged(m_show_tray_icon);
- break;
- case MinimizeToTray:
- fMinimizeToTray = value.toBool();
- settings.setValue("fMinimizeToTray", fMinimizeToTray);
- break;
- case MapPortUPnP: // core option - can be changed on-the-fly
- settings.setValue("fUseUPnP", value.toBool());
- break;
- case MapPortNatpmp: // core option - can be changed on-the-fly
- settings.setValue("fUseNatpmp", value.toBool());
- break;
- case MinimizeOnClose:
- fMinimizeOnClose = value.toBool();
- settings.setValue("fMinimizeOnClose", fMinimizeOnClose);
- break;
-
- // default proxy
- case ProxyUse:
- if (settings.value("fUseProxy") != value) {
- settings.setValue("fUseProxy", value.toBool());
- setRestartRequired(true);
- }
- break;
- case ProxyIP: {
- auto ip_port = GetProxySetting(settings, "addrProxy");
- if (!ip_port.is_set || ip_port.ip != value.toString()) {
- ip_port.ip = value.toString();
- SetProxySetting(settings, "addrProxy", ip_port);
+ QSettings settings;
+
+ switch (option) {
+ case StartAtStartup:
+ successful = GUIUtil::SetStartOnSystemStartup(value.toBool());
+ break;
+ case ShowTrayIcon:
+ m_show_tray_icon = value.toBool();
+ settings.setValue("fHideTrayIcon", !m_show_tray_icon);
+ Q_EMIT showTrayIconChanged(m_show_tray_icon);
+ break;
+ case MinimizeToTray:
+ fMinimizeToTray = value.toBool();
+ settings.setValue("fMinimizeToTray", fMinimizeToTray);
+ break;
+ case MapPortUPnP: // core option - can be changed on-the-fly
+ if (changed()) {
+ update(value.toBool());
+ node().mapPort(value.toBool(), getOption(MapPortNatpmp).toBool());
+ }
+ break;
+ case MapPortNatpmp: // core option - can be changed on-the-fly
+ if (changed()) {
+ update(value.toBool());
+ node().mapPort(getOption(MapPortUPnP).toBool(), value.toBool());
+ }
+ break;
+ case MinimizeOnClose:
+ fMinimizeOnClose = value.toBool();
+ settings.setValue("fMinimizeOnClose", fMinimizeOnClose);
+ break;
+
+ // default proxy
+ case ProxyUse:
+ if (changed()) {
+ update(ProxyString(value.toBool(), m_proxy_ip, m_proxy_port));
+ setRestartRequired(true);
+ }
+ break;
+ case ProxyIP:
+ if (changed()) {
+ m_proxy_ip = value.toString();
+ if (getOption(ProxyUse).toBool()) {
+ update(ProxyString(true, m_proxy_ip, m_proxy_port));
setRestartRequired(true);
}
}
break;
- case ProxyPort: {
- auto ip_port = GetProxySetting(settings, "addrProxy");
- if (!ip_port.is_set || ip_port.port != value.toString()) {
- ip_port.port = value.toString();
- SetProxySetting(settings, "addrProxy", ip_port);
+ case ProxyPort:
+ if (changed()) {
+ m_proxy_port = value.toString();
+ if (getOption(ProxyUse).toBool()) {
+ update(ProxyString(true, m_proxy_ip, m_proxy_port));
setRestartRequired(true);
}
}
break;
- // separate Tor proxy
- case ProxyUseTor:
- if (settings.value("fUseSeparateProxyTor") != value) {
- settings.setValue("fUseSeparateProxyTor", value.toBool());
- setRestartRequired(true);
- }
- break;
- case ProxyIPTor: {
- auto ip_port = GetProxySetting(settings, "addrSeparateProxyTor");
- if (!ip_port.is_set || ip_port.ip != value.toString()) {
- ip_port.ip = value.toString();
- SetProxySetting(settings, "addrSeparateProxyTor", ip_port);
+ // separate Tor proxy
+ case ProxyUseTor:
+ if (changed()) {
+ update(ProxyString(value.toBool(), m_onion_ip, m_onion_port));
+ setRestartRequired(true);
+ }
+ break;
+ case ProxyIPTor:
+ if (changed()) {
+ m_onion_ip = value.toString();
+ if (getOption(ProxyUseTor).toBool()) {
+ update(ProxyString(true, m_onion_ip, m_onion_port));
setRestartRequired(true);
}
}
break;
- case ProxyPortTor: {
- auto ip_port = GetProxySetting(settings, "addrSeparateProxyTor");
- if (!ip_port.is_set || ip_port.port != value.toString()) {
- ip_port.port = value.toString();
- SetProxySetting(settings, "addrSeparateProxyTor", ip_port);
+ case ProxyPortTor:
+ if (changed()) {
+ m_onion_port = value.toString();
+ if (getOption(ProxyUseTor).toBool()) {
+ update(ProxyString(true, m_onion_ip, m_onion_port));
setRestartRequired(true);
}
}
break;
#ifdef ENABLE_WALLET
- case SpendZeroConfChange:
- if (settings.value("bSpendZeroConfChange") != value) {
- settings.setValue("bSpendZeroConfChange", value);
- setRestartRequired(true);
- }
- break;
- case ExternalSignerPath:
- if (settings.value("external_signer_path") != value.toString()) {
- settings.setValue("external_signer_path", value.toString());
- setRestartRequired(true);
- }
- break;
- case SubFeeFromAmount:
- m_sub_fee_from_amount = value.toBool();
- settings.setValue("SubFeeFromAmount", m_sub_fee_from_amount);
- break;
+ case SpendZeroConfChange:
+ if (changed()) {
+ update(value.toBool());
+ setRestartRequired(true);
+ }
+ break;
+ case ExternalSignerPath:
+ if (changed()) {
+ update(value.toString().toStdString());
+ setRestartRequired(true);
+ }
+ break;
+ case SubFeeFromAmount:
+ m_sub_fee_from_amount = value.toBool();
+ settings.setValue("SubFeeFromAmount", m_sub_fee_from_amount);
+ break;
#endif
- case DisplayUnit:
- setDisplayUnit(value);
- break;
- case ThirdPartyTxUrls:
- if (strThirdPartyTxUrls != value.toString()) {
- strThirdPartyTxUrls = value.toString();
- settings.setValue("strThirdPartyTxUrls", strThirdPartyTxUrls);
- setRestartRequired(true);
- }
- break;
- case Language:
- if (settings.value("language") != value) {
- settings.setValue("language", value);
- setRestartRequired(true);
- }
- break;
- case UseEmbeddedMonospacedFont:
- m_use_embedded_monospaced_font = value.toBool();
- settings.setValue("UseEmbeddedMonospacedFont", m_use_embedded_monospaced_font);
- Q_EMIT useEmbeddedMonospacedFontChanged(m_use_embedded_monospaced_font);
- break;
- case CoinControlFeatures:
- fCoinControlFeatures = value.toBool();
- settings.setValue("fCoinControlFeatures", fCoinControlFeatures);
- Q_EMIT coinControlFeaturesChanged(fCoinControlFeatures);
- break;
- case EnablePSBTControls:
- m_enable_psbt_controls = value.toBool();
- settings.setValue("enable_psbt_controls", m_enable_psbt_controls);
- break;
- case Prune:
- if (settings.value("bPrune") != value) {
- settings.setValue("bPrune", value);
- setRestartRequired(true);
- }
- break;
- case PruneSize:
- if (settings.value("nPruneSize") != value) {
- settings.setValue("nPruneSize", value);
- setRestartRequired(true);
- }
- break;
- case DatabaseCache:
- if (settings.value("nDatabaseCache") != value) {
- settings.setValue("nDatabaseCache", value);
- setRestartRequired(true);
- }
- break;
- case ThreadsScriptVerif:
- if (settings.value("nThreadsScriptVerif") != value) {
- settings.setValue("nThreadsScriptVerif", value);
- setRestartRequired(true);
- }
- break;
- case Listen:
- if (settings.value("fListen") != value) {
- settings.setValue("fListen", value);
- setRestartRequired(true);
- }
- break;
- case Server:
- if (settings.value("server") != value) {
- settings.setValue("server", value);
+ case DisplayUnit:
+ setDisplayUnit(value);
+ break;
+ case ThirdPartyTxUrls:
+ if (strThirdPartyTxUrls != value.toString()) {
+ strThirdPartyTxUrls = value.toString();
+ settings.setValue("strThirdPartyTxUrls", strThirdPartyTxUrls);
+ setRestartRequired(true);
+ }
+ break;
+ case Language:
+ if (changed()) {
+ update(value.toString().toStdString());
+ setRestartRequired(true);
+ }
+ break;
+ case UseEmbeddedMonospacedFont:
+ m_use_embedded_monospaced_font = value.toBool();
+ settings.setValue("UseEmbeddedMonospacedFont", m_use_embedded_monospaced_font);
+ Q_EMIT useEmbeddedMonospacedFontChanged(m_use_embedded_monospaced_font);
+ break;
+ case CoinControlFeatures:
+ fCoinControlFeatures = value.toBool();
+ settings.setValue("fCoinControlFeatures", fCoinControlFeatures);
+ Q_EMIT coinControlFeaturesChanged(fCoinControlFeatures);
+ break;
+ case EnablePSBTControls:
+ m_enable_psbt_controls = value.toBool();
+ settings.setValue("enable_psbt_controls", m_enable_psbt_controls);
+ break;
+ case Prune:
+ if (changed()) {
+ update(PruneSetting(value.toBool(), m_prune_size_gb));
+ setRestartRequired(true);
+ }
+ break;
+ case PruneSize:
+ if (changed()) {
+ m_prune_size_gb = ParsePruneSizeGB(value);
+ if (getOption(Prune).toBool()) {
+ update(PruneSetting(true, m_prune_size_gb));
setRestartRequired(true);
}
- break;
- default:
- break;
}
+ break;
+ case DatabaseCache:
+ if (changed()) {
+ update(static_cast<int64_t>(value.toLongLong()));
+ setRestartRequired(true);
+ }
+ break;
+ case ThreadsScriptVerif:
+ if (changed()) {
+ update(static_cast<int64_t>(value.toLongLong()));
+ setRestartRequired(true);
+ }
+ break;
+ case Listen:
+ case Server:
+ if (changed()) {
+ update(value.toBool());
+ setRestartRequired(true);
+ }
+ break;
+ default:
+ break;
}
- Q_EMIT dataChanged(index, index);
-
return successful;
}
@@ -642,4 +669,49 @@ void OptionsModel::checkAndMigrate()
if (settings.contains("addrSeparateProxyTor") && settings.value("addrSeparateProxyTor").toString().endsWith("%2")) {
settings.setValue("addrSeparateProxyTor", GetDefaultProxyAddress());
}
+
+ // Migrate and delete legacy GUI settings that have now moved to <datadir>/settings.json.
+ auto migrate_setting = [&](OptionID option, const QString& qt_name) {
+ if (!settings.contains(qt_name)) return;
+ QVariant value = settings.value(qt_name);
+ if (node().getPersistentSetting(SettingName(option)).isNull()) {
+ if (option == ProxyIP) {
+ ProxySetting parsed = ParseProxyString(value.toString());
+ setOption(ProxyIP, parsed.ip);
+ setOption(ProxyPort, parsed.port);
+ } else if (option == ProxyIPTor) {
+ ProxySetting parsed = ParseProxyString(value.toString());
+ setOption(ProxyIPTor, parsed.ip);
+ setOption(ProxyPortTor, parsed.port);
+ } else {
+ setOption(option, value);
+ }
+ }
+ settings.remove(qt_name);
+ };
+
+ migrate_setting(DatabaseCache, "nDatabaseCache");
+ migrate_setting(ThreadsScriptVerif, "nThreadsScriptVerif");
+#ifdef ENABLE_WALLET
+ migrate_setting(SpendZeroConfChange, "bSpendZeroConfChange");
+ migrate_setting(ExternalSignerPath, "external_signer_path");
+#endif
+ migrate_setting(MapPortUPnP, "fUseUPnP");
+ migrate_setting(MapPortNatpmp, "fUseNatpmp");
+ migrate_setting(Listen, "fListen");
+ migrate_setting(Server, "server");
+ migrate_setting(PruneSize, "nPruneSize");
+ migrate_setting(Prune, "bPrune");
+ migrate_setting(ProxyIP, "addrProxy");
+ migrate_setting(ProxyUse, "fUseProxy");
+ migrate_setting(ProxyIPTor, "addrSeparateProxyTor");
+ migrate_setting(ProxyUseTor, "fUseSeparateProxyTor");
+ migrate_setting(Language, "language");
+
+ // In case migrating QSettings caused any settings value to change, rerun
+ // parameter interaction code to update other settings. This is particularly
+ // important for the -listen setting, which should cause -listenonion, -upnp,
+ // and other settings to default to false if it was set to false.
+ // (https://github.com/bitcoin-core/gui/issues/567).
+ node().initParameterInteraction();
}