Bitcoin ABC  0.22.12
P2P Digital Currency
optionsdialog.cpp
Go to the documentation of this file.
1 // Copyright (c) 2011-2016 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 #if defined(HAVE_CONFIG_H)
6 #include <config/bitcoin-config.h>
7 #endif
8 
9 #include <qt/forms/ui_optionsdialog.h>
10 #include <qt/optionsdialog.h>
11 
12 #include <interfaces/node.h>
13 #include <netbase.h>
14 #include <qt/bitcoinunits.h>
15 #include <qt/guiconstants.h>
16 #include <qt/guiutil.h>
17 #include <qt/optionsmodel.h>
18 #include <txdb.h> // for -dbcache defaults
19 #include <validation.h> // for DEFAULT_SCRIPTCHECK_THREADS and MAX_SCRIPTCHECK_THREADS
20 
21 #include <QDataWidgetMapper>
22 #include <QDir>
23 #include <QIntValidator>
24 #include <QLocale>
25 #include <QMessageBox>
26 #include <QSystemTrayIcon>
27 #include <QTimer>
28 
29 OptionsDialog::OptionsDialog(QWidget *parent, bool enableWallet)
30  : QDialog(parent), ui(new Ui::OptionsDialog), model(nullptr),
31  mapper(nullptr) {
32  ui->setupUi(this);
33 
34  /* Main elements init */
35  ui->databaseCache->setMinimum(MIN_DB_CACHE_MB);
36  ui->databaseCache->setMaximum(MAX_DB_CACHE_MB);
37  ui->threadsScriptVerif->setMinimum(-GetNumCores());
38  ui->threadsScriptVerif->setMaximum(MAX_SCRIPTCHECK_THREADS);
39  ui->pruneWarning->setVisible(false);
40  ui->pruneWarning->setStyleSheet("QLabel { color: red; }");
41 
42  ui->pruneSize->setEnabled(false);
43  connect(ui->prune, &QPushButton::toggled, ui->pruneSize,
44  &QWidget::setEnabled);
45 
46 /* Network elements init */
47 #ifndef USE_UPNP
48  ui->mapPortUpnp->setEnabled(false);
49 #endif
50 
51  ui->proxyIp->setEnabled(false);
52  ui->proxyPort->setEnabled(false);
53  ui->proxyPort->setValidator(new QIntValidator(1, 65535, this));
54 
55  ui->proxyIpTor->setEnabled(false);
56  ui->proxyPortTor->setEnabled(false);
57  ui->proxyPortTor->setValidator(new QIntValidator(1, 65535, this));
58 
59  connect(ui->connectSocks, &QPushButton::toggled, ui->proxyIp,
60  &QWidget::setEnabled);
61  connect(ui->connectSocks, &QPushButton::toggled, ui->proxyPort,
62  &QWidget::setEnabled);
63  connect(ui->connectSocks, &QPushButton::toggled, this,
65 
66  connect(ui->connectSocksTor, &QPushButton::toggled, ui->proxyIpTor,
67  &QWidget::setEnabled);
68  connect(ui->connectSocksTor, &QPushButton::toggled, ui->proxyPortTor,
69  &QWidget::setEnabled);
70  connect(ui->connectSocksTor, &QPushButton::toggled, this,
72 
73  /* Window elements init */
74 #ifdef Q_OS_MAC
75  /* remove Window tab on Mac */
76  ui->tabWidget->removeTab(ui->tabWidget->indexOf(ui->tabWindow));
77  /* hide launch at startup option on macOS */
78  ui->bitcoinAtStartup->setVisible(false);
79  ui->verticalLayout_Main->removeWidget(ui->bitcoinAtStartup);
80  ui->verticalLayout_Main->removeItem(ui->horizontalSpacer_0_Main);
81 #endif
82 
83  /* remove Wallet tab and 3rd party-URL textbox in case of -disablewallet */
84  if (!enableWallet) {
85  ui->tabWidget->removeTab(ui->tabWidget->indexOf(ui->tabWallet));
86  ui->thirdPartyTxUrlsLabel->setVisible(false);
87  ui->thirdPartyTxUrls->setVisible(false);
88  }
89 
90  /* Display elements init */
91  QDir translations(":translations");
92 
93  ui->bitcoinAtStartup->setToolTip(
94  ui->bitcoinAtStartup->toolTip().arg(PACKAGE_NAME));
95  ui->bitcoinAtStartup->setText(
96  ui->bitcoinAtStartup->text().arg(PACKAGE_NAME));
97 
98  ui->openBitcoinConfButton->setToolTip(
99  ui->openBitcoinConfButton->toolTip().arg(PACKAGE_NAME));
100 
101  ui->lang->setToolTip(ui->lang->toolTip().arg(PACKAGE_NAME));
102  ui->lang->addItem(QString("(") + tr("default") + QString(")"),
103  QVariant(""));
104  for (const QString &langStr : translations.entryList()) {
105  QLocale locale(langStr);
106 
108  if (langStr.contains("_")) {
111  ui->lang->addItem(locale.nativeLanguageName() + QString(" - ") +
112  locale.nativeCountryName() + QString(" (") +
113  langStr + QString(")"),
114  QVariant(langStr));
115  } else {
118  ui->lang->addItem(locale.nativeLanguageName() + QString(" (") +
119  langStr + QString(")"),
120  QVariant(langStr));
121  }
122  }
123  ui->unit->setModel(new BitcoinUnits(this));
124 
125  /* Widget-to-option mapper */
126  mapper = new QDataWidgetMapper(this);
127  mapper->setSubmitPolicy(QDataWidgetMapper::ManualSubmit);
128  mapper->setOrientation(Qt::Vertical);
129 
131  connect(delegate, &GUIUtil::ItemDelegate::keyEscapePressed, this,
132  &OptionsDialog::reject);
133  mapper->setItemDelegate(delegate);
134 
135  /* setup/change UI elements when proxy IPs are invalid/valid */
136  ui->proxyIp->setCheckValidator(new ProxyAddressValidator(parent));
137  ui->proxyIpTor->setCheckValidator(new ProxyAddressValidator(parent));
138  connect(ui->proxyIp, &QValidatedLineEdit::validationDidChange, this,
140  connect(ui->proxyIpTor, &QValidatedLineEdit::validationDidChange, this,
142  connect(ui->proxyPort, &QLineEdit::textChanged, this,
144  connect(ui->proxyPortTor, &QLineEdit::textChanged, this,
146 
147  if (!QSystemTrayIcon::isSystemTrayAvailable()) {
148  ui->hideTrayIcon->setChecked(true);
149  ui->hideTrayIcon->setEnabled(false);
150  ui->minimizeToTray->setChecked(false);
151  ui->minimizeToTray->setEnabled(false);
152  }
153 }
154 
156  delete ui;
157 }
158 
160  this->model = _model;
161 
162  if (_model) {
163  /* check if client restart is needed and show persistent message */
164  if (_model->isRestartRequired()) {
165  showRestartWarning(true);
166  }
167 
168  // Prune values are in GB to be consistent with intro.cpp
169  static constexpr uint64_t nMinDiskSpace =
172  ? 1
173  : 0;
174  ui->pruneSize->setRange(nMinDiskSpace, std::numeric_limits<int>::max());
175 
176  QString strLabel = _model->getOverriddenByCommandLine();
177  if (strLabel.isEmpty()) {
178  strLabel = tr("none");
179  }
180  ui->overriddenByCommandLineLabel->setText(strLabel);
181 
182  mapper->setModel(_model);
183  setMapper();
184  mapper->toFirst();
185 
187  }
188 
189  /* warn when one of the following settings changes by user action (placed
190  * here so init via mapper doesn't trigger them) */
191 
192  /* Main */
193  connect(ui->prune, &QCheckBox::clicked, this,
195  connect(ui->prune, &QCheckBox::clicked, this,
197  connect(ui->pruneSize,
198  static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this,
200  connect(ui->databaseCache,
201  static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this,
203  connect(ui->threadsScriptVerif,
204  static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this,
206  /* Wallet */
207  connect(ui->spendZeroConfChange, &QCheckBox::clicked, this,
209  /* Network */
210  connect(ui->allowIncoming, &QCheckBox::clicked, this,
212  connect(ui->connectSocks, &QCheckBox::clicked, this,
214  connect(ui->connectSocksTor, &QCheckBox::clicked, this,
216  /* Display */
217  connect(
218  ui->lang,
219  static_cast<void (QValueComboBox::*)()>(&QValueComboBox::valueChanged),
220  [this] { showRestartWarning(); });
221  connect(ui->thirdPartyTxUrls, &QLineEdit::textChanged,
222  [this] { showRestartWarning(); });
223 }
224 
226  QWidget *tab_widget = nullptr;
227  if (tab == OptionsDialog::Tab::TAB_NETWORK) {
228  tab_widget = ui->tabNetwork;
229  }
230  if (tab == OptionsDialog::Tab::TAB_MAIN) {
231  tab_widget = ui->tabMain;
232  }
233  if (tab_widget && ui->tabWidget->currentWidget() != tab_widget) {
234  ui->tabWidget->setCurrentWidget(tab_widget);
235  }
236 }
237 
239  /* Main */
240  mapper->addMapping(ui->bitcoinAtStartup, OptionsModel::StartAtStartup);
241  mapper->addMapping(ui->threadsScriptVerif,
243  mapper->addMapping(ui->databaseCache, OptionsModel::DatabaseCache);
244  mapper->addMapping(ui->prune, OptionsModel::Prune);
245  mapper->addMapping(ui->pruneSize, OptionsModel::PruneSize);
246 
247  /* Wallet */
248  mapper->addMapping(ui->spendZeroConfChange,
250  mapper->addMapping(ui->coinControlFeatures,
252 
253  /* Network */
254  mapper->addMapping(ui->mapPortUpnp, OptionsModel::MapPortUPnP);
255  mapper->addMapping(ui->allowIncoming, OptionsModel::Listen);
256 
257  mapper->addMapping(ui->connectSocks, OptionsModel::ProxyUse);
258  mapper->addMapping(ui->proxyIp, OptionsModel::ProxyIP);
259  mapper->addMapping(ui->proxyPort, OptionsModel::ProxyPort);
260 
261  mapper->addMapping(ui->connectSocksTor, OptionsModel::ProxyUseTor);
262  mapper->addMapping(ui->proxyIpTor, OptionsModel::ProxyIPTor);
263  mapper->addMapping(ui->proxyPortTor, OptionsModel::ProxyPortTor);
264 
265 /* Window */
266 #ifndef Q_OS_MAC
267  if (QSystemTrayIcon::isSystemTrayAvailable()) {
268  mapper->addMapping(ui->hideTrayIcon, OptionsModel::HideTrayIcon);
269  mapper->addMapping(ui->minimizeToTray, OptionsModel::MinimizeToTray);
270  }
271  mapper->addMapping(ui->minimizeOnClose, OptionsModel::MinimizeOnClose);
272 #endif
273 
274  /* Display */
275  mapper->addMapping(ui->lang, OptionsModel::Language);
276  mapper->addMapping(ui->unit, OptionsModel::DisplayUnit);
277  mapper->addMapping(ui->thirdPartyTxUrls, OptionsModel::ThirdPartyTxUrls);
278 }
279 
281  ui->okButton->setEnabled(fState);
282 }
283 
285  if (model) {
286  // confirmation dialog
287  QMessageBox::StandardButton btnRetVal = QMessageBox::question(
288  this, tr("Confirm options reset"),
289  tr("Client restart required to activate changes.") + "<br><br>" +
290  tr("Client will be shut down. Do you want to proceed?"),
291  QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Cancel);
292 
293  if (btnRetVal == QMessageBox::Cancel) {
294  return;
295  }
296 
297  /* reset all options and close GUI */
298  model->Reset();
299  QApplication::quit();
300  }
301 }
302 
304  /* explain the purpose of the config file */
305  QMessageBox::information(
306  this, tr("Configuration options"),
307  tr("The configuration file is used to specify advanced user options "
308  "which override GUI settings. Additionally, any command-line "
309  "options will override this configuration file."));
310 
311  /* show an error if there was some problem opening the file */
312  if (!GUIUtil::openBitcoinConf()) {
313  QMessageBox::critical(
314  this, tr("Error"),
315  tr("The configuration file could not be opened."));
316  }
317 }
318 
320  mapper->submit();
321  accept();
323 }
324 
326  reject();
327 }
328 
330  if (fState) {
331  ui->minimizeToTray->setChecked(false);
332  ui->minimizeToTray->setEnabled(false);
333  } else {
334  ui->minimizeToTray->setEnabled(true);
335  }
336 }
337 
339  ui->pruneWarning->setVisible(!ui->pruneWarning->isVisible());
340 }
341 
342 void OptionsDialog::showRestartWarning(bool fPersistent) {
343  ui->statusLabel->setStyleSheet("QLabel { color: red; }");
344 
345  if (fPersistent) {
346  ui->statusLabel->setText(
347  tr("Client restart required to activate changes."));
348  } else {
349  ui->statusLabel->setText(
350  tr("This change would require a client restart."));
351  // clear non-persistent status label after 10 seconds
352  // TODO: should perhaps be a class attribute, if we extend the use of
353  // statusLabel
354  QTimer::singleShot(10000, this, &OptionsDialog::clearStatusLabel);
355  }
356 }
357 
359  ui->statusLabel->clear();
360  if (model && model->isRestartRequired()) {
361  showRestartWarning(true);
362  }
363 }
364 
366  QValidatedLineEdit *pUiProxyIp = ui->proxyIp;
367  QValidatedLineEdit *otherProxyWidget =
368  (pUiProxyIp == ui->proxyIpTor) ? ui->proxyIp : ui->proxyIpTor;
369  if (pUiProxyIp->isValid() &&
370  (!ui->proxyPort->isEnabled() || ui->proxyPort->text().toInt() > 0) &&
371  (!ui->proxyPortTor->isEnabled() ||
372  ui->proxyPortTor->text().toInt() > 0)) {
373  // Only enable ok button if both proxys are valid
374  setOkButtonState(otherProxyWidget->isValid());
376  } else {
377  setOkButtonState(false);
378  ui->statusLabel->setStyleSheet("QLabel { color: red; }");
379  ui->statusLabel->setText(tr("The supplied proxy address is invalid."));
380  }
381 }
382 
384  proxyType proxy;
385  std::string strProxy;
386  QString strDefaultProxyGUI;
387 
388  model->node().getProxy(NET_IPV4, proxy);
389  strProxy = proxy.proxy.ToStringIP() + ":" + proxy.proxy.ToStringPort();
390  strDefaultProxyGUI = ui->proxyIp->text() + ":" + ui->proxyPort->text();
391  (strProxy == strDefaultProxyGUI.toStdString())
392  ? ui->proxyReachIPv4->setChecked(true)
393  : ui->proxyReachIPv4->setChecked(false);
394 
395  model->node().getProxy(NET_IPV6, proxy);
396  strProxy = proxy.proxy.ToStringIP() + ":" + proxy.proxy.ToStringPort();
397  strDefaultProxyGUI = ui->proxyIp->text() + ":" + ui->proxyPort->text();
398  (strProxy == strDefaultProxyGUI.toStdString())
399  ? ui->proxyReachIPv6->setChecked(true)
400  : ui->proxyReachIPv6->setChecked(false);
401 
402  model->node().getProxy(NET_ONION, proxy);
403  strProxy = proxy.proxy.ToStringIP() + ":" + proxy.proxy.ToStringPort();
404  strDefaultProxyGUI = ui->proxyIp->text() + ":" + ui->proxyPort->text();
405  (strProxy == strDefaultProxyGUI.toStdString())
406  ? ui->proxyReachTor->setChecked(true)
407  : ui->proxyReachTor->setChecked(false);
408 }
409 
411  : QValidator(parent) {}
412 
413 QValidator::State ProxyAddressValidator::validate(QString &input,
414  int &pos) const {
415  Q_UNUSED(pos);
416  // Validate the proxy
417  CService serv(LookupNumeric(input.toStdString(), DEFAULT_GUI_PROXY_PORT));
418  proxyType addrProxy = proxyType(serv, true);
419  if (addrProxy.IsValid()) {
420  return QValidator::Acceptable;
421  }
422 
423  return QValidator::Invalid;
424 }
Ui::OptionsDialog * ui
Definition: optionsdialog.h:70
std::string ToStringPort() const
Definition: netaddress.cpp:768
OptionsDialog(QWidget *parent, bool enableWallet)
Bitcoin unit definitions.
Definition: bitcoinunits.h:32
Proxy address widget validator, checks for a valid proxy address.
Definition: optionsdialog.h:23
CService LookupNumeric(const std::string &name, int portDefault)
Resolve a service string with a numeric IP to its first corresponding service.
Definition: netbase.cpp:270
int GetNumCores()
Return the number of cores available on the current system.
Definition: system.cpp:1338
State validate(QString &input, int &pos) const override
virtual bool getProxy(Network net, proxyType &proxy_info)=0
Get proxy.
static constexpr int64_t MIN_DB_CACHE_MB
min. -dbcache (MiB)
Definition: txdb.h:31
void valueChanged()
void setOkButtonState(bool fState)
const QString & getOverriddenByCommandLine()
Definition: optionsmodel.h:100
static constexpr uint64_t GB_BYTES
Definition: guiconstants.h:52
void on_resetButton_clicked()
Line edit that can be marked as "invalid" to show input validation feedback.
void togglePruneWarning(bool enabled)
bool isRestartRequired() const
interfaces::Node & node() const
Definition: optionsmodel.h:112
OptionsModel * model
Definition: optionsdialog.h:71
static constexpr int64_t MAX_DB_CACHE_MB
max. -dbcache (MiB)
Definition: txdb.h:33
void on_openBitcoinConfButton_clicked()
std::string ToStringIP() const
Definition: netaddress.cpp:332
A combination of a network address (CNetAddr) and a (TCP) port.
Definition: netaddress.h:179
void validationDidChange(QValidatedLineEdit *validatedLineEdit)
void setModel(OptionsModel *model)
QDataWidgetMapper * mapper
Definition: optionsdialog.h:72
void on_okButton_clicked()
void updateDefaultProxyNets()
void setCurrentTab(OptionsDialog::Tab tab)
void on_hideTrayIcon_stateChanged(int fState)
bool openBitcoinConf()
Definition: guiutil.cpp:396
CService proxy
Definition: netbase.h:37
bool IsValid() const
Definition: netbase.h:35
Interface from Qt to configuration data structure for Bitcoin client.
Definition: optionsmodel.h:48
void showRestartWarning(bool fPersistent=false)
void on_cancelButton_clicked()
Preferences dialog.
Definition: optionsdialog.h:33
void clearStatusLabel()
void updateProxyValidationState()
static const uint64_t MIN_DISK_SPACE_FOR_BLOCK_FILES
Require that user allocate at least 550 MiB for block & undo files (blk???.dat and rev...
Definition: validation.h:240
ProxyAddressValidator(QObject *parent)
static constexpr unsigned short DEFAULT_GUI_PROXY_PORT
Definition: optionsmodel.h:24
static const int MAX_SCRIPTCHECK_THREADS
Maximum number of dedicated script-checking threads allowed.
Definition: validation.h:80