Bitcoin Core 28.0.0
P2P Digital Currency
Loading...
Searching...
No Matches
optionsdialog.cpp
Go to the documentation of this file.
1// Copyright (c) 2011-2022 The Bitcoin Core developers
2// Distributed under the MIT software license, see the accompanying
3// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4
5#include <config/bitcoin-config.h> // IWYU pragma: keep
6
7#include <qt/optionsdialog.h>
9
10#include <qt/bitcoinunits.h>
11#include <qt/clientmodel.h>
12#include <qt/guiconstants.h>
13#include <qt/guiutil.h>
14#include <qt/optionsmodel.h>
15
16#include <common/system.h>
17#include <interfaces/node.h>
19#include <netbase.h>
20#include <txdb.h>
21#include <util/strencodings.h>
22
23#include <chrono>
24
25#include <QApplication>
26#include <QDataWidgetMapper>
27#include <QDir>
28#include <QFontDialog>
29#include <QIntValidator>
30#include <QLocale>
31#include <QMessageBox>
32#include <QSystemTrayIcon>
33#include <QTimer>
34
35int setFontChoice(QComboBox* cb, const OptionsModel::FontChoice& fc)
36{
37 int i;
38 for (i = cb->count(); --i >= 0; ) {
39 QVariant item_data = cb->itemData(i);
40 if (!item_data.canConvert<OptionsModel::FontChoice>()) continue;
41 if (item_data.value<OptionsModel::FontChoice>() == fc) {
42 break;
43 }
44 }
45 if (i == -1) {
46 // New item needed
47 QFont chosen_font = OptionsModel::getFontForChoice(fc);
48 QSignalBlocker block_currentindexchanged_signal(cb); // avoid triggering QFontDialog
49 cb->insertItem(0, QFontInfo(chosen_font).family(), QVariant::fromValue(fc));
50 i = 0;
51 }
52
53 cb->setCurrentIndex(i);
54 return i;
55}
56
57void setupFontOptions(QComboBox* cb, QLabel* preview)
58{
59 QFont embedded_font{GUIUtil::fixedPitchFont(true)};
60 QFont system_font{GUIUtil::fixedPitchFont(false)};
61 cb->addItem(QObject::tr("Embedded \"%1\"").arg(QFontInfo(embedded_font).family()), QVariant::fromValue(OptionsModel::FontChoice{OptionsModel::FontChoiceAbstract::EmbeddedFont}));
62 cb->addItem(QObject::tr("Default system font \"%1\"").arg(QFontInfo(system_font).family()), QVariant::fromValue(OptionsModel::FontChoice{OptionsModel::FontChoiceAbstract::BestSystemFont}));
63 cb->addItem(QObject::tr("Custom…"));
64
65 const auto& on_font_choice_changed = [cb, preview](int index) {
66 static int previous_index = -1;
67 QVariant item_data = cb->itemData(index);
68 QFont f;
69 if (item_data.canConvert<OptionsModel::FontChoice>()) {
71 } else {
72 bool ok;
73 f = QFontDialog::getFont(&ok, GUIUtil::fixedPitchFont(false), cb->parentWidget());
74 if (!ok) {
75 cb->setCurrentIndex(previous_index);
76 return;
77 }
79 }
80 if (preview) {
81 preview->setFont(f);
82 }
83 previous_index = index;
84 };
85 QObject::connect(cb, QOverload<int>::of(&QComboBox::currentIndexChanged), on_font_choice_changed);
86 on_font_choice_changed(cb->currentIndex());
87}
88
89OptionsDialog::OptionsDialog(QWidget* parent, bool enableWallet)
90 : QDialog(parent, GUIUtil::dialog_flags | Qt::WindowMaximizeButtonHint),
91 ui(new Ui::OptionsDialog)
92{
93 ui->setupUi(this);
94
95 ui->verticalLayout->setStretchFactor(ui->tabWidget, 1);
96
97 /* Main elements init */
98 ui->databaseCache->setMinimum(nMinDbCache);
99 ui->databaseCache->setMaximum(nMaxDbCache);
100 ui->threadsScriptVerif->setMinimum(-GetNumCores());
102 ui->pruneWarning->setVisible(false);
103 ui->pruneWarning->setStyleSheet("QLabel { color: red; }");
104
105 ui->pruneSize->setEnabled(false);
106 connect(ui->prune, &QPushButton::toggled, ui->pruneSize, &QWidget::setEnabled);
107
108 /* Network elements init */
109#ifndef USE_UPNP
110 ui->mapPortUpnp->setEnabled(false);
111#endif
112#ifndef USE_NATPMP
113 ui->mapPortNatpmp->setEnabled(false);
114#endif
115
116 ui->proxyIp->setEnabled(false);
117 ui->proxyPort->setEnabled(false);
118 ui->proxyPort->setValidator(new QIntValidator(1, 65535, this));
119
120 ui->proxyIpTor->setEnabled(false);
121 ui->proxyPortTor->setEnabled(false);
122 ui->proxyPortTor->setValidator(new QIntValidator(1, 65535, this));
123
124 connect(ui->connectSocks, &QPushButton::toggled, ui->proxyIp, &QWidget::setEnabled);
125 connect(ui->connectSocks, &QPushButton::toggled, ui->proxyPort, &QWidget::setEnabled);
126 connect(ui->connectSocks, &QPushButton::toggled, this, &OptionsDialog::updateProxyValidationState);
127
128 connect(ui->connectSocksTor, &QPushButton::toggled, ui->proxyIpTor, &QWidget::setEnabled);
129 connect(ui->connectSocksTor, &QPushButton::toggled, ui->proxyPortTor, &QWidget::setEnabled);
130 connect(ui->connectSocksTor, &QPushButton::toggled, this, &OptionsDialog::updateProxyValidationState);
131
132 /* Window elements init */
133#ifdef Q_OS_MACOS
134 /* remove Window tab on Mac */
135 ui->tabWidget->removeTab(ui->tabWidget->indexOf(ui->tabWindow));
136 /* hide launch at startup option on macOS */
137 ui->bitcoinAtStartup->setVisible(false);
138 ui->verticalLayout_Main->removeWidget(ui->bitcoinAtStartup);
140#endif
141
142 /* remove Wallet tab and 3rd party-URL textbox in case of -disablewallet */
143 if (!enableWallet) {
144 ui->tabWidget->removeTab(ui->tabWidget->indexOf(ui->tabWallet));
145 ui->thirdPartyTxUrlsLabel->setVisible(false);
146 ui->thirdPartyTxUrls->setVisible(false);
147 }
148
149#ifdef ENABLE_EXTERNAL_SIGNER
150 ui->externalSignerPath->setToolTip(ui->externalSignerPath->toolTip().arg(PACKAGE_NAME));
151#else
152 //: "External signing" means using devices such as hardware wallets.
153 ui->externalSignerPath->setToolTip(tr("Compiled without external signing support (required for external signing)"));
154 ui->externalSignerPath->setEnabled(false);
155#endif
156 /* Display elements init */
157 QDir translations(":translations");
158
159 ui->bitcoinAtStartup->setToolTip(ui->bitcoinAtStartup->toolTip().arg(PACKAGE_NAME));
160 ui->bitcoinAtStartup->setText(ui->bitcoinAtStartup->text().arg(PACKAGE_NAME));
161
162 ui->openBitcoinConfButton->setToolTip(ui->openBitcoinConfButton->toolTip().arg(PACKAGE_NAME));
163
164 ui->lang->setToolTip(ui->lang->toolTip().arg(PACKAGE_NAME));
165 ui->lang->addItem(QString("(") + tr("default") + QString(")"), QVariant(""));
166 for (const QString &langStr : translations.entryList())
167 {
168 QLocale locale(langStr);
169
171 if(langStr.contains("_"))
172 {
174 ui->lang->addItem(locale.nativeLanguageName() + QString(" - ") + locale.nativeCountryName() + QString(" (") + langStr + QString(")"), QVariant(langStr));
175 }
176 else
177 {
179 ui->lang->addItem(locale.nativeLanguageName() + QString(" (") + langStr + QString(")"), QVariant(langStr));
180 }
181 }
182 ui->unit->setModel(new BitcoinUnits(this));
183
184 /* Widget-to-option mapper */
185 mapper = new QDataWidgetMapper(this);
186 mapper->setSubmitPolicy(QDataWidgetMapper::ManualSubmit);
187 mapper->setOrientation(Qt::Vertical);
188
190 connect(delegate, &GUIUtil::ItemDelegate::keyEscapePressed, this, &OptionsDialog::reject);
191 mapper->setItemDelegate(delegate);
192
193 /* setup/change UI elements when proxy IPs are invalid/valid */
198 connect(ui->proxyPort, &QLineEdit::textChanged, this, &OptionsDialog::updateProxyValidationState);
199 connect(ui->proxyPortTor, &QLineEdit::textChanged, this, &OptionsDialog::updateProxyValidationState);
200
201 if (!QSystemTrayIcon::isSystemTrayAvailable()) {
202 ui->showTrayIcon->setChecked(false);
203 ui->showTrayIcon->setEnabled(false);
204 ui->minimizeToTray->setChecked(false);
205 ui->minimizeToTray->setEnabled(false);
206 }
207
209
211}
212
214{
215 delete ui;
216}
217
219{
220 m_client_model = client_model;
221}
222
224{
225 this->model = _model;
226
227 if(_model)
228 {
229 /* check if client restart is needed and show persistent message */
230 if (_model->isRestartRequired())
231 showRestartWarning(true);
232
233 // Prune values are in GB to be consistent with intro.cpp
234 static constexpr uint64_t nMinDiskSpace = (MIN_DISK_SPACE_FOR_BLOCK_FILES / GB_BYTES) + (MIN_DISK_SPACE_FOR_BLOCK_FILES % GB_BYTES) ? 1 : 0;
235 ui->pruneSize->setRange(nMinDiskSpace, std::numeric_limits<int>::max());
236
237 QString strLabel = _model->getOverriddenByCommandLine();
238 if (strLabel.isEmpty())
239 strLabel = tr("none");
240 ui->overriddenByCommandLineLabel->setText(strLabel);
241
242 mapper->setModel(_model);
243 setMapper();
244 mapper->toFirst();
245
246 const auto& font_for_money = _model->data(_model->index(OptionsModel::FontForMoney, 0), Qt::EditRole).value<OptionsModel::FontChoice>();
247 setFontChoice(ui->moneyFont, font_for_money);
248
250 }
251
252 /* warn when one of the following settings changes by user action (placed here so init via mapper doesn't trigger them) */
253
254 /* Main */
255 connect(ui->prune, &QCheckBox::clicked, this, &OptionsDialog::showRestartWarning);
256 connect(ui->prune, &QCheckBox::clicked, this, &OptionsDialog::togglePruneWarning);
257 connect(ui->pruneSize, qOverload<int>(&QSpinBox::valueChanged), this, &OptionsDialog::showRestartWarning);
258 connect(ui->databaseCache, qOverload<int>(&QSpinBox::valueChanged), this, &OptionsDialog::showRestartWarning);
259 connect(ui->externalSignerPath, &QLineEdit::textChanged, [this]{ showRestartWarning(); });
260 connect(ui->threadsScriptVerif, qOverload<int>(&QSpinBox::valueChanged), this, &OptionsDialog::showRestartWarning);
261 /* Wallet */
262 connect(ui->spendZeroConfChange, &QCheckBox::clicked, this, &OptionsDialog::showRestartWarning);
263 /* Network */
264 connect(ui->allowIncoming, &QCheckBox::clicked, this, &OptionsDialog::showRestartWarning);
265 connect(ui->enableServer, &QCheckBox::clicked, this, &OptionsDialog::showRestartWarning);
266 connect(ui->connectSocks, &QCheckBox::clicked, this, &OptionsDialog::showRestartWarning);
267 connect(ui->connectSocksTor, &QCheckBox::clicked, this, &OptionsDialog::showRestartWarning);
268 /* Display */
269 connect(ui->lang, qOverload<>(&QValueComboBox::valueChanged), [this]{ showRestartWarning(); });
270 connect(ui->thirdPartyTxUrls, &QLineEdit::textChanged, [this]{ showRestartWarning(); });
271}
272
274{
275 QWidget *tab_widget = nullptr;
276 if (tab == OptionsDialog::Tab::TAB_NETWORK) tab_widget = ui->tabNetwork;
277 if (tab == OptionsDialog::Tab::TAB_MAIN) tab_widget = ui->tabMain;
278 if (tab_widget && ui->tabWidget->currentWidget() != tab_widget) {
279 ui->tabWidget->setCurrentWidget(tab_widget);
280 }
281}
282
284{
285 /* Main */
289 mapper->addMapping(ui->prune, OptionsModel::Prune);
291
292 /* Wallet */
298
299 /* Network */
304
306 mapper->addMapping(ui->proxyIp, OptionsModel::ProxyIP);
308
312
313 /* Window */
314#ifndef Q_OS_MACOS
315 if (QSystemTrayIcon::isSystemTrayAvailable()) {
318 }
320#endif
321
322 /* Display */
323 mapper->addMapping(ui->lang, OptionsModel::Language);
326}
327
329{
330 ui->okButton->setEnabled(fState);
331}
332
334{
335 if (model) {
336 // confirmation dialog
337 /*: Text explaining that the settings changed will not come into effect
338 until the client is restarted. */
339 QString reset_dialog_text = tr("Client restart required to activate changes.") + "<br><br>";
340 /*: Text explaining to the user that the client's current settings
341 will be backed up at a specific location. %1 is a stand-in
342 argument for the backup location's path. */
343 reset_dialog_text.append(tr("Current settings will be backed up at \"%1\".").arg(m_client_model->dataDir()) + "<br><br>");
344 /*: Text asking the user to confirm if they would like to proceed
345 with a client shutdown. */
346 reset_dialog_text.append(tr("Client will be shut down. Do you want to proceed?"));
347 //: Window title text of pop-up window shown when the user has chosen to reset options.
348 QMessageBox::StandardButton btnRetVal = QMessageBox::question(this, tr("Confirm options reset"),
349 reset_dialog_text, QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Cancel);
350
351 if (btnRetVal == QMessageBox::Cancel)
352 return;
353
354 /* reset all options and close GUI */
355 model->Reset();
356 close();
357 Q_EMIT quitOnReset();
358 }
359}
360
362{
363 QMessageBox config_msgbox(this);
364 config_msgbox.setIcon(QMessageBox::Information);
365 //: Window title text of pop-up box that allows opening up of configuration file.
366 config_msgbox.setWindowTitle(tr("Configuration options"));
367 /*: Explanatory text about the priority order of instructions considered by client.
368 The order from high to low being: command-line, configuration file, GUI settings. */
369 config_msgbox.setText(tr("The configuration file is used to specify advanced user options which override GUI settings. "
370 "Additionally, any command-line options will override this configuration file."));
371
372 QPushButton* open_button = config_msgbox.addButton(tr("Continue"), QMessageBox::ActionRole);
373 config_msgbox.addButton(tr("Cancel"), QMessageBox::RejectRole);
374 open_button->setDefault(true);
375
376 config_msgbox.exec();
377
378 if (config_msgbox.clickedButton() != open_button) return;
379
380 /* show an error if there was some problem opening the file */
382 QMessageBox::critical(this, tr("Error"), tr("The configuration file could not be opened."));
383}
384
386{
387 model->setData(model->index(OptionsModel::FontForMoney, 0), ui->moneyFont->itemData(ui->moneyFont->currentIndex()));
388
389 mapper->submit();
390 accept();
392}
393
395{
396 reject();
397}
398
400{
401 if (state == Qt::Checked) {
402 ui->minimizeToTray->setEnabled(true);
403 } else {
404 ui->minimizeToTray->setChecked(false);
405 ui->minimizeToTray->setEnabled(false);
406 }
407}
408
410{
411 ui->pruneWarning->setVisible(!ui->pruneWarning->isVisible());
412}
413
415{
416 ui->statusLabel->setStyleSheet("QLabel { color: red; }");
417
418 if(fPersistent)
419 {
420 ui->statusLabel->setText(tr("Client restart required to activate changes."));
421 }
422 else
423 {
424 ui->statusLabel->setText(tr("This change would require a client restart."));
425 // clear non-persistent status label after 10 seconds
426 // Todo: should perhaps be a class attribute, if we extend the use of statusLabel
427 QTimer::singleShot(10s, this, &OptionsDialog::clearStatusLabel);
428 }
429}
430
432{
433 ui->statusLabel->clear();
434 if (model && model->isRestartRequired()) {
435 showRestartWarning(true);
436 }
437}
438
440{
441 QValidatedLineEdit *pUiProxyIp = ui->proxyIp;
442 QValidatedLineEdit *otherProxyWidget = (pUiProxyIp == ui->proxyIpTor) ? ui->proxyIp : ui->proxyIpTor;
443 if (pUiProxyIp->isValid() && (!ui->proxyPort->isEnabled() || ui->proxyPort->text().toInt() > 0) && (!ui->proxyPortTor->isEnabled() || ui->proxyPortTor->text().toInt() > 0))
444 {
445 setOkButtonState(otherProxyWidget->isValid()); //only enable ok button if both proxies are valid
447 }
448 else
449 {
450 setOkButtonState(false);
451 ui->statusLabel->setStyleSheet("QLabel { color: red; }");
452 ui->statusLabel->setText(tr("The supplied proxy address is invalid."));
453 }
454}
455
457{
458 std::string proxyIpText{ui->proxyIp->text().toStdString()};
459 if (!IsUnixSocketPath(proxyIpText)) {
460 const std::optional<CNetAddr> ui_proxy_netaddr{LookupHost(proxyIpText, /*fAllowLookup=*/false)};
461 const CService ui_proxy{ui_proxy_netaddr.value_or(CNetAddr{}), ui->proxyPort->text().toUShort()};
462 proxyIpText = ui_proxy.ToStringAddrPort();
463 }
464
465 Proxy proxy;
466 bool has_proxy;
467
468 has_proxy = model->node().getProxy(NET_IPV4, proxy);
469 ui->proxyReachIPv4->setChecked(has_proxy && proxy.ToString() == proxyIpText);
470
471 has_proxy = model->node().getProxy(NET_IPV6, proxy);
472 ui->proxyReachIPv6->setChecked(has_proxy && proxy.ToString() == proxyIpText);
473
474 has_proxy = model->node().getProxy(NET_ONION, proxy);
475 ui->proxyReachTor->setChecked(has_proxy && proxy.ToString() == proxyIpText);
476}
477
479QValidator(parent)
480{
481}
482
483QValidator::State ProxyAddressValidator::validate(QString &input, int &pos) const
484{
485 Q_UNUSED(pos);
486 uint16_t port{0};
487 std::string hostname;
488 if (!SplitHostPort(input.toStdString(), port, hostname) || port != 0) return QValidator::Invalid;
489
490 CService serv(LookupNumeric(input.toStdString(), DEFAULT_GUI_PROXY_PORT));
491 Proxy addrProxy = Proxy(serv, true);
492 if (addrProxy.IsValid())
493 return QValidator::Acceptable;
494
495 return QValidator::Invalid;
496}
#define PACKAGE_NAME
static constexpr int MAX_SCRIPTCHECK_THREADS
Maximum number of dedicated script-checking threads allowed.
Bitcoin unit definitions.
Network address.
Definition netaddress.h:112
A combination of a network address (CNetAddr) and a (TCP) port.
Definition netaddress.h:531
std::string ToStringAddrPort() const
Model for Bitcoin network client.
Definition clientmodel.h:57
QString dataDir() const
Preferences dialog.
void setModel(OptionsModel *model)
OptionsModel * model
void setCurrentTab(OptionsDialog::Tab tab)
void on_showTrayIcon_stateChanged(int state)
void on_okButton_clicked()
void on_openBitcoinConfButton_clicked()
void updateDefaultProxyNets()
void updateProxyValidationState()
void togglePruneWarning(bool enabled)
void showRestartWarning(bool fPersistent=false)
ClientModel * m_client_model
void on_resetButton_clicked()
Ui::OptionsDialog * ui
OptionsDialog(QWidget *parent, bool enableWallet)
QDataWidgetMapper * mapper
void setClientModel(ClientModel *client_model)
void on_cancelButton_clicked()
void setOkButtonState(bool fState)
Interface from Qt to configuration data structure for Bitcoin client.
QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const override
bool isRestartRequired() const
interfaces::Node & node() const
std::variant< FontChoiceAbstract, QFont > FontChoice
bool setData(const QModelIndex &index, const QVariant &value, int role=Qt::EditRole) override
const QString & getOverriddenByCommandLine()
static QFont getFontForChoice(const FontChoice &fc)
Proxy address widget validator, checks for a valid proxy address.
ProxyAddressValidator(QObject *parent)
State validate(QString &input, int &pos) const override
std::string ToString() const
Definition netbase.h:82
bool IsValid() const
Definition netbase.h:70
Line edit that can be marked as "invalid" to show input validation feedback.
void validationDidChange(QValidatedLineEdit *validatedLineEdit)
void setEnabled(bool enabled)
void setCheckValidator(const QValidator *v)
QValueComboBox * lang
QVBoxLayout * verticalLayout
QLabel * overriddenByCommandLineLabel
QLineEdit * proxyPortTor
QSpinBox * threadsScriptVerif
QCheckBox * connectSocksTor
QSpinBox * databaseCache
QCheckBox * minimizeToTray
void setupUi(QDialog *OptionsDialog)
QCheckBox * bitcoinAtStartup
QVBoxLayout * verticalLayout_Main
QLineEdit * externalSignerPath
QCheckBox * subFeeFromAmount
QPushButton * openBitcoinConfButton
QCheckBox * coinControlFeatures
QPushButton * okButton
QCheckBox * mapPortUpnp
QValidatedLineEdit * proxyIpTor
QTabWidget * tabWidget
QCheckBox * allowIncoming
QLineEdit * thirdPartyTxUrls
QValueComboBox * unit
QLineEdit * proxyPort
QSpacerItem * horizontalSpacer_0_Main
QCheckBox * spendZeroConfChange
QCheckBox * mapPortNatpmp
QCheckBox * proxyReachIPv4
QCheckBox * proxyReachIPv6
QCheckBox * enableServer
QCheckBox * proxyReachTor
QCheckBox * showTrayIcon
QValidatedLineEdit * proxyIp
QCheckBox * m_enable_psbt_controls
QCheckBox * minimizeOnClose
QCheckBox * connectSocks
QLabel * thirdPartyTxUrlsLabel
virtual bool getProxy(Network net, Proxy &proxy_info)=0
Get proxy.
int GetNumCores()
Return the number of cores available on the current system.
Definition system.cpp:103
static constexpr uint64_t GB_BYTES
Utility functions used by the Bitcoin Qt UI.
Definition bitcoingui.h:58
bool openBitcoinConf()
Definition guiutil.cpp:445
QFont fixedPitchFont(bool use_embedded_font)
Definition guiutil.cpp:100
void handleCloseWindowShortcut(QWidget *w)
Definition guiutil.cpp:431
@ NET_ONION
TOR (v2 or v3)
Definition netaddress.h:43
@ NET_IPV6
IPv6.
Definition netaddress.h:40
@ NET_IPV4
IPv4.
Definition netaddress.h:37
std::vector< CNetAddr > LookupHost(const std::string &name, unsigned int nMaxSolutions, bool fAllowLookup, DNSLookupFn dns_lookup_function)
Resolve a host string to its corresponding network addresses.
Definition netbase.cpp:177
bool IsUnixSocketPath(const std::string &name)
Check if a string is a valid UNIX domain socket path.
Definition netbase.cpp:230
CService LookupNumeric(const std::string &name, uint16_t portDefault, DNSLookupFn dns_lookup_function)
Resolve a service string with a numeric IP to its first corresponding service.
Definition netbase.cpp:220
void setupFontOptions(QComboBox *cb, QLabel *preview)
int setFontChoice(QComboBox *cb, const OptionsModel::FontChoice &fc)
static constexpr uint16_t DEFAULT_GUI_PROXY_PORT
static const int64_t nMinDbCache
min. -dbcache (MiB)
Definition txdb.h:31
static const int64_t nMaxDbCache
max. -dbcache (MiB)
Definition txdb.h:29
bool SplitHostPort(std::string_view in, uint16_t &portOut, std::string &hostOut)
Splits socket address string into host string and port value.
static const uint64_t MIN_DISK_SPACE_FOR_BLOCK_FILES
Definition validation.h:79