diff --git a/doc/release-notes.md b/doc/release-notes.md
index 3aa6cbc94..20b97afd0 100644
--- a/doc/release-notes.md
+++ b/doc/release-notes.md
@@ -1,12 +1,18 @@
Bitcoin ABC version 0.20.5 is now available from:
This release includes the following features and fixes:
+ - It is now possible to unload wallets dynamically at runtime. This feature is
+ currently only available through the RPC interface.
+
+GUI changes
+-----------
- Wallets loaded dynamically through the RPC interface may now be displayed in
the bitcoin-qt GUI.
- The default wallet will now be labeled `[default wallet]` in the bitcoin-qt
GUI if no name is provided by the `-wallet` option on start up.
- - It is now possible to unload wallets dynamically at runtime. This feature is
- currently only available through the RPC interface.
- Wallets dynamically unloaded will now be reflected in the gui.
+ - Block storage can be limited under Preferences, in the Main tab. Undoing
+ this setting requires downloading the full blockchain again. This mode is
+ incompatible with -txindex and -rescan.
diff --git a/src/qt/forms/optionsdialog.ui b/src/qt/forms/optionsdialog.ui
index a3721991e..8f34e6bc8 100644
--- a/src/qt/forms/optionsdialog.ui
+++ b/src/qt/forms/optionsdialog.ui
@@ -1,837 +1,900 @@
OptionsDialog
0
0
560
440
Options
true
-
0
&Main
-
Automatically start %1 after logging in to the system.
&Start %1 on system login
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 5
+
+
+
+
+ -
+
+
-
+
+
+ Disables some advanced features but all blocks will still be fully validated. Reverting this setting requires re-downloading the entire blockchain. Actual disk usage may be somewhat higher.
+
+
+ Prune &block storage to
+
+
+
+ -
+
+
+ -
+
+
+ GB
+
+
+ Qt::PlainText
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+
+
+ -
+
+
+ Reverting this setting requires re-downloading the entire blockchain.
+
+
+ Qt::PlainText
+
+
+
-
-
Size of &database cache
Qt::PlainText
databaseCache
-
-
MB
Qt::PlainText
-
Qt::Horizontal
40
20
-
-
+
-
Number of script &verification threads
Qt::PlainText
threadsScriptVerif
-
(0 = auto, <0 = leave that many cores free)
-
-
+
Qt::Horizontal
40
20
-
Qt::Vertical
20
40
W&allet
-
Expert
-
Whether to show coin control features or not.
Enable coin &control features
-
If you disable the spending of unconfirmed change, the change from a transaction cannot be used until that transaction has at least one confirmation. This also affects how your balance is computed.
&Spend unconfirmed change
-
Qt::Vertical
20
40
&Network
-
Automatically open the Bitcoin client port on the router. This only works when your router supports UPnP and it is enabled.
Map port using &UPnP
-
Accept connections from outside.
Allow incomin&g connections
-
Connect to the Bitcoin network through a SOCKS5 proxy.
&Connect through SOCKS5 proxy (default proxy):
-
-
Proxy &IP:
Qt::PlainText
proxyIp
-
140
0
140
16777215
IP address of the proxy (e.g. IPv4: 127.0.0.1 / IPv6: ::1)
-
&Port:
Qt::PlainText
proxyPort
-
55
0
55
16777215
Port of the proxy (e.g. 9050)
-
Qt::Horizontal
40
20
-
-
Used for reaching peers via:
Qt::PlainText
-
false
Shows if the supplied default SOCKS5 proxy is used to reach peers via this network type.
-
IPv4
Qt::PlainText
-
false
Shows if the supplied default SOCKS5 proxy is used to reach peers via this network type.
-
IPv6
Qt::PlainText
-
false
Shows if the supplied default SOCKS5 proxy is used to reach peers via this network type.
-
Tor
Qt::PlainText
-
Qt::Horizontal
40
20
-
Connect to the Bitcoin network through a separate SOCKS5 proxy for Tor hidden services.
Use separate SOCKS&5 proxy to reach peers via Tor hidden services:
-
-
Proxy &IP:
Qt::PlainText
proxyIpTor
-
140
0
140
16777215
IP address of the proxy (e.g. IPv4: 127.0.0.1 / IPv6: ::1)
-
&Port:
Qt::PlainText
proxyPortTor
-
55
0
55
16777215
Port of the proxy (e.g. 9050)
-
Qt::Horizontal
40
20
-
Qt::Vertical
20
40
&Window
-
Hide the icon from the system tray.
&Hide tray icon
-
Show only a tray icon after minimizing the window.
&Minimize to the tray instead of the taskbar
-
Minimize instead of exit the application when the window is closed. When this option is enabled, the application will be closed only after selecting Exit in the menu.
M&inimize on close
-
Qt::Vertical
20
40
&Display
-
-
User Interface &language:
Qt::PlainText
lang
-
The user interface language can be set here. This setting will take effect after restarting %1.
-
-
&Unit to show amounts in:
Qt::PlainText
unit
-
Choose the default subdivision unit to show in the interface and when sending coins.
-
-
Third party URLs (e.g. a block explorer) that appear in the transactions tab as context menu items. %s in the URL is replaced by transaction hash. Multiple URLs are separated by vertical bar |.
&Third party transaction URLs
thirdPartyTxUrls
-
Third party URLs (e.g. a block explorer) that appear in the transactions tab as context menu items. %s in the URL is replaced by transaction hash. Multiple URLs are separated by vertical bar |.
-
Qt::Vertical
20
40
-
-
-
Active command-line options that override above options:
Qt::PlainText
-
Qt::Horizontal
40
20
-
Qt::PlainText
true
-
-
-
Open the %1 configuration file from the working directory.
Open Configuration File
false
-
Reset all client options to default.
&Reset Options
false
-
Qt::Horizontal
40
48
-
200
0
75
true
Qt::PlainText
true
-
Qt::Horizontal
40
48
-
-
Qt::Vertical
20
40
-
-
&OK
false
true
-
&Cancel
false
QValidatedLineEdit
QLineEdit
QValueComboBox
QComboBox
diff --git a/src/qt/optionsdialog.cpp b/src/qt/optionsdialog.cpp
index 86d310233..879684f25 100644
--- a/src/qt/optionsdialog.cpp
+++ b/src/qt/optionsdialog.cpp
@@ -1,351 +1,375 @@
// Copyright (c) 2011-2016 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
#endif
#include
#include
#include
#include
#include
#include
#include
#include // for -dbcache defaults
#include // for DEFAULT_SCRIPTCHECK_THREADS and MAX_SCRIPTCHECK_THREADS
#include
#include
#include
#include
#include
#include
OptionsDialog::OptionsDialog(QWidget *parent, bool enableWallet)
: QDialog(parent), ui(new Ui::OptionsDialog), model(0), mapper(0) {
ui->setupUi(this);
/* Main elements init */
ui->databaseCache->setMinimum(nMinDbCache);
ui->databaseCache->setMaximum(nMaxDbCache);
+ static const uint64_t GiB = 1024 * 1024 * 1024;
+ static const uint64_t nMinDiskSpace =
+ MIN_DISK_SPACE_FOR_BLOCK_FILES / GiB +
+ (MIN_DISK_SPACE_FOR_BLOCK_FILES % GiB)
+ ? 1
+ : 0;
+ ui->pruneSize->setMinimum(nMinDiskSpace);
ui->threadsScriptVerif->setMinimum(-GetNumCores());
ui->threadsScriptVerif->setMaximum(MAX_SCRIPTCHECK_THREADS);
+ ui->pruneWarning->setVisible(false);
+ ui->pruneWarning->setStyleSheet("QLabel { color: red; }");
+
+ ui->pruneSize->setEnabled(false);
+ connect(ui->prune, SIGNAL(toggled(bool)), ui->pruneSize,
+ SLOT(setEnabled(bool)));
/* Network elements init */
#ifndef USE_UPNP
ui->mapPortUpnp->setEnabled(false);
#endif
ui->proxyIp->setEnabled(false);
ui->proxyPort->setEnabled(false);
ui->proxyPort->setValidator(new QIntValidator(1, 65535, this));
ui->proxyIpTor->setEnabled(false);
ui->proxyPortTor->setEnabled(false);
ui->proxyPortTor->setValidator(new QIntValidator(1, 65535, this));
connect(ui->connectSocks, SIGNAL(toggled(bool)), ui->proxyIp,
SLOT(setEnabled(bool)));
connect(ui->connectSocks, SIGNAL(toggled(bool)), ui->proxyPort,
SLOT(setEnabled(bool)));
connect(ui->connectSocks, SIGNAL(toggled(bool)), this,
SLOT(updateProxyValidationState()));
connect(ui->connectSocksTor, SIGNAL(toggled(bool)), ui->proxyIpTor,
SLOT(setEnabled(bool)));
connect(ui->connectSocksTor, SIGNAL(toggled(bool)), ui->proxyPortTor,
SLOT(setEnabled(bool)));
connect(ui->connectSocksTor, SIGNAL(toggled(bool)), this,
SLOT(updateProxyValidationState()));
/* Window elements init */
#ifdef Q_OS_MAC
/* remove Window tab on Mac */
ui->tabWidget->removeTab(ui->tabWidget->indexOf(ui->tabWindow));
#endif
/* remove Wallet tab in case of -disablewallet */
if (!enableWallet) {
ui->tabWidget->removeTab(ui->tabWidget->indexOf(ui->tabWallet));
}
/* Display elements init */
QDir translations(":translations");
ui->bitcoinAtStartup->setToolTip(
ui->bitcoinAtStartup->toolTip().arg(tr(PACKAGE_NAME)));
ui->bitcoinAtStartup->setText(
ui->bitcoinAtStartup->text().arg(tr(PACKAGE_NAME)));
ui->openBitcoinConfButton->setToolTip(
ui->openBitcoinConfButton->toolTip().arg(tr(PACKAGE_NAME)));
ui->lang->setToolTip(ui->lang->toolTip().arg(tr(PACKAGE_NAME)));
ui->lang->addItem(QString("(") + tr("default") + QString(")"),
QVariant(""));
for (const QString &langStr : translations.entryList()) {
QLocale locale(langStr);
/** check if the locale name consists of 2 parts (language_country) */
if (langStr.contains("_")) {
/** display language strings as "native language - native country
* (locale name)", e.g. "Deutsch - Deutschland (de)" */
ui->lang->addItem(locale.nativeLanguageName() + QString(" - ") +
locale.nativeCountryName() + QString(" (") +
langStr + QString(")"),
QVariant(langStr));
} else {
/** display language strings as "native language (locale name)",
* e.g. "Deutsch (de)" */
ui->lang->addItem(locale.nativeLanguageName() + QString(" (") +
langStr + QString(")"),
QVariant(langStr));
}
}
ui->thirdPartyTxUrls->setPlaceholderText("https://example.com/tx/%s");
ui->unit->setModel(new BitcoinUnits(this));
/* Widget-to-option mapper */
mapper = new QDataWidgetMapper(this);
mapper->setSubmitPolicy(QDataWidgetMapper::ManualSubmit);
mapper->setOrientation(Qt::Vertical);
/* setup/change UI elements when proxy IPs are invalid/valid */
ui->proxyIp->setCheckValidator(new ProxyAddressValidator(parent));
ui->proxyIpTor->setCheckValidator(new ProxyAddressValidator(parent));
connect(ui->proxyIp, SIGNAL(validationDidChange(QValidatedLineEdit *)),
this, SLOT(updateProxyValidationState()));
connect(ui->proxyIpTor, SIGNAL(validationDidChange(QValidatedLineEdit *)),
this, SLOT(updateProxyValidationState()));
connect(ui->proxyPort, SIGNAL(textChanged(const QString &)), this,
SLOT(updateProxyValidationState()));
connect(ui->proxyPortTor, SIGNAL(textChanged(const QString &)), this,
SLOT(updateProxyValidationState()));
}
OptionsDialog::~OptionsDialog() {
delete ui;
}
void OptionsDialog::setModel(OptionsModel *_model) {
this->model = _model;
if (_model) {
/* check if client restart is needed and show persistent message */
if (_model->isRestartRequired()) showRestartWarning(true);
QString strLabel = _model->getOverriddenByCommandLine();
if (strLabel.isEmpty()) strLabel = tr("none");
ui->overriddenByCommandLineLabel->setText(strLabel);
mapper->setModel(_model);
setMapper();
mapper->toFirst();
updateDefaultProxyNets();
}
/* warn when one of the following settings changes by user action (placed
* here so init via mapper doesn't trigger them) */
/* Main */
+ connect(ui->prune, SIGNAL(clicked(bool)), this, SLOT(showRestartWarning()));
+ connect(ui->prune, SIGNAL(clicked(bool)), this,
+ SLOT(togglePruneWarning(bool)));
+ connect(ui->pruneSize, SIGNAL(valueChanged(int)), this,
+ SLOT(showRestartWarning()));
connect(ui->databaseCache, SIGNAL(valueChanged(int)), this,
SLOT(showRestartWarning()));
connect(ui->threadsScriptVerif, SIGNAL(valueChanged(int)), this,
SLOT(showRestartWarning()));
/* Wallet */
connect(ui->spendZeroConfChange, SIGNAL(clicked(bool)), this,
SLOT(showRestartWarning()));
/* Network */
connect(ui->allowIncoming, SIGNAL(clicked(bool)), this,
SLOT(showRestartWarning()));
connect(ui->connectSocks, SIGNAL(clicked(bool)), this,
SLOT(showRestartWarning()));
connect(ui->connectSocksTor, SIGNAL(clicked(bool)), this,
SLOT(showRestartWarning()));
/* Display */
connect(ui->lang, SIGNAL(valueChanged()), this, SLOT(showRestartWarning()));
connect(ui->thirdPartyTxUrls, SIGNAL(textChanged(const QString &)), this,
SLOT(showRestartWarning()));
}
void OptionsDialog::setMapper() {
/* Main */
mapper->addMapping(ui->bitcoinAtStartup, OptionsModel::StartAtStartup);
mapper->addMapping(ui->threadsScriptVerif,
OptionsModel::ThreadsScriptVerif);
mapper->addMapping(ui->databaseCache, OptionsModel::DatabaseCache);
+ mapper->addMapping(ui->prune, OptionsModel::Prune);
+ mapper->addMapping(ui->pruneSize, OptionsModel::PruneSize);
/* Wallet */
mapper->addMapping(ui->spendZeroConfChange,
OptionsModel::SpendZeroConfChange);
mapper->addMapping(ui->coinControlFeatures,
OptionsModel::CoinControlFeatures);
/* Network */
mapper->addMapping(ui->mapPortUpnp, OptionsModel::MapPortUPnP);
mapper->addMapping(ui->allowIncoming, OptionsModel::Listen);
mapper->addMapping(ui->connectSocks, OptionsModel::ProxyUse);
mapper->addMapping(ui->proxyIp, OptionsModel::ProxyIP);
mapper->addMapping(ui->proxyPort, OptionsModel::ProxyPort);
mapper->addMapping(ui->connectSocksTor, OptionsModel::ProxyUseTor);
mapper->addMapping(ui->proxyIpTor, OptionsModel::ProxyIPTor);
mapper->addMapping(ui->proxyPortTor, OptionsModel::ProxyPortTor);
/* Window */
#ifndef Q_OS_MAC
mapper->addMapping(ui->hideTrayIcon, OptionsModel::HideTrayIcon);
mapper->addMapping(ui->minimizeToTray, OptionsModel::MinimizeToTray);
mapper->addMapping(ui->minimizeOnClose, OptionsModel::MinimizeOnClose);
#endif
/* Display */
mapper->addMapping(ui->lang, OptionsModel::Language);
mapper->addMapping(ui->unit, OptionsModel::DisplayUnit);
mapper->addMapping(ui->thirdPartyTxUrls, OptionsModel::ThirdPartyTxUrls);
}
void OptionsDialog::setOkButtonState(bool fState) {
ui->okButton->setEnabled(fState);
}
void OptionsDialog::on_resetButton_clicked() {
if (model) {
// confirmation dialog
QMessageBox::StandardButton btnRetVal = QMessageBox::question(
this, tr("Confirm options reset"),
tr("Client restart required to activate changes.") + "
" +
tr("Client will be shut down. Do you want to proceed?"),
QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Cancel);
if (btnRetVal == QMessageBox::Cancel) return;
/* reset all options and close GUI */
model->Reset();
QApplication::quit();
}
}
void OptionsDialog::on_openBitcoinConfButton_clicked() {
/* explain the purpose of the config file */
QMessageBox::information(
this, tr("Configuration options"),
tr("The configuration file is used to specify advanced user options "
"which override GUI settings. Additionally, any command-line "
"options will override this configuration file."));
/* show an error if there was some problem opening the file */
if (!GUIUtil::openBitcoinConf()) {
QMessageBox::critical(
this, tr("Error"),
tr("The configuration file could not be opened."));
}
}
void OptionsDialog::on_okButton_clicked() {
mapper->submit();
accept();
updateDefaultProxyNets();
}
void OptionsDialog::on_cancelButton_clicked() {
reject();
}
void OptionsDialog::on_hideTrayIcon_stateChanged(int fState) {
if (fState) {
ui->minimizeToTray->setChecked(false);
ui->minimizeToTray->setEnabled(false);
} else {
ui->minimizeToTray->setEnabled(true);
}
}
+void OptionsDialog::togglePruneWarning(bool enabled) {
+ ui->pruneWarning->setVisible(!ui->pruneWarning->isVisible());
+}
+
void OptionsDialog::showRestartWarning(bool fPersistent) {
ui->statusLabel->setStyleSheet("QLabel { color: red; }");
if (fPersistent) {
ui->statusLabel->setText(
tr("Client restart required to activate changes."));
} else {
ui->statusLabel->setText(
tr("This change would require a client restart."));
// clear non-persistent status label after 10 seconds
// TODO: should perhaps be a class attribute, if we extend the use of
// statusLabel
QTimer::singleShot(10000, this, SLOT(clearStatusLabel()));
}
}
void OptionsDialog::clearStatusLabel() {
ui->statusLabel->clear();
if (model && model->isRestartRequired()) {
showRestartWarning(true);
}
}
void OptionsDialog::updateProxyValidationState() {
QValidatedLineEdit *pUiProxyIp = ui->proxyIp;
QValidatedLineEdit *otherProxyWidget =
(pUiProxyIp == ui->proxyIpTor) ? ui->proxyIp : ui->proxyIpTor;
if (pUiProxyIp->isValid() &&
(!ui->proxyPort->isEnabled() || ui->proxyPort->text().toInt() > 0) &&
(!ui->proxyPortTor->isEnabled() ||
ui->proxyPortTor->text().toInt() > 0)) {
// Only enable ok button if both proxys are valid
setOkButtonState(otherProxyWidget->isValid());
clearStatusLabel();
} else {
setOkButtonState(false);
ui->statusLabel->setStyleSheet("QLabel { color: red; }");
ui->statusLabel->setText(tr("The supplied proxy address is invalid."));
}
}
void OptionsDialog::updateDefaultProxyNets() {
proxyType proxy;
std::string strProxy;
QString strDefaultProxyGUI;
model->node().getProxy(NET_IPV4, proxy);
strProxy = proxy.proxy.ToStringIP() + ":" + proxy.proxy.ToStringPort();
strDefaultProxyGUI = ui->proxyIp->text() + ":" + ui->proxyPort->text();
(strProxy == strDefaultProxyGUI.toStdString())
? ui->proxyReachIPv4->setChecked(true)
: ui->proxyReachIPv4->setChecked(false);
model->node().getProxy(NET_IPV6, proxy);
strProxy = proxy.proxy.ToStringIP() + ":" + proxy.proxy.ToStringPort();
strDefaultProxyGUI = ui->proxyIp->text() + ":" + ui->proxyPort->text();
(strProxy == strDefaultProxyGUI.toStdString())
? ui->proxyReachIPv6->setChecked(true)
: ui->proxyReachIPv6->setChecked(false);
model->node().getProxy(NET_ONION, proxy);
strProxy = proxy.proxy.ToStringIP() + ":" + proxy.proxy.ToStringPort();
strDefaultProxyGUI = ui->proxyIp->text() + ":" + ui->proxyPort->text();
(strProxy == strDefaultProxyGUI.toStdString())
? ui->proxyReachTor->setChecked(true)
: ui->proxyReachTor->setChecked(false);
}
ProxyAddressValidator::ProxyAddressValidator(QObject *parent)
: QValidator(parent) {}
QValidator::State ProxyAddressValidator::validate(QString &input,
int &pos) const {
Q_UNUSED(pos);
// Validate the proxy
CService serv(
LookupNumeric(input.toStdString().c_str(), DEFAULT_GUI_PROXY_PORT));
proxyType addrProxy = proxyType(serv, true);
if (addrProxy.IsValid()) return QValidator::Acceptable;
return QValidator::Invalid;
}
diff --git a/src/qt/optionsdialog.h b/src/qt/optionsdialog.h
index 10a37205f..87361ff65 100644
--- a/src/qt/optionsdialog.h
+++ b/src/qt/optionsdialog.h
@@ -1,68 +1,69 @@
// Copyright (c) 2011-2016 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef BITCOIN_QT_OPTIONSDIALOG_H
#define BITCOIN_QT_OPTIONSDIALOG_H
#include
#include
class OptionsModel;
class QValidatedLineEdit;
QT_BEGIN_NAMESPACE
class QDataWidgetMapper;
QT_END_NAMESPACE
namespace Ui {
class OptionsDialog;
}
/** Proxy address widget validator, checks for a valid proxy address. */
class ProxyAddressValidator : public QValidator {
Q_OBJECT
public:
explicit ProxyAddressValidator(QObject *parent);
State validate(QString &input, int &pos) const override;
};
/** Preferences dialog. */
class OptionsDialog : public QDialog {
Q_OBJECT
public:
explicit OptionsDialog(QWidget *parent, bool enableWallet);
~OptionsDialog();
void setModel(OptionsModel *model);
void setMapper();
private Q_SLOTS:
/* set OK button state (enabled / disabled) */
void setOkButtonState(bool fState);
void on_resetButton_clicked();
void on_openBitcoinConfButton_clicked();
void on_okButton_clicked();
void on_cancelButton_clicked();
void on_hideTrayIcon_stateChanged(int fState);
+ void togglePruneWarning(bool enabled);
void showRestartWarning(bool fPersistent = false);
void clearStatusLabel();
void updateProxyValidationState();
/* query the networks, for which the default proxy is used */
void updateDefaultProxyNets();
Q_SIGNALS:
void proxyIpChecks(QValidatedLineEdit *pUiProxyIp, int nProxyPort);
private:
Ui::OptionsDialog *ui;
OptionsModel *model;
QDataWidgetMapper *mapper;
};
#endif // BITCOIN_QT_OPTIONSDIALOG_H
diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp
index 85d34b6d9..39a3eb312 100644
--- a/src/qt/optionsmodel.cpp
+++ b/src/qt/optionsmodel.cpp
@@ -1,545 +1,575 @@
// Copyright (c) 2011-2016 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
#endif
#include
#include
#include
#include
#include
#include
#include
#include
#include // for -dbcache defaults
#include // For DEFAULT_SCRIPTCHECK_THREADS
#include
#include
#include
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::BCH);
}
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", 2);
+ }
+ // Convert prune size to MB:
+ const uint64_t nPruneSizeMB = settings.value("nPruneSize").toInt() * 1000;
+ if (!m_node.softSetArg("-prune", settings.value("bPrune").toBool()
+ ? std::to_string(nPruneSizeMB)
+ : "0")) {
+ addOverriddenOption("-prune");
+ }
+
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", Intro::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) {
qWarning() << "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 = Intro::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);
}
// 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);
}
}
bool OptionsModel::getProxySettings(QNetworkProxy &proxy) const {
// Directly query current base proxy, because
// GUI settings can be overridden with -proxy.
proxyType curProxy;
if (m_node.getProxy(NET_IPV4, curProxy)) {
proxy.setType(QNetworkProxy::Socks5Proxy);
proxy.setHostName(QString::fromStdString(curProxy.proxy.ToStringIP()));
proxy.setPort(curProxy.proxy.GetPort());
return true;
} else
proxy.setType(QNetworkProxy::NoProxy);
return false;
}
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());
}
}
diff --git a/src/qt/optionsmodel.h b/src/qt/optionsmodel.h
index 266bfe1e0..9483be3d3 100644
--- a/src/qt/optionsmodel.h
+++ b/src/qt/optionsmodel.h
@@ -1,114 +1,116 @@
// Copyright (c) 2011-2016 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef BITCOIN_QT_OPTIONSMODEL_H
#define BITCOIN_QT_OPTIONSMODEL_H
#include
#include
namespace interfaces {
class Node;
}
QT_BEGIN_NAMESPACE
class QNetworkProxy;
QT_END_NAMESPACE
extern const char *DEFAULT_GUI_PROXY_HOST;
static constexpr unsigned short DEFAULT_GUI_PROXY_PORT = 9050;
/** Interface from Qt to configuration data structure for Bitcoin client.
To Qt, the options are presented as a list with the different options
laid out vertically.
This can be changed to a tree once the settings become sufficiently
complex.
*/
class OptionsModel : public QAbstractListModel {
Q_OBJECT
public:
explicit OptionsModel(interfaces::Node &node, QObject *parent = 0,
bool resetSettings = false);
enum OptionID {
StartAtStartup, // bool
HideTrayIcon, // bool
MinimizeToTray, // bool
MapPortUPnP, // bool
MinimizeOnClose, // bool
ProxyUse, // bool
ProxyIP, // QString
ProxyPort, // int
ProxyUseTor, // bool
ProxyIPTor, // QString
ProxyPortTor, // int
DisplayUnit, // BitcoinUnits::Unit
ThirdPartyTxUrls, // QString
Language, // QString
CoinControlFeatures, // bool
ThreadsScriptVerif, // int
+ Prune, // bool
+ PruneSize, // int
DatabaseCache, // int
SpendZeroConfChange, // bool
Listen, // bool
OptionIDRowCount,
};
void Init(bool resetSettings = false);
void Reset();
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index,
int role = Qt::DisplayRole) const override;
bool setData(const QModelIndex &index, const QVariant &value,
int role = Qt::EditRole) override;
/** Updates current unit in memory, settings and emits
* displayUnitChanged(newUnit) signal */
void setDisplayUnit(const QVariant &value);
/* Explicit getters */
bool getHideTrayIcon() const { return fHideTrayIcon; }
bool getMinimizeToTray() const { return fMinimizeToTray; }
bool getMinimizeOnClose() const { return fMinimizeOnClose; }
int getDisplayUnit() const { return nDisplayUnit; }
QString getThirdPartyTxUrls() const { return strThirdPartyTxUrls; }
bool getProxySettings(QNetworkProxy &proxy) const;
bool getCoinControlFeatures() const { return fCoinControlFeatures; }
const QString &getOverriddenByCommandLine() {
return strOverriddenByCommandLine;
}
/* Restart flag helper */
void setRestartRequired(bool fRequired);
bool isRestartRequired() const;
interfaces::Node &node() const { return m_node; }
private:
interfaces::Node &m_node;
/* Qt-only settings */
bool fHideTrayIcon;
bool fMinimizeToTray;
bool fMinimizeOnClose;
QString language;
int nDisplayUnit;
QString strThirdPartyTxUrls;
bool fCoinControlFeatures;
/* settings that were overridden by command-line */
QString strOverriddenByCommandLine;
// Add option to list of GUI options overridden through command line/config
// file
void addOverriddenOption(const std::string &option);
// Check settings version and upgrade default values if required
void checkAndMigrate();
Q_SIGNALS:
void displayUnitChanged(int unit);
void coinControlFeaturesChanged(bool);
void hideTrayIconChanged(bool);
};
#endif // BITCOIN_QT_OPTIONSMODEL_H