2021-12-30 19:36:57 +02:00
|
|
|
// Copyright (c) 2011-2021 The Bitcoin Core developers
|
2014-12-13 12:09:33 +08:00
|
|
|
// Distributed under the MIT software license, see the accompanying
|
2013-11-04 16:20:43 +01:00
|
|
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
|
|
|
2013-05-27 19:55:01 -04:00
|
|
|
#if defined(HAVE_CONFIG_H)
|
2017-11-10 13:57:53 +13:00
|
|
|
#include <config/bitcoin-config.h>
|
2013-05-27 19:55:01 -04:00
|
|
|
#endif
|
|
|
|
|
2017-11-10 13:57:53 +13:00
|
|
|
#include <qt/optionsmodel.h>
|
2011-06-26 19:23:24 +02:00
|
|
|
|
2017-11-10 13:57:53 +13:00
|
|
|
#include <qt/bitcoinunits.h>
|
2019-01-14 13:40:00 +02:00
|
|
|
#include <qt/guiconstants.h>
|
2017-11-10 13:57:53 +13:00
|
|
|
#include <qt/guiutil.h>
|
2013-04-13 00:13:08 -05:00
|
|
|
|
2018-04-07 03:42:02 -04:00
|
|
|
#include <interfaces/node.h>
|
2020-02-06 19:00:26 +02:00
|
|
|
#include <mapport.h>
|
2017-11-10 13:57:53 +13:00
|
|
|
#include <net.h>
|
|
|
|
#include <netbase.h>
|
2020-02-06 19:00:26 +02:00
|
|
|
#include <txdb.h> // for -dbcache defaults
|
2020-02-12 23:01:45 -05:00
|
|
|
#include <util/string.h>
|
2020-02-06 19:00:26 +02:00
|
|
|
#include <validation.h> // For DEFAULT_SCRIPTCHECK_THREADS
|
2019-04-29 15:29:00 -04:00
|
|
|
#include <wallet/wallet.h> // For DEFAULT_SPEND_ZEROCONF_CHANGE
|
2014-09-05 13:18:35 +02:00
|
|
|
|
2019-10-12 12:49:29 -04:00
|
|
|
#include <QDebug>
|
2021-05-03 11:16:06 +03:00
|
|
|
#include <QLatin1Char>
|
2013-01-23 21:51:02 +01:00
|
|
|
#include <QSettings>
|
2013-12-03 09:10:10 +01:00
|
|
|
#include <QStringList>
|
2021-01-21 22:27:28 +02:00
|
|
|
#include <QVariant>
|
2013-01-23 21:51:02 +01:00
|
|
|
|
2019-04-29 15:29:00 -04:00
|
|
|
#include <univalue.h>
|
|
|
|
|
2017-12-01 12:08:31 +01:00
|
|
|
const char *DEFAULT_GUI_PROXY_HOST = "127.0.0.1";
|
|
|
|
|
2018-03-08 20:58:57 +01:00
|
|
|
static const QString GetDefaultProxyAddress();
|
|
|
|
|
2019-04-29 15:29:00 -04:00
|
|
|
/** Map GUI option ID to node setting name. */
|
|
|
|
static const char* SettingName(OptionsModel::OptionID option)
|
|
|
|
{
|
|
|
|
switch (option) {
|
|
|
|
case OptionsModel::DatabaseCache: return "dbcache";
|
2019-04-29 15:29:00 -04:00
|
|
|
case OptionsModel::ThreadsScriptVerif: return "par";
|
2019-04-29 15:29:00 -04:00
|
|
|
case OptionsModel::SpendZeroConfChange: return "spendzeroconfchange";
|
|
|
|
case OptionsModel::ExternalSignerPath: return "signer";
|
2022-04-18 19:19:02 -04:00
|
|
|
case OptionsModel::MapPortUPnP: return "upnp";
|
|
|
|
case OptionsModel::MapPortNatpmp: return "natpmp";
|
2019-04-29 15:29:00 -04:00
|
|
|
case OptionsModel::Listen: return "listen";
|
|
|
|
case OptionsModel::Server: return "server";
|
2019-04-29 15:29:00 -04:00
|
|
|
case OptionsModel::PruneSize: return "prune";
|
|
|
|
case OptionsModel::Prune: return "prune";
|
2019-04-29 15:29:00 -04:00
|
|
|
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";
|
2019-04-29 15:29:00 -04:00
|
|
|
case OptionsModel::Language: return "lang";
|
2019-04-29 15:29:00 -04:00
|
|
|
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)
|
|
|
|
{
|
2019-04-29 15:29:00 -04:00
|
|
|
if (value.isNum() &&
|
|
|
|
(option == OptionsModel::DatabaseCache ||
|
2019-04-29 15:29:00 -04:00
|
|
|
option == OptionsModel::ThreadsScriptVerif ||
|
|
|
|
option == OptionsModel::Prune ||
|
|
|
|
option == OptionsModel::PruneSize)) {
|
2019-04-29 15:29:00 -04:00
|
|
|
// 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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-29 15:29:00 -04:00
|
|
|
//! 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());
|
|
|
|
}
|
|
|
|
|
2019-04-29 15:29:00 -04:00
|
|
|
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);
|
|
|
|
|
2019-04-29 15:29:00 -04:00
|
|
|
OptionsModel::OptionsModel(interfaces::Node& node, QObject *parent) :
|
2019-04-29 15:29:00 -04:00
|
|
|
QAbstractListModel(parent), m_node{node}
|
2011-05-31 22:24:53 +02:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2014-03-14 07:22:59 +01:00
|
|
|
void OptionsModel::addOverriddenOption(const std::string &option)
|
|
|
|
{
|
2017-08-01 21:17:40 +02:00
|
|
|
strOverriddenByCommandLine += QString::fromStdString(option) + "=" + QString::fromStdString(gArgs.GetArg(option, "")) + " ";
|
2014-03-14 07:22:59 +01:00
|
|
|
}
|
|
|
|
|
2013-12-03 09:10:10 +01:00
|
|
|
// Writes all missing QSettings with their default values
|
2019-04-29 15:29:00 -04:00
|
|
|
bool OptionsModel::Init(bilingual_str& error)
|
2012-02-16 21:09:41 -05:00
|
|
|
{
|
2019-04-29 15:29:00 -04:00
|
|
|
// Initialize display settings from stored settings.
|
2019-04-29 15:29:00 -04:00
|
|
|
m_prune_size_gb = PruneSizeGB(node().getPersistentSetting("prune"));
|
2019-04-29 15:29:00 -04:00
|
|
|
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;
|
2019-04-29 15:29:00 -04:00
|
|
|
language = QString::fromStdString(SettingToString(node().getPersistentSetting("lang"), ""));
|
2019-04-29 15:29:00 -04:00
|
|
|
|
2016-07-26 14:01:36 +02:00
|
|
|
checkAndMigrate();
|
|
|
|
|
2012-02-16 21:09:41 -05:00
|
|
|
QSettings settings;
|
|
|
|
|
2013-12-03 09:10:10 +01:00
|
|
|
// Ensure restart flag is unset on client startup
|
|
|
|
setRestartRequired(false);
|
|
|
|
|
2012-07-07 16:35:29 +02:00
|
|
|
// These are Qt-only settings:
|
2013-12-03 09:10:10 +01:00
|
|
|
|
|
|
|
// Window
|
2020-10-25 00:34:41 +03:00
|
|
|
if (!settings.contains("fHideTrayIcon")) {
|
2016-05-11 22:28:02 -04:00
|
|
|
settings.setValue("fHideTrayIcon", false);
|
2020-10-25 00:34:41 +03:00
|
|
|
}
|
|
|
|
m_show_tray_icon = !settings.value("fHideTrayIcon").toBool();
|
|
|
|
Q_EMIT showTrayIconChanged(m_show_tray_icon);
|
2018-07-24 16:59:49 +01:00
|
|
|
|
2013-12-03 09:10:10 +01:00
|
|
|
if (!settings.contains("fMinimizeToTray"))
|
|
|
|
settings.setValue("fMinimizeToTray", false);
|
2020-10-25 00:34:41 +03:00
|
|
|
fMinimizeToTray = settings.value("fMinimizeToTray").toBool() && m_show_tray_icon;
|
2013-12-03 09:10:10 +01:00
|
|
|
|
|
|
|
if (!settings.contains("fMinimizeOnClose"))
|
|
|
|
settings.setValue("fMinimizeOnClose", false);
|
|
|
|
fMinimizeOnClose = settings.value("fMinimizeOnClose").toBool();
|
|
|
|
|
|
|
|
// Display
|
2021-01-21 22:27:28 +02:00
|
|
|
if (!settings.contains("DisplayBitcoinUnit")) {
|
|
|
|
settings.setValue("DisplayBitcoinUnit", QVariant::fromValue(BitcoinUnit::BTC));
|
|
|
|
}
|
|
|
|
QVariant unit = settings.value("DisplayBitcoinUnit");
|
|
|
|
if (unit.canConvert<BitcoinUnit>()) {
|
|
|
|
m_display_bitcoin_unit = unit.value<BitcoinUnit>();
|
|
|
|
} else {
|
|
|
|
m_display_bitcoin_unit = BitcoinUnit::BTC;
|
|
|
|
settings.setValue("DisplayBitcoinUnit", QVariant::fromValue(m_display_bitcoin_unit));
|
|
|
|
}
|
2013-12-03 09:10:10 +01:00
|
|
|
|
2014-04-24 22:21:45 +02:00
|
|
|
if (!settings.contains("strThirdPartyTxUrls"))
|
|
|
|
settings.setValue("strThirdPartyTxUrls", "");
|
|
|
|
strThirdPartyTxUrls = settings.value("strThirdPartyTxUrls", "").toString();
|
|
|
|
|
2013-12-03 09:10:10 +01:00
|
|
|
if (!settings.contains("fCoinControlFeatures"))
|
|
|
|
settings.setValue("fCoinControlFeatures", false);
|
2013-08-12 17:03:03 +02:00
|
|
|
fCoinControlFeatures = settings.value("fCoinControlFeatures", false).toBool();
|
2012-02-16 21:09:41 -05:00
|
|
|
|
2020-04-28 15:29:12 -04:00
|
|
|
if (!settings.contains("enable_psbt_controls")) {
|
|
|
|
settings.setValue("enable_psbt_controls", false);
|
|
|
|
}
|
|
|
|
m_enable_psbt_controls = settings.value("enable_psbt_controls", false).toBool();
|
|
|
|
|
2013-12-03 09:10:10 +01:00
|
|
|
// These are shared with the core or have a command-line parameter
|
|
|
|
// and we want command-line parameters to overwrite the GUI settings.
|
2022-04-18 19:19:02 -04:00
|
|
|
for (OptionID option : {DatabaseCache, ThreadsScriptVerif, SpendZeroConfChange, ExternalSignerPath, MapPortUPnP,
|
2019-04-29 15:29:00 -04:00
|
|
|
MapPortNatpmp, Listen, Server, Prune, ProxyUse, ProxyUseTor, Language}) {
|
2019-04-29 15:29:00 -04:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-12-03 09:10:10 +01:00
|
|
|
// If setting doesn't exist create it with defaults.
|
|
|
|
|
|
|
|
// Main
|
2016-08-08 14:17:23 -04:00
|
|
|
if (!settings.contains("strDataDir"))
|
2019-04-23 13:26:06 +02:00
|
|
|
settings.setValue("strDataDir", GUIUtil::getDefaultDataDirectory());
|
2016-08-08 14:17:23 -04:00
|
|
|
|
2014-02-18 12:48:16 +01:00
|
|
|
// Wallet
|
2013-12-11 15:00:56 +01:00
|
|
|
#ifdef ENABLE_WALLET
|
2021-07-29 21:57:57 +05:30
|
|
|
if (!settings.contains("SubFeeFromAmount")) {
|
|
|
|
settings.setValue("SubFeeFromAmount", false);
|
|
|
|
}
|
|
|
|
m_sub_fee_from_amount = settings.value("SubFeeFromAmount", false).toBool();
|
2013-12-11 15:00:56 +01:00
|
|
|
#endif
|
2013-12-03 09:10:10 +01:00
|
|
|
|
|
|
|
// Display
|
2021-02-21 20:58:19 +02:00
|
|
|
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);
|
2019-04-29 15:29:00 -04:00
|
|
|
|
|
|
|
return true;
|
2012-02-16 21:09:41 -05:00
|
|
|
}
|
|
|
|
|
2017-09-15 11:50:45 +02:00
|
|
|
/** Helper function to copy contents from one QSettings to another.
|
|
|
|
* By using allKeys this also covers nested settings in a hierarchy.
|
|
|
|
*/
|
|
|
|
static void CopySettings(QSettings& dst, const QSettings& src)
|
|
|
|
{
|
|
|
|
for (const QString& key : src.allKeys()) {
|
|
|
|
dst.setValue(key, src.value(key));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Back up a QSettings to an ini-formatted file. */
|
|
|
|
static void BackupSettings(const fs::path& filename, const QSettings& src)
|
|
|
|
{
|
2022-01-11 18:32:11 +02:00
|
|
|
qInfo() << "Backing up GUI settings to" << GUIUtil::PathToQString(filename);
|
|
|
|
QSettings dst(GUIUtil::PathToQString(filename), QSettings::IniFormat);
|
2017-09-15 11:50:45 +02:00
|
|
|
dst.clear();
|
|
|
|
CopySettings(dst, src);
|
|
|
|
}
|
|
|
|
|
2012-08-18 15:54:39 +02:00
|
|
|
void OptionsModel::Reset()
|
|
|
|
{
|
|
|
|
QSettings settings;
|
|
|
|
|
2017-09-15 11:50:45 +02:00
|
|
|
// Backup old settings to chain-specific datadir for troubleshooting
|
2021-05-04 13:31:18 +02:00
|
|
|
BackupSettings(gArgs.GetDataDirNet() / "guisettings.ini.bak", settings);
|
2017-09-15 11:50:45 +02:00
|
|
|
|
2016-08-08 14:17:23 -04:00
|
|
|
// Save the strDataDir setting
|
2019-04-23 13:26:06 +02:00
|
|
|
QString dataDir = GUIUtil::getDefaultDataDirectory();
|
2016-08-08 14:17:23 -04:00
|
|
|
dataDir = settings.value("strDataDir", dataDir).toString();
|
|
|
|
|
2013-12-03 09:10:10 +01:00
|
|
|
// Remove all entries from our QSettings object
|
2012-08-18 15:54:39 +02:00
|
|
|
settings.clear();
|
|
|
|
|
2016-08-08 14:17:23 -04:00
|
|
|
// Set strDataDir
|
|
|
|
settings.setValue("strDataDir", dataDir);
|
|
|
|
|
2016-08-10 15:35:22 -04:00
|
|
|
// Set that this was reset
|
|
|
|
settings.setValue("fReset", true);
|
|
|
|
|
2012-08-18 15:54:39 +02:00
|
|
|
// default setting for OptionsModel::StartAtStartup - disabled
|
|
|
|
if (GUIUtil::GetStartOnSystemStartup())
|
|
|
|
GUIUtil::SetStartOnSystemStartup(false);
|
2012-02-16 21:09:41 -05:00
|
|
|
}
|
|
|
|
|
2011-05-31 22:24:53 +02:00
|
|
|
int OptionsModel::rowCount(const QModelIndex & parent) const
|
|
|
|
{
|
|
|
|
return OptionIDRowCount;
|
|
|
|
}
|
|
|
|
|
2019-04-29 15:29:00 -04:00
|
|
|
static ProxySetting ParseProxyString(const QString& proxy)
|
2017-12-01 12:08:31 +01:00
|
|
|
{
|
|
|
|
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
|
2019-04-29 15:29:00 -04:00
|
|
|
if (proxy.isEmpty()) {
|
2017-12-01 12:08:31 +01:00
|
|
|
return default_val;
|
|
|
|
}
|
|
|
|
// contains IP at index 0 and port at index 1
|
2019-04-29 15:29:00 -04:00
|
|
|
QStringList ip_port = GUIUtil::SplitSkipEmptyParts(proxy, ":");
|
2017-12-01 12:08:31 +01:00
|
|
|
if (ip_port.size() == 2) {
|
|
|
|
return {true, ip_port.at(0), ip_port.at(1)};
|
|
|
|
} else { // Invalid: return default
|
|
|
|
return default_val;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-29 15:29:00 -04:00
|
|
|
static ProxySetting ParseProxyString(const std::string& proxy)
|
2017-12-01 12:08:31 +01:00
|
|
|
{
|
2019-04-29 15:29:00 -04:00
|
|
|
return ParseProxyString(QString::fromStdString(proxy));
|
|
|
|
}
|
|
|
|
|
|
|
|
static std::string ProxyString(bool is_set, QString ip, QString port)
|
|
|
|
{
|
|
|
|
return is_set ? QString(ip + ":" + port).toStdString() : "";
|
2017-12-01 12:08:31 +01:00
|
|
|
}
|
|
|
|
|
2018-03-08 20:58:57 +01:00
|
|
|
static const QString GetDefaultProxyAddress()
|
|
|
|
{
|
|
|
|
return QString("%1:%2").arg(DEFAULT_GUI_PROXY_HOST).arg(DEFAULT_GUI_PROXY_PORT);
|
|
|
|
}
|
|
|
|
|
2019-04-29 15:29:00 -04:00
|
|
|
void OptionsModel::SetPruneTargetGB(int prune_target_gb)
|
2019-08-24 22:31:14 +02:00
|
|
|
{
|
2019-04-29 15:29:00 -04:00
|
|
|
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);
|
2019-11-12 12:09:10 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-12-03 09:10:10 +01:00
|
|
|
// read QSettings values and return them
|
2011-05-31 22:24:53 +02:00
|
|
|
QVariant OptionsModel::data(const QModelIndex & index, int role) const
|
|
|
|
{
|
|
|
|
if(role == Qt::EditRole)
|
|
|
|
{
|
2019-04-29 15:29:00 -04:00
|
|
|
return getOption(OptionID(index.row()));
|
|
|
|
}
|
|
|
|
return QVariant();
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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)
|
|
|
|
{
|
|
|
|
successful = setOption(OptionID(index.row()), value);
|
|
|
|
}
|
|
|
|
|
|
|
|
Q_EMIT dataChanged(index, index);
|
|
|
|
|
|
|
|
return successful;
|
|
|
|
}
|
|
|
|
|
|
|
|
QVariant OptionsModel::getOption(OptionID option) const
|
|
|
|
{
|
2019-04-29 15:29:00 -04:00
|
|
|
auto setting = [&]{ return node().getPersistentSetting(SettingName(option)); };
|
|
|
|
|
2019-04-29 15:29:00 -04:00
|
|
|
QSettings settings;
|
|
|
|
switch (option) {
|
|
|
|
case StartAtStartup:
|
|
|
|
return GUIUtil::GetStartOnSystemStartup();
|
|
|
|
case ShowTrayIcon:
|
|
|
|
return m_show_tray_icon;
|
|
|
|
case MinimizeToTray:
|
|
|
|
return fMinimizeToTray;
|
|
|
|
case MapPortUPnP:
|
2012-12-12 11:31:30 +04:00
|
|
|
#ifdef USE_UPNP
|
2022-04-18 19:19:02 -04:00
|
|
|
return SettingToBool(setting(), DEFAULT_UPNP);
|
2012-12-12 11:31:30 +04:00
|
|
|
#else
|
2019-04-29 15:29:00 -04:00
|
|
|
return false;
|
2020-02-23 02:12:19 +02:00
|
|
|
#endif // USE_UPNP
|
2019-04-29 15:29:00 -04:00
|
|
|
case MapPortNatpmp:
|
2020-02-23 02:12:19 +02:00
|
|
|
#ifdef USE_NATPMP
|
2022-04-18 19:19:02 -04:00
|
|
|
return SettingToBool(setting(), DEFAULT_NATPMP);
|
2020-02-23 02:12:19 +02:00
|
|
|
#else
|
2019-04-29 15:29:00 -04:00
|
|
|
return false;
|
2020-02-23 02:12:19 +02:00
|
|
|
#endif // USE_NATPMP
|
2019-04-29 15:29:00 -04:00
|
|
|
case MinimizeOnClose:
|
|
|
|
return fMinimizeOnClose;
|
|
|
|
|
|
|
|
// default proxy
|
|
|
|
case ProxyUse:
|
2019-04-29 15:29:00 -04:00
|
|
|
return ParseProxyString(SettingToString(setting(), "")).is_set;
|
2019-04-29 15:29:00 -04:00
|
|
|
case ProxyIP:
|
2019-04-29 15:29:00 -04:00
|
|
|
return m_proxy_ip;
|
2019-04-29 15:29:00 -04:00
|
|
|
case ProxyPort:
|
2019-04-29 15:29:00 -04:00
|
|
|
return m_proxy_port;
|
2019-04-29 15:29:00 -04:00
|
|
|
|
|
|
|
// separate Tor proxy
|
|
|
|
case ProxyUseTor:
|
2019-04-29 15:29:00 -04:00
|
|
|
return ParseProxyString(SettingToString(setting(), "")).is_set;
|
2019-04-29 15:29:00 -04:00
|
|
|
case ProxyIPTor:
|
2019-04-29 15:29:00 -04:00
|
|
|
return m_onion_ip;
|
2019-04-29 15:29:00 -04:00
|
|
|
case ProxyPortTor:
|
2019-04-29 15:29:00 -04:00
|
|
|
return m_onion_port;
|
2014-07-25 18:20:40 +02:00
|
|
|
|
2013-12-11 15:00:56 +01:00
|
|
|
#ifdef ENABLE_WALLET
|
2019-04-29 15:29:00 -04:00
|
|
|
case SpendZeroConfChange:
|
2019-04-29 15:29:00 -04:00
|
|
|
return SettingToBool(setting(), wallet::DEFAULT_SPEND_ZEROCONF_CHANGE);
|
2019-04-29 15:29:00 -04:00
|
|
|
case ExternalSignerPath:
|
2019-04-29 15:29:00 -04:00
|
|
|
return QString::fromStdString(SettingToString(setting(), ""));
|
2019-04-29 15:29:00 -04:00
|
|
|
case SubFeeFromAmount:
|
|
|
|
return m_sub_fee_from_amount;
|
2013-12-11 15:00:56 +01:00
|
|
|
#endif
|
2019-04-29 15:29:00 -04:00
|
|
|
case DisplayUnit:
|
|
|
|
return QVariant::fromValue(m_display_bitcoin_unit);
|
|
|
|
case ThirdPartyTxUrls:
|
|
|
|
return strThirdPartyTxUrls;
|
|
|
|
case Language:
|
2019-04-29 15:29:00 -04:00
|
|
|
return QString::fromStdString(SettingToString(setting(), ""));
|
2019-04-29 15:29:00 -04:00
|
|
|
case UseEmbeddedMonospacedFont:
|
|
|
|
return m_use_embedded_monospaced_font;
|
|
|
|
case CoinControlFeatures:
|
|
|
|
return fCoinControlFeatures;
|
|
|
|
case EnablePSBTControls:
|
|
|
|
return settings.value("enable_psbt_controls");
|
|
|
|
case Prune:
|
2019-04-29 15:29:00 -04:00
|
|
|
return PruneEnabled(setting());
|
2019-04-29 15:29:00 -04:00
|
|
|
case PruneSize:
|
2019-04-29 15:29:00 -04:00
|
|
|
return m_prune_size_gb;
|
2019-04-29 15:29:00 -04:00
|
|
|
case DatabaseCache:
|
2019-04-29 15:29:00 -04:00
|
|
|
return qlonglong(SettingToInt(setting(), nDefaultDbCache));
|
2019-04-29 15:29:00 -04:00
|
|
|
case ThreadsScriptVerif:
|
2019-04-29 15:29:00 -04:00
|
|
|
return qlonglong(SettingToInt(setting(), DEFAULT_SCRIPTCHECK_THREADS));
|
2019-04-29 15:29:00 -04:00
|
|
|
case Listen:
|
2019-04-29 15:29:00 -04:00
|
|
|
return SettingToBool(setting(), DEFAULT_LISTEN);
|
2019-04-29 15:29:00 -04:00
|
|
|
case Server:
|
2019-04-29 15:29:00 -04:00
|
|
|
return SettingToBool(setting(), false);
|
2019-04-29 15:29:00 -04:00
|
|
|
default:
|
|
|
|
return QVariant();
|
2011-05-31 22:24:53 +02:00
|
|
|
}
|
|
|
|
}
|
2012-09-23 12:55:05 +02:00
|
|
|
|
2019-04-29 15:29:00 -04:00
|
|
|
bool OptionsModel::setOption(OptionID option, const QVariant& value)
|
2011-05-31 22:24:53 +02:00
|
|
|
{
|
2019-04-29 15:29:00 -04:00
|
|
|
auto changed = [&] { return value.isValid() && value != getOption(option); };
|
|
|
|
auto update = [&](const util::SettingsValue& value) { return UpdateRwSetting(node(), option, value); };
|
|
|
|
|
2011-06-01 14:40:06 +02:00
|
|
|
bool successful = true; /* set to false on parse error */
|
2019-04-29 15:29:00 -04:00
|
|
|
QSettings settings;
|
|
|
|
|
|
|
|
switch (option) {
|
|
|
|
case StartAtStartup:
|
|
|
|
successful = GUIUtil::SetStartOnSystemStartup(value.toBool());
|
2012-09-23 12:55:05 +02:00
|
|
|
break;
|
2019-04-29 15:29:00 -04:00
|
|
|
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
|
2022-04-18 19:19:02 -04:00
|
|
|
if (changed()) {
|
|
|
|
update(value.toBool());
|
|
|
|
node().mapPort(value.toBool(), getOption(MapPortNatpmp).toBool());
|
|
|
|
}
|
2019-04-29 15:29:00 -04:00
|
|
|
break;
|
|
|
|
case MapPortNatpmp: // core option - can be changed on-the-fly
|
2022-04-18 19:19:02 -04:00
|
|
|
if (changed()) {
|
|
|
|
update(value.toBool());
|
|
|
|
node().mapPort(getOption(MapPortUPnP).toBool(), value.toBool());
|
|
|
|
}
|
2019-04-29 15:29:00 -04:00
|
|
|
break;
|
|
|
|
case MinimizeOnClose:
|
|
|
|
fMinimizeOnClose = value.toBool();
|
|
|
|
settings.setValue("fMinimizeOnClose", fMinimizeOnClose);
|
2012-09-23 12:55:05 +02:00
|
|
|
break;
|
2014-07-25 18:20:40 +02:00
|
|
|
|
2019-04-29 15:29:00 -04:00
|
|
|
// default proxy
|
|
|
|
case ProxyUse:
|
2019-04-29 15:29:00 -04:00
|
|
|
if (changed()) {
|
|
|
|
update(ProxyString(value.toBool(), m_proxy_ip, m_proxy_port));
|
2019-04-29 15:29:00 -04:00
|
|
|
setRestartRequired(true);
|
2014-07-25 18:20:40 +02:00
|
|
|
}
|
|
|
|
break;
|
2019-04-29 15:29:00 -04:00
|
|
|
case ProxyIP:
|
|
|
|
if (changed()) {
|
|
|
|
m_proxy_ip = value.toString();
|
|
|
|
if (getOption(ProxyUse).toBool()) {
|
|
|
|
update(ProxyString(true, m_proxy_ip, m_proxy_port));
|
|
|
|
setRestartRequired(true);
|
|
|
|
}
|
2019-04-29 15:29:00 -04:00
|
|
|
}
|
2019-04-29 15:29:00 -04:00
|
|
|
break;
|
|
|
|
case ProxyPort:
|
|
|
|
if (changed()) {
|
|
|
|
m_proxy_port = value.toString();
|
|
|
|
if (getOption(ProxyUse).toBool()) {
|
|
|
|
update(ProxyString(true, m_proxy_ip, m_proxy_port));
|
|
|
|
setRestartRequired(true);
|
|
|
|
}
|
2019-04-29 15:29:00 -04:00
|
|
|
}
|
2019-04-29 15:29:00 -04:00
|
|
|
break;
|
2019-04-29 15:29:00 -04:00
|
|
|
|
|
|
|
// separate Tor proxy
|
|
|
|
case ProxyUseTor:
|
2019-04-29 15:29:00 -04:00
|
|
|
if (changed()) {
|
|
|
|
update(ProxyString(value.toBool(), m_onion_ip, m_onion_port));
|
2019-04-29 15:29:00 -04:00
|
|
|
setRestartRequired(true);
|
2014-07-25 18:20:40 +02:00
|
|
|
}
|
|
|
|
break;
|
2019-04-29 15:29:00 -04:00
|
|
|
case ProxyIPTor:
|
|
|
|
if (changed()) {
|
|
|
|
m_onion_ip = value.toString();
|
|
|
|
if (getOption(ProxyUseTor).toBool()) {
|
|
|
|
update(ProxyString(true, m_onion_ip, m_onion_port));
|
|
|
|
setRestartRequired(true);
|
|
|
|
}
|
2019-04-29 15:29:00 -04:00
|
|
|
}
|
2019-04-29 15:29:00 -04:00
|
|
|
break;
|
|
|
|
case ProxyPortTor:
|
|
|
|
if (changed()) {
|
|
|
|
m_onion_port = value.toString();
|
|
|
|
if (getOption(ProxyUseTor).toBool()) {
|
|
|
|
update(ProxyString(true, m_onion_ip, m_onion_port));
|
|
|
|
setRestartRequired(true);
|
|
|
|
}
|
2019-04-29 15:29:00 -04:00
|
|
|
}
|
2019-04-29 15:29:00 -04:00
|
|
|
break;
|
2014-07-25 18:20:40 +02:00
|
|
|
|
2013-12-11 15:00:56 +01:00
|
|
|
#ifdef ENABLE_WALLET
|
2019-04-29 15:29:00 -04:00
|
|
|
case SpendZeroConfChange:
|
2019-04-29 15:29:00 -04:00
|
|
|
if (changed()) {
|
|
|
|
update(value.toBool());
|
2019-04-29 15:29:00 -04:00
|
|
|
setRestartRequired(true);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case ExternalSignerPath:
|
2019-04-29 15:29:00 -04:00
|
|
|
if (changed()) {
|
|
|
|
update(value.toString().toStdString());
|
2019-04-29 15:29:00 -04:00
|
|
|
setRestartRequired(true);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case SubFeeFromAmount:
|
|
|
|
m_sub_fee_from_amount = value.toBool();
|
|
|
|
settings.setValue("SubFeeFromAmount", m_sub_fee_from_amount);
|
|
|
|
break;
|
2013-12-11 15:00:56 +01:00
|
|
|
#endif
|
2019-04-29 15:29:00 -04:00
|
|
|
case DisplayUnit:
|
|
|
|
setDisplayUnit(value);
|
|
|
|
break;
|
|
|
|
case ThirdPartyTxUrls:
|
|
|
|
if (strThirdPartyTxUrls != value.toString()) {
|
|
|
|
strThirdPartyTxUrls = value.toString();
|
|
|
|
settings.setValue("strThirdPartyTxUrls", strThirdPartyTxUrls);
|
|
|
|
setRestartRequired(true);
|
2011-06-01 14:40:06 +02:00
|
|
|
}
|
2019-04-29 15:29:00 -04:00
|
|
|
break;
|
|
|
|
case Language:
|
2019-04-29 15:29:00 -04:00
|
|
|
if (changed()) {
|
|
|
|
update(value.toString().toStdString());
|
2019-04-29 15:29:00 -04:00
|
|
|
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:
|
2019-04-29 15:29:00 -04:00
|
|
|
if (changed()) {
|
|
|
|
update(PruneSetting(value.toBool(), m_prune_size_gb));
|
2019-04-29 15:29:00 -04:00
|
|
|
setRestartRequired(true);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case PruneSize:
|
2019-04-29 15:29:00 -04:00
|
|
|
if (changed()) {
|
|
|
|
m_prune_size_gb = ParsePruneSizeGB(value);
|
|
|
|
if (getOption(Prune).toBool()) {
|
|
|
|
update(PruneSetting(true, m_prune_size_gb));
|
|
|
|
setRestartRequired(true);
|
|
|
|
}
|
2019-04-29 15:29:00 -04:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case DatabaseCache:
|
2019-04-29 15:29:00 -04:00
|
|
|
if (changed()) {
|
|
|
|
update(static_cast<int64_t>(value.toLongLong()));
|
2019-04-29 15:29:00 -04:00
|
|
|
setRestartRequired(true);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case ThreadsScriptVerif:
|
2019-04-29 15:29:00 -04:00
|
|
|
if (changed()) {
|
|
|
|
update(static_cast<int64_t>(value.toLongLong()));
|
2019-04-29 15:29:00 -04:00
|
|
|
setRestartRequired(true);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case Listen:
|
|
|
|
case Server:
|
2019-04-29 15:29:00 -04:00
|
|
|
if (changed()) {
|
|
|
|
update(value.toBool());
|
2019-04-29 15:29:00 -04:00
|
|
|
setRestartRequired(true);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
2011-06-01 14:40:06 +02:00
|
|
|
}
|
2014-06-07 02:20:22 -04:00
|
|
|
|
2011-06-01 14:40:06 +02:00
|
|
|
return successful;
|
2011-05-31 22:24:53 +02:00
|
|
|
}
|
2011-06-01 09:34:12 +02:00
|
|
|
|
2021-04-20 22:15:39 +03:00
|
|
|
void OptionsModel::setDisplayUnit(const QVariant& new_unit)
|
2014-06-07 02:20:22 -04:00
|
|
|
{
|
2021-04-20 22:15:39 +03:00
|
|
|
if (new_unit.isNull() || new_unit.value<BitcoinUnit>() == m_display_bitcoin_unit) return;
|
|
|
|
m_display_bitcoin_unit = new_unit.value<BitcoinUnit>();
|
|
|
|
QSettings settings;
|
|
|
|
settings.setValue("DisplayBitcoinUnit", QVariant::fromValue(m_display_bitcoin_unit));
|
|
|
|
Q_EMIT displayUnitChanged(m_display_bitcoin_unit);
|
2014-06-07 02:20:22 -04:00
|
|
|
}
|
|
|
|
|
2013-12-03 09:10:10 +01:00
|
|
|
void OptionsModel::setRestartRequired(bool fRequired)
|
|
|
|
{
|
|
|
|
QSettings settings;
|
|
|
|
return settings.setValue("fRestartRequired", fRequired);
|
|
|
|
}
|
|
|
|
|
2017-03-09 13:34:54 +01:00
|
|
|
bool OptionsModel::isRestartRequired() const
|
2013-12-03 09:10:10 +01:00
|
|
|
{
|
|
|
|
QSettings settings;
|
|
|
|
return settings.value("fRestartRequired", false).toBool();
|
|
|
|
}
|
2016-07-26 14:01:36 +02:00
|
|
|
|
|
|
|
void OptionsModel::checkAndMigrate()
|
|
|
|
{
|
|
|
|
// Migration of default values
|
|
|
|
// Check if the QSettings container was already loaded with this client version
|
|
|
|
QSettings settings;
|
|
|
|
static const char strSettingsVersionKey[] = "nSettingsVersion";
|
|
|
|
int settingsVersion = settings.contains(strSettingsVersionKey) ? settings.value(strSettingsVersionKey).toInt() : 0;
|
|
|
|
if (settingsVersion < CLIENT_VERSION)
|
|
|
|
{
|
|
|
|
// -dbcache was bumped from 100 to 300 in 0.13
|
|
|
|
// see https://github.com/bitcoin/bitcoin/pull/8273
|
|
|
|
// force people to upgrade to the new value if they are using 100MB
|
|
|
|
if (settingsVersion < 130000 && settings.contains("nDatabaseCache") && settings.value("nDatabaseCache").toLongLong() == 100)
|
|
|
|
settings.setValue("nDatabaseCache", (qint64)nDefaultDbCache);
|
|
|
|
|
|
|
|
settings.setValue(strSettingsVersionKey, CLIENT_VERSION);
|
|
|
|
}
|
2018-03-08 20:58:57 +01:00
|
|
|
|
|
|
|
// Overwrite the 'addrProxy' setting in case it has been set to an illegal
|
|
|
|
// default value (see issue #12623; PR #12650).
|
|
|
|
if (settings.contains("addrProxy") && settings.value("addrProxy").toString().endsWith("%2")) {
|
|
|
|
settings.setValue("addrProxy", GetDefaultProxyAddress());
|
|
|
|
}
|
|
|
|
|
|
|
|
// Overwrite the 'addrSeparateProxyTor' setting in case it has been set to an illegal
|
|
|
|
// default value (see issue #12623; PR #12650).
|
|
|
|
if (settings.contains("addrSeparateProxyTor") && settings.value("addrSeparateProxyTor").toString().endsWith("%2")) {
|
|
|
|
settings.setValue("addrSeparateProxyTor", GetDefaultProxyAddress());
|
|
|
|
}
|
2019-04-29 15:29:00 -04:00
|
|
|
|
|
|
|
// 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()) {
|
2019-04-29 15:29:00 -04:00
|
|
|
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);
|
|
|
|
}
|
2019-04-29 15:29:00 -04:00
|
|
|
}
|
|
|
|
settings.remove(qt_name);
|
|
|
|
};
|
|
|
|
|
|
|
|
migrate_setting(DatabaseCache, "nDatabaseCache");
|
2019-04-29 15:29:00 -04:00
|
|
|
migrate_setting(ThreadsScriptVerif, "nThreadsScriptVerif");
|
2019-04-29 15:29:00 -04:00
|
|
|
#ifdef ENABLE_WALLET
|
|
|
|
migrate_setting(SpendZeroConfChange, "bSpendZeroConfChange");
|
|
|
|
migrate_setting(ExternalSignerPath, "external_signer_path");
|
|
|
|
#endif
|
2022-04-18 19:19:02 -04:00
|
|
|
migrate_setting(MapPortUPnP, "fUseUPnP");
|
|
|
|
migrate_setting(MapPortNatpmp, "fUseNatpmp");
|
2019-04-29 15:29:00 -04:00
|
|
|
migrate_setting(Listen, "fListen");
|
|
|
|
migrate_setting(Server, "server");
|
2019-04-29 15:29:00 -04:00
|
|
|
migrate_setting(PruneSize, "nPruneSize");
|
|
|
|
migrate_setting(Prune, "bPrune");
|
2019-04-29 15:29:00 -04:00
|
|
|
migrate_setting(ProxyIP, "addrProxy");
|
|
|
|
migrate_setting(ProxyUse, "fUseProxy");
|
|
|
|
migrate_setting(ProxyIPTor, "addrSeparateProxyTor");
|
|
|
|
migrate_setting(ProxyUseTor, "fUseSeparateProxyTor");
|
2019-04-29 15:29:00 -04:00
|
|
|
migrate_setting(Language, "language");
|
2019-04-29 15:29:00 -04:00
|
|
|
|
|
|
|
// 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();
|
2016-11-29 18:45:24 -08:00
|
|
|
}
|