1 // Copyright (c) 2011-2017 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.
5 #if defined(HAVE_CONFIG_H)
6 #include <config/bitcoin-config.h>
9 #include <qt/optionsmodel.h>
11 #include <qt/bitcoinunits.h>
12 #include <qt/guiutil.h>
15 #include <validation.h> // For DEFAULT_SCRIPTCHECK_THREADS
18 #include <txdb.h> // for -dbcache defaults
22 #include <wallet/wallet.h>
23 #include <wallet/walletdb.h>
26 #include <QNetworkProxy>
28 #include <QStringList>
30 const char *DEFAULT_GUI_PROXY_HOST
= "127.0.0.1";
32 OptionsModel::OptionsModel(QObject
*parent
, bool resetSettings
) :
33 QAbstractListModel(parent
)
38 void OptionsModel::addOverriddenOption(const std::string
&option
)
40 strOverriddenByCommandLine
+= QString::fromStdString(option
) + "=" + QString::fromStdString(gArgs
.GetArg(option
, "")) + " ";
43 // Writes all missing QSettings with their default values
44 void OptionsModel::Init(bool resetSettings
)
53 // Ensure restart flag is unset on client startup
54 setRestartRequired(false);
56 // These are Qt-only settings:
59 if (!settings
.contains("fHideTrayIcon"))
60 settings
.setValue("fHideTrayIcon", false);
61 fHideTrayIcon
= settings
.value("fHideTrayIcon").toBool();
62 Q_EMIT
hideTrayIconChanged(fHideTrayIcon
);
64 if (!settings
.contains("fMinimizeToTray"))
65 settings
.setValue("fMinimizeToTray", false);
66 fMinimizeToTray
= settings
.value("fMinimizeToTray").toBool() && !fHideTrayIcon
;
68 if (!settings
.contains("fMinimizeOnClose"))
69 settings
.setValue("fMinimizeOnClose", false);
70 fMinimizeOnClose
= settings
.value("fMinimizeOnClose").toBool();
73 if (!settings
.contains("nDisplayUnit"))
74 settings
.setValue("nDisplayUnit", BitcoinUnits::BTC
);
75 nDisplayUnit
= settings
.value("nDisplayUnit").toInt();
77 if (!settings
.contains("strThirdPartyTxUrls"))
78 settings
.setValue("strThirdPartyTxUrls", "");
79 strThirdPartyTxUrls
= settings
.value("strThirdPartyTxUrls", "").toString();
81 if (!settings
.contains("fCoinControlFeatures"))
82 settings
.setValue("fCoinControlFeatures", false);
83 fCoinControlFeatures
= settings
.value("fCoinControlFeatures", false).toBool();
85 // These are shared with the core or have a command-line parameter
86 // and we want command-line parameters to overwrite the GUI settings.
88 // If setting doesn't exist create it with defaults.
90 // If gArgs.SoftSetArg() or gArgs.SoftSetBoolArg() return false we were overridden
91 // by command-line and show this in the UI.
94 if (!settings
.contains("nDatabaseCache"))
95 settings
.setValue("nDatabaseCache", (qint64
)nDefaultDbCache
);
96 if (!gArgs
.SoftSetArg("-dbcache", settings
.value("nDatabaseCache").toString().toStdString()))
97 addOverriddenOption("-dbcache");
99 if (!settings
.contains("nThreadsScriptVerif"))
100 settings
.setValue("nThreadsScriptVerif", DEFAULT_SCRIPTCHECK_THREADS
);
101 if (!gArgs
.SoftSetArg("-par", settings
.value("nThreadsScriptVerif").toString().toStdString()))
102 addOverriddenOption("-par");
104 if (!settings
.contains("strDataDir"))
105 settings
.setValue("strDataDir", Intro::getDefaultDataDirectory());
109 if (!settings
.contains("bSpendZeroConfChange"))
110 settings
.setValue("bSpendZeroConfChange", true);
111 if (!gArgs
.SoftSetBoolArg("-spendzeroconfchange", settings
.value("bSpendZeroConfChange").toBool()))
112 addOverriddenOption("-spendzeroconfchange");
116 if (!settings
.contains("fUseUPnP"))
117 settings
.setValue("fUseUPnP", DEFAULT_UPNP
);
118 if (!gArgs
.SoftSetBoolArg("-upnp", settings
.value("fUseUPnP").toBool()))
119 addOverriddenOption("-upnp");
121 if (!settings
.contains("fListen"))
122 settings
.setValue("fListen", DEFAULT_LISTEN
);
123 if (!gArgs
.SoftSetBoolArg("-listen", settings
.value("fListen").toBool()))
124 addOverriddenOption("-listen");
126 if (!settings
.contains("fUseProxy"))
127 settings
.setValue("fUseProxy", false);
128 if (!settings
.contains("addrProxy"))
129 settings
.setValue("addrProxy", QString("%1:%2").arg(DEFAULT_GUI_PROXY_HOST
, DEFAULT_GUI_PROXY_PORT
));
130 // Only try to set -proxy, if user has enabled fUseProxy
131 if (settings
.value("fUseProxy").toBool() && !gArgs
.SoftSetArg("-proxy", settings
.value("addrProxy").toString().toStdString()))
132 addOverriddenOption("-proxy");
133 else if(!settings
.value("fUseProxy").toBool() && !gArgs
.GetArg("-proxy", "").empty())
134 addOverriddenOption("-proxy");
136 if (!settings
.contains("fUseSeparateProxyTor"))
137 settings
.setValue("fUseSeparateProxyTor", false);
138 if (!settings
.contains("addrSeparateProxyTor"))
139 settings
.setValue("addrSeparateProxyTor", QString("%1:%2").arg(DEFAULT_GUI_PROXY_HOST
, DEFAULT_GUI_PROXY_PORT
));
140 // Only try to set -onion, if user has enabled fUseSeparateProxyTor
141 if (settings
.value("fUseSeparateProxyTor").toBool() && !gArgs
.SoftSetArg("-onion", settings
.value("addrSeparateProxyTor").toString().toStdString()))
142 addOverriddenOption("-onion");
143 else if(!settings
.value("fUseSeparateProxyTor").toBool() && !gArgs
.GetArg("-onion", "").empty())
144 addOverriddenOption("-onion");
147 if (!settings
.contains("language"))
148 settings
.setValue("language", "");
149 if (!gArgs
.SoftSetArg("-lang", settings
.value("language").toString().toStdString()))
150 addOverriddenOption("-lang");
152 language
= settings
.value("language").toString();
155 /** Helper function to copy contents from one QSettings to another.
156 * By using allKeys this also covers nested settings in a hierarchy.
158 static void CopySettings(QSettings
& dst
, const QSettings
& src
)
160 for (const QString
& key
: src
.allKeys()) {
161 dst
.setValue(key
, src
.value(key
));
165 /** Back up a QSettings to an ini-formatted file. */
166 static void BackupSettings(const fs::path
& filename
, const QSettings
& src
)
168 qWarning() << "Backing up GUI settings to" << GUIUtil::boostPathToQString(filename
);
169 QSettings
dst(GUIUtil::boostPathToQString(filename
), QSettings::IniFormat
);
171 CopySettings(dst
, src
);
174 void OptionsModel::Reset()
178 // Backup old settings to chain-specific datadir for troubleshooting
179 BackupSettings(GetDataDir(true) / "guisettings.ini.bak", settings
);
181 // Save the strDataDir setting
182 QString dataDir
= Intro::getDefaultDataDirectory();
183 dataDir
= settings
.value("strDataDir", dataDir
).toString();
185 // Remove all entries from our QSettings object
189 settings
.setValue("strDataDir", dataDir
);
191 // Set that this was reset
192 settings
.setValue("fReset", true);
194 // default setting for OptionsModel::StartAtStartup - disabled
195 if (GUIUtil::GetStartOnSystemStartup())
196 GUIUtil::SetStartOnSystemStartup(false);
199 int OptionsModel::rowCount(const QModelIndex
& parent
) const
201 return OptionIDRowCount
;
204 struct ProxySetting
{
210 static ProxySetting
GetProxySetting(QSettings
&settings
, const QString
&name
)
212 static const ProxySetting default_val
= {false, DEFAULT_GUI_PROXY_HOST
, QString("%1").arg(DEFAULT_GUI_PROXY_PORT
)};
213 // Handle the case that the setting is not set at all
214 if (!settings
.contains(name
)) {
217 // contains IP at index 0 and port at index 1
218 QStringList ip_port
= settings
.value(name
).toString().split(":", QString::SkipEmptyParts
);
219 if (ip_port
.size() == 2) {
220 return {true, ip_port
.at(0), ip_port
.at(1)};
221 } else { // Invalid: return default
226 static void SetProxySetting(QSettings
&settings
, const QString
&name
, const ProxySetting
&ip_port
)
228 settings
.setValue(name
, ip_port
.ip
+ ":" + ip_port
.port
);
231 // read QSettings values and return them
232 QVariant
OptionsModel::data(const QModelIndex
& index
, int role
) const
234 if(role
== Qt::EditRole
)
240 return GUIUtil::GetStartOnSystemStartup();
242 return fHideTrayIcon
;
244 return fMinimizeToTray
;
247 return settings
.value("fUseUPnP");
251 case MinimizeOnClose
:
252 return fMinimizeOnClose
;
256 return settings
.value("fUseProxy", false);
258 return GetProxySetting(settings
, "addrProxy").ip
;
260 return GetProxySetting(settings
, "addrProxy").port
;
262 // separate Tor proxy
264 return settings
.value("fUseSeparateProxyTor", false);
266 return GetProxySetting(settings
, "addrSeparateProxyTor").ip
;
268 return GetProxySetting(settings
, "addrSeparateProxyTor").port
;
271 case SpendZeroConfChange
:
272 return settings
.value("bSpendZeroConfChange");
276 case ThirdPartyTxUrls
:
277 return strThirdPartyTxUrls
;
279 return settings
.value("language");
280 case CoinControlFeatures
:
281 return fCoinControlFeatures
;
283 return settings
.value("nDatabaseCache");
284 case ThreadsScriptVerif
:
285 return settings
.value("nThreadsScriptVerif");
287 return settings
.value("fListen");
295 // write QSettings values
296 bool OptionsModel::setData(const QModelIndex
& index
, const QVariant
& value
, int role
)
298 bool successful
= true; /* set to false on parse error */
299 if(role
== Qt::EditRole
)
305 successful
= GUIUtil::SetStartOnSystemStartup(value
.toBool());
308 fHideTrayIcon
= value
.toBool();
309 settings
.setValue("fHideTrayIcon", fHideTrayIcon
);
310 Q_EMIT
hideTrayIconChanged(fHideTrayIcon
);
313 fMinimizeToTray
= value
.toBool();
314 settings
.setValue("fMinimizeToTray", fMinimizeToTray
);
316 case MapPortUPnP
: // core option - can be changed on-the-fly
317 settings
.setValue("fUseUPnP", value
.toBool());
318 MapPort(value
.toBool());
320 case MinimizeOnClose
:
321 fMinimizeOnClose
= value
.toBool();
322 settings
.setValue("fMinimizeOnClose", fMinimizeOnClose
);
327 if (settings
.value("fUseProxy") != value
) {
328 settings
.setValue("fUseProxy", value
.toBool());
329 setRestartRequired(true);
333 auto ip_port
= GetProxySetting(settings
, "addrProxy");
334 if (!ip_port
.is_set
|| ip_port
.ip
!= value
.toString()) {
335 ip_port
.ip
= value
.toString();
336 SetProxySetting(settings
, "addrProxy", ip_port
);
337 setRestartRequired(true);
342 auto ip_port
= GetProxySetting(settings
, "addrProxy");
343 if (!ip_port
.is_set
|| ip_port
.port
!= value
.toString()) {
344 ip_port
.port
= value
.toString();
345 SetProxySetting(settings
, "addrProxy", ip_port
);
346 setRestartRequired(true);
351 // separate Tor proxy
353 if (settings
.value("fUseSeparateProxyTor") != value
) {
354 settings
.setValue("fUseSeparateProxyTor", value
.toBool());
355 setRestartRequired(true);
359 auto ip_port
= GetProxySetting(settings
, "addrSeparateProxyTor");
360 if (!ip_port
.is_set
|| ip_port
.ip
!= value
.toString()) {
361 ip_port
.ip
= value
.toString();
362 SetProxySetting(settings
, "addrSeparateProxyTor", ip_port
);
363 setRestartRequired(true);
368 auto ip_port
= GetProxySetting(settings
, "addrSeparateProxyTor");
369 if (!ip_port
.is_set
|| ip_port
.port
!= value
.toString()) {
370 ip_port
.port
= value
.toString();
371 SetProxySetting(settings
, "addrSeparateProxyTor", ip_port
);
372 setRestartRequired(true);
378 case SpendZeroConfChange
:
379 if (settings
.value("bSpendZeroConfChange") != value
) {
380 settings
.setValue("bSpendZeroConfChange", value
);
381 setRestartRequired(true);
386 setDisplayUnit(value
);
388 case ThirdPartyTxUrls
:
389 if (strThirdPartyTxUrls
!= value
.toString()) {
390 strThirdPartyTxUrls
= value
.toString();
391 settings
.setValue("strThirdPartyTxUrls", strThirdPartyTxUrls
);
392 setRestartRequired(true);
396 if (settings
.value("language") != value
) {
397 settings
.setValue("language", value
);
398 setRestartRequired(true);
401 case CoinControlFeatures
:
402 fCoinControlFeatures
= value
.toBool();
403 settings
.setValue("fCoinControlFeatures", fCoinControlFeatures
);
404 Q_EMIT
coinControlFeaturesChanged(fCoinControlFeatures
);
407 if (settings
.value("nDatabaseCache") != value
) {
408 settings
.setValue("nDatabaseCache", value
);
409 setRestartRequired(true);
412 case ThreadsScriptVerif
:
413 if (settings
.value("nThreadsScriptVerif") != value
) {
414 settings
.setValue("nThreadsScriptVerif", value
);
415 setRestartRequired(true);
419 if (settings
.value("fListen") != value
) {
420 settings
.setValue("fListen", value
);
421 setRestartRequired(true);
429 Q_EMIT
dataChanged(index
, index
);
434 /** Updates current unit in memory, settings and emits displayUnitChanged(newUnit) signal */
435 void OptionsModel::setDisplayUnit(const QVariant
&value
)
440 nDisplayUnit
= value
.toInt();
441 settings
.setValue("nDisplayUnit", nDisplayUnit
);
442 Q_EMIT
displayUnitChanged(nDisplayUnit
);
446 bool OptionsModel::getProxySettings(QNetworkProxy
& proxy
) const
448 // Directly query current base proxy, because
449 // GUI settings can be overridden with -proxy.
451 if (GetProxy(NET_IPV4
, curProxy
)) {
452 proxy
.setType(QNetworkProxy::Socks5Proxy
);
453 proxy
.setHostName(QString::fromStdString(curProxy
.proxy
.ToStringIP()));
454 proxy
.setPort(curProxy
.proxy
.GetPort());
459 proxy
.setType(QNetworkProxy::NoProxy
);
464 void OptionsModel::setRestartRequired(bool fRequired
)
467 return settings
.setValue("fRestartRequired", fRequired
);
470 bool OptionsModel::isRestartRequired() const
473 return settings
.value("fRestartRequired", false).toBool();
476 void OptionsModel::checkAndMigrate()
478 // Migration of default values
479 // Check if the QSettings container was already loaded with this client version
481 static const char strSettingsVersionKey
[] = "nSettingsVersion";
482 int settingsVersion
= settings
.contains(strSettingsVersionKey
) ? settings
.value(strSettingsVersionKey
).toInt() : 0;
483 if (settingsVersion
< CLIENT_VERSION
)
485 // -dbcache was bumped from 100 to 300 in 0.13
486 // see https://github.com/bitcoin/bitcoin/pull/8273
487 // force people to upgrade to the new value if they are using 100MB
488 if (settingsVersion
< 130000 && settings
.contains("nDatabaseCache") && settings
.value("nDatabaseCache").toLongLong() == 100)
489 settings
.setValue("nDatabaseCache", (qint64
)nDefaultDbCache
);
491 settings
.setValue(strSettingsVersionKey
, CLIENT_VERSION
);