mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-01-15 06:12:37 -03:00
b89f2d0599
4f7127d1e3
gui: Make Intro consistent with prune checkbox (Hennadii Stepanov)4824a7d36c
gui: Add Intro::UpdateFreeSpaceLabel() (Hennadii Stepanov)daa3f3fa90
refactor: Add Intro::UpdatePruneLabels() (Hennadii Stepanov)e4caa82a03
refactor: Replace static variable with data member (Hennadii Stepanov)2bede28cd9
util: Add PruneGBtoMiB() function (Hennadii Stepanov)e35e4b2ba0
util: Add PruneMiBtoGB() function (Hennadii Stepanov) Pull request description: On master (a6f6333ba2
) and on 0.19.0.1 the intro dialog with prune enabled (checkbox "Discard blocks..." is checked) provides a user with wrong info about the required disk space: ![DeepinScreenshot_bitcoin-qt_20191208112228](https://user-images.githubusercontent.com/32963518/70387510-8daab400-19ae-11ea-9338-29add9c31118.png) Also the paragraph "If you have chosen to limit..." is missed. --- With this PR when prune checkbox is toggled, the related text labels and the amount of required space shown are updated (previously they were only updated when the data directory was updated): ![Screenshot from 2019-12-08 11-34-53](https://user-images.githubusercontent.com/32963518/70387542-eed28780-19ae-11ea-9565-49d8a64b2f33.png) --- This PR is an alternative to #17035. **ryanofsky**'s [suggestion](https://github.com/bitcoin/bitcoin/pull/17035#discussion_r337594268) also has been implemented. ACKs for top commit: emilengler: ACK4f7127d1e3
Sjors: tACK4f7127d1e3
ryanofsky: Code review ACK4f7127d1e3
. It seems like there are a few visible changes here: jonasschnelli: utACK4f7127d1e3
Tree-SHA512: fa0bbdcfafde97d7906cda066cbd4608b936a71cae1b4cda3ee3aa2eed3a9795f279f14c6b1b4997278e094db891c7d3bb695368ba0882347aa42165a86e5172
536 lines
18 KiB
C++
536 lines
18 KiB
C++
// Copyright (c) 2011-2020 The Bitcoin Core developers
|
|
// Distributed under the MIT software license, see the accompanying
|
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
|
|
#if defined(HAVE_CONFIG_H)
|
|
#include <config/bitcoin-config.h>
|
|
#endif
|
|
|
|
#include <qt/optionsmodel.h>
|
|
|
|
#include <qt/bitcoinunits.h>
|
|
#include <qt/guiconstants.h>
|
|
#include <qt/guiutil.h>
|
|
|
|
#include <interfaces/node.h>
|
|
#include <validation.h> // For DEFAULT_SCRIPTCHECK_THREADS
|
|
#include <net.h>
|
|
#include <netbase.h>
|
|
#include <txdb.h> // for -dbcache defaults
|
|
|
|
#include <QDebug>
|
|
#include <QSettings>
|
|
#include <QStringList>
|
|
|
|
const char *DEFAULT_GUI_PROXY_HOST = "127.0.0.1";
|
|
|
|
static const QString GetDefaultProxyAddress();
|
|
|
|
OptionsModel::OptionsModel(interfaces::Node& node, QObject *parent, bool resetSettings) :
|
|
QAbstractListModel(parent), m_node(node)
|
|
{
|
|
Init(resetSettings);
|
|
}
|
|
|
|
void OptionsModel::addOverriddenOption(const std::string &option)
|
|
{
|
|
strOverriddenByCommandLine += QString::fromStdString(option) + "=" + QString::fromStdString(gArgs.GetArg(option, "")) + " ";
|
|
}
|
|
|
|
// Writes all missing QSettings with their default values
|
|
void OptionsModel::Init(bool resetSettings)
|
|
{
|
|
if (resetSettings)
|
|
Reset();
|
|
|
|
checkAndMigrate();
|
|
|
|
QSettings settings;
|
|
|
|
// Ensure restart flag is unset on client startup
|
|
setRestartRequired(false);
|
|
|
|
// These are Qt-only settings:
|
|
|
|
// Window
|
|
if (!settings.contains("fHideTrayIcon"))
|
|
settings.setValue("fHideTrayIcon", false);
|
|
fHideTrayIcon = settings.value("fHideTrayIcon").toBool();
|
|
Q_EMIT hideTrayIconChanged(fHideTrayIcon);
|
|
|
|
if (!settings.contains("fMinimizeToTray"))
|
|
settings.setValue("fMinimizeToTray", false);
|
|
fMinimizeToTray = settings.value("fMinimizeToTray").toBool() && !fHideTrayIcon;
|
|
|
|
if (!settings.contains("fMinimizeOnClose"))
|
|
settings.setValue("fMinimizeOnClose", false);
|
|
fMinimizeOnClose = settings.value("fMinimizeOnClose").toBool();
|
|
|
|
// Display
|
|
if (!settings.contains("nDisplayUnit"))
|
|
settings.setValue("nDisplayUnit", BitcoinUnits::BTC);
|
|
nDisplayUnit = settings.value("nDisplayUnit").toInt();
|
|
|
|
if (!settings.contains("strThirdPartyTxUrls"))
|
|
settings.setValue("strThirdPartyTxUrls", "");
|
|
strThirdPartyTxUrls = settings.value("strThirdPartyTxUrls", "").toString();
|
|
|
|
if (!settings.contains("fCoinControlFeatures"))
|
|
settings.setValue("fCoinControlFeatures", false);
|
|
fCoinControlFeatures = settings.value("fCoinControlFeatures", false).toBool();
|
|
|
|
// These are shared with the core or have a command-line parameter
|
|
// and we want command-line parameters to overwrite the GUI settings.
|
|
//
|
|
// 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 (!m_node.softSetArg("-dbcache", settings.value("nDatabaseCache").toString().toStdString()))
|
|
addOverriddenOption("-dbcache");
|
|
|
|
if (!settings.contains("nThreadsScriptVerif"))
|
|
settings.setValue("nThreadsScriptVerif", DEFAULT_SCRIPTCHECK_THREADS);
|
|
if (!m_node.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 (!m_node.softSetBoolArg("-spendzeroconfchange", settings.value("bSpendZeroConfChange").toBool()))
|
|
addOverriddenOption("-spendzeroconfchange");
|
|
#endif
|
|
|
|
// Network
|
|
if (!settings.contains("fUseUPnP"))
|
|
settings.setValue("fUseUPnP", DEFAULT_UPNP);
|
|
if (!m_node.softSetBoolArg("-upnp", settings.value("fUseUPnP").toBool()))
|
|
addOverriddenOption("-upnp");
|
|
|
|
if (!settings.contains("fListen"))
|
|
settings.setValue("fListen", DEFAULT_LISTEN);
|
|
if (!m_node.softSetBoolArg("-listen", settings.value("fListen").toBool()))
|
|
addOverriddenOption("-listen");
|
|
|
|
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() && !m_node.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() && !m_node.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 (!m_node.softSetArg("-lang", settings.value("language").toString().toStdString()))
|
|
addOverriddenOption("-lang");
|
|
|
|
language = settings.value("language").toString();
|
|
}
|
|
|
|
/** 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)
|
|
{
|
|
qInfo() << "Backing up GUI settings to" << GUIUtil::boostPathToQString(filename);
|
|
QSettings dst(GUIUtil::boostPathToQString(filename), QSettings::IniFormat);
|
|
dst.clear();
|
|
CopySettings(dst, src);
|
|
}
|
|
|
|
void OptionsModel::Reset()
|
|
{
|
|
QSettings settings;
|
|
|
|
// Backup old settings to chain-specific datadir for troubleshooting
|
|
BackupSettings(GetDataDir(true) / "guisettings.ini.bak", settings);
|
|
|
|
// Save the strDataDir setting
|
|
QString dataDir = GUIUtil::getDefaultDataDirectory();
|
|
dataDir = settings.value("strDataDir", dataDir).toString();
|
|
|
|
// Remove all entries from our QSettings object
|
|
settings.clear();
|
|
|
|
// Set strDataDir
|
|
settings.setValue("strDataDir", dataDir);
|
|
|
|
// Set that this was reset
|
|
settings.setValue("fReset", true);
|
|
|
|
// default setting for OptionsModel::StartAtStartup - disabled
|
|
if (GUIUtil::GetStartOnSystemStartup())
|
|
GUIUtil::SetStartOnSystemStartup(false);
|
|
}
|
|
|
|
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 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)) {
|
|
return default_val;
|
|
}
|
|
// contains IP at index 0 and port at index 1
|
|
QStringList ip_port = settings.value(name).toString().split(":", QString::SkipEmptyParts);
|
|
if (ip_port.size() == 2) {
|
|
return {true, ip_port.at(0), ip_port.at(1)};
|
|
} else { // Invalid: return default
|
|
return default_val;
|
|
}
|
|
}
|
|
|
|
static void SetProxySetting(QSettings &settings, const QString &name, const ProxySetting &ip_port)
|
|
{
|
|
settings.setValue(name, ip_port.ip + ":" + ip_port.port);
|
|
}
|
|
|
|
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)
|
|
{
|
|
QSettings settings;
|
|
settings.setValue("bPrune", prune);
|
|
const int64_t prune_target_mib = PruneGBtoMiB(settings.value("nPruneSize").toInt());
|
|
std::string prune_val = prune ? std::to_string(prune_target_mib) : "0";
|
|
if (force) {
|
|
m_node.forceSetArg("-prune", prune_val);
|
|
return;
|
|
}
|
|
if (!m_node.softSetArg("-prune", prune_val)) {
|
|
addOverriddenOption("-prune");
|
|
}
|
|
}
|
|
|
|
void OptionsModel::SetPruneTargetGB(int prune_target_gb, bool force)
|
|
{
|
|
const bool prune = prune_target_gb > 0;
|
|
if (prune) {
|
|
QSettings settings;
|
|
settings.setValue("nPruneSize", prune_target_gb);
|
|
}
|
|
SetPruneEnabled(prune, force);
|
|
}
|
|
|
|
// read QSettings values and return them
|
|
QVariant OptionsModel::data(const QModelIndex & index, int role) const
|
|
{
|
|
if(role == Qt::EditRole)
|
|
{
|
|
QSettings settings;
|
|
switch(index.row())
|
|
{
|
|
case StartAtStartup:
|
|
return GUIUtil::GetStartOnSystemStartup();
|
|
case HideTrayIcon:
|
|
return fHideTrayIcon;
|
|
case MinimizeToTray:
|
|
return fMinimizeToTray;
|
|
case MapPortUPnP:
|
|
#ifdef USE_UPNP
|
|
return settings.value("fUseUPnP");
|
|
#else
|
|
return false;
|
|
#endif
|
|
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;
|
|
|
|
#ifdef ENABLE_WALLET
|
|
case SpendZeroConfChange:
|
|
return settings.value("bSpendZeroConfChange");
|
|
#endif
|
|
case DisplayUnit:
|
|
return nDisplayUnit;
|
|
case ThirdPartyTxUrls:
|
|
return strThirdPartyTxUrls;
|
|
case Language:
|
|
return settings.value("language");
|
|
case CoinControlFeatures:
|
|
return fCoinControlFeatures;
|
|
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");
|
|
default:
|
|
return QVariant();
|
|
}
|
|
}
|
|
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)
|
|
{
|
|
QSettings settings;
|
|
switch(index.row())
|
|
{
|
|
case StartAtStartup:
|
|
successful = GUIUtil::SetStartOnSystemStartup(value.toBool());
|
|
break;
|
|
case HideTrayIcon:
|
|
fHideTrayIcon = value.toBool();
|
|
settings.setValue("fHideTrayIcon", fHideTrayIcon);
|
|
Q_EMIT hideTrayIconChanged(fHideTrayIcon);
|
|
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());
|
|
m_node.mapPort(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);
|
|
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);
|
|
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);
|
|
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);
|
|
setRestartRequired(true);
|
|
}
|
|
}
|
|
break;
|
|
|
|
#ifdef ENABLE_WALLET
|
|
case SpendZeroConfChange:
|
|
if (settings.value("bSpendZeroConfChange") != value) {
|
|
settings.setValue("bSpendZeroConfChange", value);
|
|
setRestartRequired(true);
|
|
}
|
|
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 CoinControlFeatures:
|
|
fCoinControlFeatures = value.toBool();
|
|
settings.setValue("fCoinControlFeatures", fCoinControlFeatures);
|
|
Q_EMIT coinControlFeaturesChanged(fCoinControlFeatures);
|
|
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;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
Q_EMIT dataChanged(index, index);
|
|
|
|
return successful;
|
|
}
|
|
|
|
/** Updates current unit in memory, settings and emits displayUnitChanged(newUnit) signal */
|
|
void OptionsModel::setDisplayUnit(const QVariant &value)
|
|
{
|
|
if (!value.isNull())
|
|
{
|
|
QSettings settings;
|
|
nDisplayUnit = value.toInt();
|
|
settings.setValue("nDisplayUnit", nDisplayUnit);
|
|
Q_EMIT displayUnitChanged(nDisplayUnit);
|
|
}
|
|
}
|
|
|
|
void OptionsModel::setRestartRequired(bool fRequired)
|
|
{
|
|
QSettings settings;
|
|
return settings.setValue("fRestartRequired", fRequired);
|
|
}
|
|
|
|
bool OptionsModel::isRestartRequired() const
|
|
{
|
|
QSettings settings;
|
|
return settings.value("fRestartRequired", false).toBool();
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
// 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());
|
|
}
|
|
}
|