Bitcoin ABC 0.33.3
P2P Digital Currency
optionsmodel.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/optionsmodel.h>
10
11#include <clientversion.h>
12#include <common/args.h>
13#include <interfaces/node.h>
14#include <mapport.h>
15#include <net.h>
16#include <netbase.h>
17#include <node/caches.h>
18#include <qt/bitcoinunits.h>
19#include <qt/guiconstants.h>
20#include <qt/guiutil.h>
21#include <util/string.h>
22#include <validation.h> // For DEFAULT_SCRIPTCHECK_THREADS
23
24#include <QDebug> // needed by qInfo()
25#ifdef ENABLE_BIP70
26#include <QNetworkProxy>
27#endif
28#include <QSettings>
29#include <QStringList>
30
31using util::ToString;
32
33const char *DEFAULT_GUI_PROXY_HOST = "127.0.0.1";
34
35static QString GetDefaultProxyAddress();
36
37OptionsModel::OptionsModel(QObject *parent, bool resetSettings)
38 : QAbstractListModel(parent) {
39 Init(resetSettings);
40}
41
42void OptionsModel::addOverriddenOption(const std::string &option) {
44 QString::fromStdString(option) + "=" +
45 QString::fromStdString(gArgs.GetArg(option, "")) + " ";
46}
47
48// Writes all missing QSettings with their default values
49void OptionsModel::Init(bool resetSettings) {
50 if (resetSettings) {
51 Reset();
52 }
53
55
56 QSettings settings;
57
58 // Ensure restart flag is unset on client startup
59 setRestartRequired(false);
60
61 // These are Qt-only settings:
62
63 // Window
64 if (!settings.contains("fHideTrayIcon")) {
65 settings.setValue("fHideTrayIcon", false);
66 }
67 fHideTrayIcon = settings.value("fHideTrayIcon").toBool();
69
70 if (!settings.contains("fMinimizeToTray")) {
71 settings.setValue("fMinimizeToTray", false);
72 }
74 settings.value("fMinimizeToTray").toBool() && !fHideTrayIcon;
75
76 if (!settings.contains("fMinimizeOnClose")) {
77 settings.setValue("fMinimizeOnClose", false);
78 }
79 fMinimizeOnClose = settings.value("fMinimizeOnClose").toBool();
80
81 // Display
82 if (!settings.contains("nDisplayUnit")) {
83 settings.setValue("nDisplayUnit", BitcoinUnits::base);
84 }
85 nDisplayUnit = settings.value("nDisplayUnit").toInt();
86
87 if (!settings.contains("strThirdPartyTxUrls")) {
88 settings.setValue("strThirdPartyTxUrls", "");
89 }
90 strThirdPartyTxUrls = settings.value("strThirdPartyTxUrls", "").toString();
91
92 if (!settings.contains("fCoinControlFeatures")) {
93 settings.setValue("fCoinControlFeatures", false);
94 }
96 settings.value("fCoinControlFeatures", false).toBool();
97
98 // These are shared with the core or have a command-line parameter
99 // and we want command-line parameters to overwrite the GUI settings.
100 //
101 // If setting doesn't exist create it with defaults.
102 //
103 // If gArgs.SoftSetArg() or gArgs.SoftSetBoolArg() return false we were
104 // overridden
105 // by command-line and show this in the UI.
106
107 // Main
108 if (!settings.contains("bPrune")) {
109 settings.setValue("bPrune", false);
110 }
111 if (!settings.contains("nPruneSize")) {
112 settings.setValue("nPruneSize", DEFAULT_PRUNE_TARGET_GB);
113 }
114 SetPruneEnabled(settings.value("bPrune").toBool());
115
116 if (!settings.contains("nDatabaseCache")) {
117 settings.setValue("nDatabaseCache", (qint64)DEFAULT_DB_CACHE >> 20);
118 }
119 if (!gArgs.SoftSetArg(
120 "-dbcache",
121 settings.value("nDatabaseCache").toString().toStdString())) {
122 addOverriddenOption("-dbcache");
123 }
124
125 if (!settings.contains("nThreadsScriptVerif")) {
126 settings.setValue("nThreadsScriptVerif", DEFAULT_SCRIPTCHECK_THREADS);
127 }
128 if (!gArgs.SoftSetArg(
129 "-par",
130 settings.value("nThreadsScriptVerif").toString().toStdString())) {
131 addOverriddenOption("-par");
132 }
133
134 if (!settings.contains("strDataDir")) {
135 settings.setValue("strDataDir", GUIUtil::getDefaultDataDirectory());
136 }
137
138// Wallet
139#ifdef ENABLE_WALLET
140 if (!settings.contains("bSpendZeroConfChange")) {
141 settings.setValue("bSpendZeroConfChange", true);
142 }
144 "-spendzeroconfchange",
145 settings.value("bSpendZeroConfChange").toBool())) {
146 addOverriddenOption("-spendzeroconfchange");
147 }
148#endif
149
150 // Network
151 if (!settings.contains("fUseNatpmp")) {
152 settings.setValue("fUseNatpmp", DEFAULT_NATPMP);
153 }
154 if (!gArgs.SoftSetBoolArg("-natpmp",
155 settings.value("fUseNatpmp").toBool())) {
156 addOverriddenOption("-natpmp");
157 }
158
159 if (!settings.contains("fListen")) {
160 settings.setValue("fListen", DEFAULT_LISTEN);
161 }
162 if (!gArgs.SoftSetBoolArg("-listen", settings.value("fListen").toBool())) {
163 addOverriddenOption("-listen");
164 }
165
166 if (!settings.contains("fUseProxy")) {
167 settings.setValue("fUseProxy", false);
168 }
169 if (!settings.contains("addrProxy")) {
170 settings.setValue("addrProxy", GetDefaultProxyAddress());
171 }
172 // Only try to set -proxy, if user has enabled fUseProxy
173 if (settings.value("fUseProxy").toBool() &&
175 "-proxy", settings.value("addrProxy").toString().toStdString())) {
176 addOverriddenOption("-proxy");
177 } else if (!settings.value("fUseProxy").toBool() &&
178 !gArgs.GetArg("-proxy", "").empty()) {
179 addOverriddenOption("-proxy");
180 }
181
182 if (!settings.contains("fUseSeparateProxyTor")) {
183 settings.setValue("fUseSeparateProxyTor", false);
184 }
185 if (!settings.contains("addrSeparateProxyTor")) {
186 settings.setValue("addrSeparateProxyTor", GetDefaultProxyAddress());
187 }
188 // Only try to set -onion, if user has enabled fUseSeparateProxyTor
189 if (settings.value("fUseSeparateProxyTor").toBool() &&
191 "-onion",
192 settings.value("addrSeparateProxyTor").toString().toStdString())) {
193 addOverriddenOption("-onion");
194 } else if (!settings.value("fUseSeparateProxyTor").toBool() &&
195 !gArgs.GetArg("-onion", "").empty()) {
196 addOverriddenOption("-onion");
197 }
198
199 // Display
200 if (!settings.contains("language")) {
201 settings.setValue("language", "");
202 }
203 if (!gArgs.SoftSetArg(
204 "-lang", settings.value("language").toString().toStdString())) {
205 addOverriddenOption("-lang");
206 }
207
208 language = settings.value("language").toString();
209}
210
215static void CopySettings(QSettings &dst, const QSettings &src) {
216 for (const QString &key : src.allKeys()) {
217 dst.setValue(key, src.value(key));
218 }
219}
220
222static void BackupSettings(const fs::path &filename, const QSettings &src) {
223 qInfo() << "Backing up GUI settings to"
224 << GUIUtil::boostPathToQString(filename);
225 QSettings dst(GUIUtil::boostPathToQString(filename), QSettings::IniFormat);
226 dst.clear();
227 CopySettings(dst, src);
228}
229
231 QSettings settings;
232
233 // Backup old settings to chain-specific datadir for troubleshooting
234 BackupSettings(gArgs.GetDataDirNet() / "guisettings.ini.bak", settings);
235
236 // Save the strDataDir setting
237 QString dataDir = GUIUtil::getDefaultDataDirectory();
238 dataDir = settings.value("strDataDir", dataDir).toString();
239
240 // Remove all entries from our QSettings object
241 settings.clear();
242
243 // Set strDataDir
244 settings.setValue("strDataDir", dataDir);
245
246 // Set that this was reset
247 settings.setValue("fReset", true);
248
249 // default setting for OptionsModel::StartAtStartup - disabled
252 }
253}
254
255int OptionsModel::rowCount(const QModelIndex &parent) const {
256 return OptionIDRowCount;
257}
258
260 bool is_set;
261 QString ip;
262 QString port;
263};
264
265static ProxySetting GetProxySetting(QSettings &settings, const QString &name) {
266 static const ProxySetting default_val = {
268 QString("%1").arg(DEFAULT_GUI_PROXY_PORT)};
269 // Handle the case that the setting is not set at all
270 if (!settings.contains(name)) {
271 return default_val;
272 }
273 // contains IP at index 0 and port at index 1
274 QStringList ip_port =
275 GUIUtil::splitSkipEmptyParts(settings.value(name).toString(), ":");
276 if (ip_port.size() == 2) {
277 return {true, ip_port.at(0), ip_port.at(1)};
278 } else { // Invalid: return default
279 return default_val;
280 }
281}
282
283static void SetProxySetting(QSettings &settings, const QString &name,
284 const ProxySetting &ip_port) {
285 settings.setValue(name, ip_port.ip + ":" + ip_port.port);
286}
287
288static QString GetDefaultProxyAddress() {
289 return QString("%1:%2")
292}
293
294void OptionsModel::SetPruneEnabled(bool prune, bool force) {
295 QSettings settings;
296 settings.setValue("bPrune", prune);
297 const int64_t prune_target_mib =
298 PruneGBtoMiB(settings.value("nPruneSize").toInt());
299 std::string prune_val = prune ? ToString(prune_target_mib) : "0";
300 if (force) {
301 gArgs.ForceSetArg("-prune", prune_val);
302 return;
303 }
304 if (!gArgs.SoftSetArg("-prune", prune_val)) {
305 addOverriddenOption("-prune");
306 }
307}
308
309void OptionsModel::SetPruneTargetGB(int prune_target_gb, bool force) {
310 const bool prune = prune_target_gb > 0;
311 if (prune) {
312 QSettings settings;
313 settings.setValue("nPruneSize", prune_target_gb);
314 }
315 SetPruneEnabled(prune, force);
316}
317
318// read QSettings values and return them
319QVariant OptionsModel::data(const QModelIndex &index, int role) const {
320 if (role == Qt::EditRole) {
321 QSettings settings;
322 switch (index.row()) {
323 case StartAtStartup:
325 case HideTrayIcon:
326 return fHideTrayIcon;
327 case MinimizeToTray:
328 return fMinimizeToTray;
329 case MapPortNatpmp:
330 return settings.value("fUseNatpmp");
331 case MinimizeOnClose:
332 return fMinimizeOnClose;
333
334 // default proxy
335 case ProxyUse:
336 return settings.value("fUseProxy", false);
337 case ProxyIP:
338 return GetProxySetting(settings, "addrProxy").ip;
339 case ProxyPort:
340 return GetProxySetting(settings, "addrProxy").port;
341
342 // separate Tor proxy
343 case ProxyUseTor:
344 return settings.value("fUseSeparateProxyTor", false);
345 case ProxyIPTor:
346 return GetProxySetting(settings, "addrSeparateProxyTor").ip;
347 case ProxyPortTor:
348 return GetProxySetting(settings, "addrSeparateProxyTor").port;
349
350#ifdef ENABLE_WALLET
352 return settings.value("bSpendZeroConfChange");
353#endif
354 case DisplayUnit:
355 return nDisplayUnit;
356 case ThirdPartyTxUrls:
357 return strThirdPartyTxUrls;
358 case Language:
359 return settings.value("language");
362 case Prune:
363 return settings.value("bPrune");
364 case PruneSize:
365 return settings.value("nPruneSize");
366 case DatabaseCache:
367 return settings.value("nDatabaseCache");
369 return settings.value("nThreadsScriptVerif");
370 case Listen:
371 return settings.value("fListen");
372 default:
373 return QVariant();
374 }
375 }
376 return QVariant();
377}
378
379// write QSettings values
380bool OptionsModel::setData(const QModelIndex &index, const QVariant &value,
381 int role) {
382 bool successful = true; /* set to false on parse error */
383 if (role == Qt::EditRole) {
384 QSettings settings;
385 switch (index.row()) {
386 case StartAtStartup:
387 successful = GUIUtil::SetStartOnSystemStartup(value.toBool());
388 break;
389 case HideTrayIcon:
390 fHideTrayIcon = value.toBool();
391 settings.setValue("fHideTrayIcon", fHideTrayIcon);
393 break;
394 case MinimizeToTray:
395 fMinimizeToTray = value.toBool();
396 settings.setValue("fMinimizeToTray", fMinimizeToTray);
397 break;
398 case MapPortNatpmp: // core option - can be changed on-the-fly
399 settings.setValue("fUseNatpmp", value.toBool());
400 break;
401 case MinimizeOnClose:
402 fMinimizeOnClose = value.toBool();
403 settings.setValue("fMinimizeOnClose", fMinimizeOnClose);
404 break;
405
406 // default proxy
407 case ProxyUse:
408 if (settings.value("fUseProxy") != value) {
409 settings.setValue("fUseProxy", value.toBool());
410 setRestartRequired(true);
411 }
412 break;
413 case ProxyIP: {
414 auto ip_port = GetProxySetting(settings, "addrProxy");
415 if (!ip_port.is_set || ip_port.ip != value.toString()) {
416 ip_port.ip = value.toString();
417 SetProxySetting(settings, "addrProxy", ip_port);
418 setRestartRequired(true);
419 }
420 } break;
421 case ProxyPort: {
422 auto ip_port = GetProxySetting(settings, "addrProxy");
423 if (!ip_port.is_set || ip_port.port != value.toString()) {
424 ip_port.port = value.toString();
425 SetProxySetting(settings, "addrProxy", ip_port);
426 setRestartRequired(true);
427 }
428 } break;
429
430 // separate Tor proxy
431 case ProxyUseTor:
432 if (settings.value("fUseSeparateProxyTor") != value) {
433 settings.setValue("fUseSeparateProxyTor", value.toBool());
434 setRestartRequired(true);
435 }
436 break;
437 case ProxyIPTor: {
438 auto ip_port =
439 GetProxySetting(settings, "addrSeparateProxyTor");
440 if (!ip_port.is_set || ip_port.ip != value.toString()) {
441 ip_port.ip = value.toString();
442 SetProxySetting(settings, "addrSeparateProxyTor", ip_port);
443 setRestartRequired(true);
444 }
445 } break;
446 case ProxyPortTor: {
447 auto ip_port =
448 GetProxySetting(settings, "addrSeparateProxyTor");
449 if (!ip_port.is_set || ip_port.port != value.toString()) {
450 ip_port.port = value.toString();
451 SetProxySetting(settings, "addrSeparateProxyTor", ip_port);
452 setRestartRequired(true);
453 }
454 } break;
455
456#ifdef ENABLE_WALLET
458 if (settings.value("bSpendZeroConfChange") != value) {
459 settings.setValue("bSpendZeroConfChange", value);
460 setRestartRequired(true);
461 }
462 break;
463#endif
464 case DisplayUnit:
465 setDisplayUnit(value);
466 break;
467 case ThirdPartyTxUrls:
468 if (strThirdPartyTxUrls != value.toString()) {
469 strThirdPartyTxUrls = value.toString();
470 settings.setValue("strThirdPartyTxUrls",
472 setRestartRequired(true);
473 }
474 break;
475 case Language:
476 if (settings.value("language") != value) {
477 settings.setValue("language", value);
478 setRestartRequired(true);
479 }
480 break;
482 fCoinControlFeatures = value.toBool();
483 settings.setValue("fCoinControlFeatures", fCoinControlFeatures);
485 break;
486 case Prune:
487 if (settings.value("bPrune") != value) {
488 settings.setValue("bPrune", value);
489 setRestartRequired(true);
490 }
491 break;
492 case PruneSize:
493 if (settings.value("nPruneSize") != value) {
494 settings.setValue("nPruneSize", value);
495 setRestartRequired(true);
496 }
497 break;
498 case DatabaseCache:
499 if (settings.value("nDatabaseCache") != value) {
500 settings.setValue("nDatabaseCache", value);
501 setRestartRequired(true);
502 }
503 break;
505 if (settings.value("nThreadsScriptVerif") != value) {
506 settings.setValue("nThreadsScriptVerif", value);
507 setRestartRequired(true);
508 }
509 break;
510 case Listen:
511 if (settings.value("fListen") != value) {
512 settings.setValue("fListen", value);
513 setRestartRequired(true);
514 }
515 break;
516 default:
517 break;
518 }
519 }
520
521 Q_EMIT dataChanged(index, index);
522
523 return successful;
524}
525
528void OptionsModel::setDisplayUnit(const QVariant &value) {
529 if (!value.isNull()) {
530 QSettings settings;
531 nDisplayUnit = value.toInt();
532 settings.setValue("nDisplayUnit", nDisplayUnit);
534 }
535}
536
537#ifdef ENABLE_BIP70
538bool OptionsModel::getProxySettings(QNetworkProxy &proxy) const {
539 // Directly query current base proxy, because
540 // GUI settings can be overridden with -proxy.
541 Proxy curProxy;
542 if (node().getProxy(NET_IPV4, curProxy)) {
543 proxy.setType(QNetworkProxy::Socks5Proxy);
544 proxy.setHostName(
545 QString::fromStdString(curProxy.proxy.ToStringAddr()));
546 proxy.setPort(curProxy.proxy.GetPort());
547
548 return true;
549 } else {
550 proxy.setType(QNetworkProxy::NoProxy);
551 }
552
553 return false;
554}
555#endif
556
558 QSettings settings;
559 return settings.setValue("fRestartRequired", fRequired);
560}
561
563 QSettings settings;
564 return settings.value("fRestartRequired", false).toBool();
565}
566
568 // Migration of default values
569 // Check if the QSettings container was already loaded with this client
570 // version
571 QSettings settings;
572 static const char strSettingsVersionKey[] = "nSettingsVersion";
573 int settingsVersion = settings.contains(strSettingsVersionKey)
574 ? settings.value(strSettingsVersionKey).toInt()
575 : 0;
576 if (settingsVersion < CLIENT_VERSION) {
577 // -dbcache was bumped from 100 to 300 in 0.13
578 // see https://github.com/bitcoin/bitcoin/pull/8273
579 // force people to upgrade to the new value if they are using 100MB
580 if (settingsVersion < 130000 && settings.contains("nDatabaseCache") &&
581 settings.value("nDatabaseCache").toLongLong() == 100) {
582 settings.setValue("nDatabaseCache", (qint64)DEFAULT_DB_CACHE >> 20);
583 }
584
585 settings.setValue(strSettingsVersionKey, CLIENT_VERSION);
586 }
587
588 // Overwrite the 'addrProxy' setting in case it has been set to an illegal
589 // default value (see issue #12623; PR #12650).
590 if (settings.contains("addrProxy") &&
591 settings.value("addrProxy").toString().endsWith("%2")) {
592 settings.setValue("addrProxy", GetDefaultProxyAddress());
593 }
594
595 // Overwrite the 'addrSeparateProxyTor' setting in case it has been set to
596 // an illegal default value (see issue #12623; PR #12650).
597 if (settings.contains("addrSeparateProxyTor") &&
598 settings.value("addrSeparateProxyTor").toString().endsWith("%2")) {
599 settings.setValue("addrSeparateProxyTor", GetDefaultProxyAddress());
600 }
601}
ArgsManager gArgs
Definition: args.cpp:39
void ForceSetArg(const std::string &strArg, const std::string &strValue)
Definition: args.cpp:565
fs::path GetDataDirNet() const
Get data directory path with appended network identifier.
Definition: args.h:239
bool SoftSetArg(const std::string &strArg, const std::string &strValue)
Set an argument if it doesn't already have a value.
Definition: args.cpp:547
std::string GetArg(const std::string &strArg, const std::string &strDefault) const
Return string argument or default value.
Definition: args.cpp:462
bool SoftSetBoolArg(const std::string &strArg, bool fValue)
Set a boolean argument if it doesn't already have a value.
Definition: args.cpp:557
std::string ToStringAddr() const
Definition: netaddress.cpp:630
uint16_t GetPort() const
QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const override
QString strOverriddenByCommandLine
Definition: optionsmodel.h:132
bool getProxySettings(QNetworkProxy &proxy) const
bool isRestartRequired() const
void Init(bool resetSettings=false)
bool fCoinControlFeatures
Definition: optionsmodel.h:130
interfaces::Node & node() const
Definition: optionsmodel.h:112
void setDisplayUnit(const QVariant &value)
Updates current unit in memory, settings and emits displayUnitChanged(newUnit) signal.
void coinControlFeaturesChanged(bool)
QString strThirdPartyTxUrls
Definition: optionsmodel.h:129
OptionsModel(QObject *parent=nullptr, bool resetSettings=false)
int rowCount(const QModelIndex &parent=QModelIndex()) const override
void SetPruneTargetGB(int prune_target_gb, bool force=false)
void SetPruneEnabled(bool prune, bool force=false)
void displayUnitChanged(int unit)
bool fHideTrayIcon
Definition: optionsmodel.h:124
bool fMinimizeToTray
Definition: optionsmodel.h:125
bool setData(const QModelIndex &index, const QVariant &value, int role=Qt::EditRole) override
void checkAndMigrate()
QString language
Definition: optionsmodel.h:127
void addOverriddenOption(const std::string &option)
bool fMinimizeOnClose
Definition: optionsmodel.h:126
void setRestartRequired(bool fRequired)
void hideTrayIconChanged(bool)
Definition: netbase.h:67
CService proxy
Definition: netbase.h:77
Path class wrapper to block calls to the fs::path(std::string) implicit constructor and the fs::path:...
Definition: fs.h:30
static constexpr int CLIENT_VERSION
bitcoind-res.rc includes this file, but it cannot cope with real c++ code.
Definition: clientversion.h:38
static constexpr int DEFAULT_PRUNE_TARGET_GB
Definition: guiconstants.h:56
static constexpr bool DEFAULT_NATPMP
Definition: mapport.h:8
QString getDefaultDataDirectory()
Determine default data directory for operating system.
Definition: guiutil.cpp:295
QString boostPathToQString(const fs::path &path)
Convert OS specific boost path to QString through UTF-8.
Definition: guiutil.cpp:791
bool SetStartOnSystemStartup(bool fAutoStart)
Definition: guiutil.cpp:776
bool GetStartOnSystemStartup()
Definition: guiutil.cpp:773
QStringList splitSkipEmptyParts(const QString &s, const QString &separator)
Definition: guiutil.cpp:451
std::string ToString(const T &t)
Locale-independent version of std::to_string.
Definition: string.h:150
static const bool DEFAULT_LISTEN
-listen default
Definition: net.h:89
@ NET_IPV4
IPv4.
Definition: netaddress.h:43
static constexpr size_t DEFAULT_DB_CACHE
-dbcache default (bytes)
Definition: caches.h:18
static void CopySettings(QSettings &dst, const QSettings &src)
Helper function to copy contents from one QSettings to another.
static QString GetDefaultProxyAddress()
static ProxySetting GetProxySetting(QSettings &settings, const QString &name)
static void SetProxySetting(QSettings &settings, const QString &name, const ProxySetting &ip_port)
static void BackupSettings(const fs::path &filename, const QSettings &src)
Back up a QSettings to an ini-formatted file.
const char * DEFAULT_GUI_PROXY_HOST
static int64_t PruneGBtoMiB(int gb)
Convert displayed prune target GB to configured MiB.
Definition: optionsmodel.h:38
static constexpr uint16_t DEFAULT_GUI_PROXY_PORT
Definition: optionsmodel.h:24
const char * name
Definition: rest.cpp:47
static std::string ToString(const CService &ip)
Definition: db.h:36
static const int DEFAULT_SCRIPTCHECK_THREADS
-par default (number of script-checking threads, 0 = auto)
Definition: validation.h:91