Bitcoin ABC 0.32.4
P2P Digital Currency
intro.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 <common/args.h>
10#include <config.h>
11#include <interfaces/node.h>
12#include <qt/forms/ui_intro.h>
13#include <qt/guiconstants.h>
14#include <qt/guiutil.h>
15#include <qt/intro.h>
16#include <qt/optionsmodel.h>
17#include <util/chaintype.h>
18#include <util/fs.h>
19#include <util/fs_helpers.h>
20
21#include <QFileDialog>
22#include <QMessageBox>
23#include <QSettings>
24
25#include <cmath>
26
27/* Check free space asynchronously to prevent hanging the UI thread.
28
29 Up to one request to check a path is in flight to this thread; when the
30 check()
31 function runs, the current path is requested from the associated Intro
32 object.
33 The reply is sent back through a signal.
34
35 This ensures that no queue of checking requests is built up while the user is
36 still entering the path, and that always the most recently entered path is
37 checked as
38 soon as the thread becomes available.
39*/
40class FreespaceChecker : public QObject {
41 Q_OBJECT
42
43public:
44 explicit FreespaceChecker(Intro *intro);
45
47
48public Q_SLOTS:
49 void check();
50
51Q_SIGNALS:
52 void reply(int status, const QString &message, quint64 available);
53
54private:
56};
57
58#include <qt/intro.moc>
59
61 this->intro = _intro;
62}
63
65 QString dataDirStr = intro->getPathToCheck();
66 fs::path dataDir = GUIUtil::qstringToBoostPath(dataDirStr);
67 uint64_t freeBytesAvailable = 0;
68 int replyStatus = ST_OK;
69 QString replyMessage = tr("A new data directory will be created.");
70
71 /* Find first parent that exists, so that fs::space does not fail */
72 fs::path parentDir = dataDir;
73 fs::path parentDirOld = fs::path();
74 while (parentDir.has_parent_path() && !fs::exists(parentDir)) {
75 parentDir = parentDir.parent_path();
76
77 /* Check if we make any progress, break if not to prevent an infinite
78 * loop here */
79 if (parentDirOld == parentDir) {
80 break;
81 }
82
83 parentDirOld = parentDir;
84 }
85
86 try {
87 freeBytesAvailable = fs::space(parentDir).available;
88 if (fs::exists(dataDir)) {
89 if (fs::is_directory(dataDir)) {
90 QString separator = "<code>" + QDir::toNativeSeparators("/") +
91 tr("name") + "</code>";
92 replyStatus = ST_OK;
93 replyMessage = tr("Directory already exists. Add %1 if you "
94 "intend to create a new directory here.")
95 .arg(separator);
96 } else {
97 replyStatus = ST_ERROR;
98 replyMessage =
99 tr("Path already exists, and is not a directory.");
100 }
101 }
102 } catch (const fs::filesystem_error &) {
103 /* Parent directory does not exist or is not accessible */
104 replyStatus = ST_ERROR;
105 replyMessage = tr("Cannot create data directory here.");
106 }
107 Q_EMIT reply(replyStatus, replyMessage, freeBytesAvailable);
108}
109
110namespace {
112int GetPruneTargetGB() {
113 int64_t prune_target_mib = gArgs.GetIntArg("-prune", 0);
114 // >1 means automatic pruning is enabled by config, 1 means manual pruning,
115 // 0 means no pruning.
116 return prune_target_mib > 1 ? PruneMiBtoGB(prune_target_mib)
118}
119} // namespace
120
121Intro::Intro(QWidget *parent, int64_t blockchain_size_gb,
122 int64_t chain_state_size_gb)
123 : QDialog(parent), ui(new Ui::Intro), thread(nullptr), signalled(false),
124 m_blockchain_size_gb(blockchain_size_gb),
125 m_chain_state_size_gb(chain_state_size_gb),
126 m_prune_target_gb(GetPruneTargetGB()) {
127 ui->setupUi(this);
128 ui->welcomeLabel->setText(ui->welcomeLabel->text().arg(PACKAGE_NAME));
129 ui->storageLabel->setText(ui->storageLabel->text().arg(PACKAGE_NAME));
130
131 ui->lblExplanation1->setText(ui->lblExplanation1->text()
132 .arg(PACKAGE_NAME)
134 .arg(2009)
135 .arg(tr("Bitcoin")));
136 ui->lblExplanation2->setText(ui->lblExplanation2->text().arg(PACKAGE_NAME));
137
138 // -prune=1 means enabled, above that it's a size in MiB
139 if (gArgs.GetIntArg("-prune", 0) > 1) {
140 ui->prune->setChecked(true);
141 ui->prune->setEnabled(false);
142 }
143 ui->prune->setText(tr("Discard blocks after verification, except most "
144 "recent %1 GB (prune)")
145 .arg(m_prune_target_gb));
146 UpdatePruneLabels(ui->prune->isChecked());
147
148 connect(ui->prune, &QCheckBox::toggled, [this](bool prune_checked) {
149 UpdatePruneLabels(prune_checked);
150 UpdateFreeSpaceLabel();
151 });
152
153 startThread();
154}
155
157 delete ui;
158 /* Ensure thread is finished before it is deleted */
159 thread->quit();
160 thread->wait();
161}
162
164 return ui->dataDirectory->text();
165}
166
167void Intro::setDataDirectory(const QString &dataDir) {
168 ui->dataDirectory->setText(dataDir);
169 if (dataDir == GUIUtil::getDefaultDataDirectory()) {
170 ui->dataDirDefault->setChecked(true);
171 ui->dataDirectory->setEnabled(false);
172 ui->ellipsisButton->setEnabled(false);
173 } else {
174 ui->dataDirCustom->setChecked(true);
175 ui->dataDirectory->setEnabled(true);
176 ui->ellipsisButton->setEnabled(true);
177 }
178}
179
180bool Intro::showIfNeeded(bool &did_show_intro, bool &prune) {
181 did_show_intro = false;
182
183 QSettings settings;
184 /* If data directory provided on command line, no need to look at settings
185 or show a picking dialog */
186 if (!gArgs.GetArg("-datadir", "").empty()) {
187 return true;
188 }
189 /* 1) Default data directory for operating system */
190 QString dataDir = GUIUtil::getDefaultDataDirectory();
191 /* 2) Allow QSettings to override default dir */
192 dataDir = settings.value("strDataDir", dataDir).toString();
193
195 gArgs.GetBoolArg("-choosedatadir", DEFAULT_CHOOSE_DATADIR) ||
196 settings.value("fReset", false).toBool() ||
197 gArgs.GetBoolArg("-resetguisettings", false)) {
202 try {
204 } catch (const std::exception &) {
205 return false;
206 }
207
212 const CChainParams &params = GetConfig().GetChainParams();
213 Intro intro(nullptr, params.AssumedBlockchainSize(),
214 params.AssumedChainStateSize());
215 intro.setDataDirectory(dataDir);
216 intro.setWindowIcon(QIcon(":icons/bitcoin"));
217 did_show_intro = true;
218
219 while (true) {
220 if (!intro.exec()) {
221 /* Cancel clicked */
222 return false;
223 }
224 dataDir = intro.getDataDirectory();
225 try {
227 GUIUtil::qstringToBoostPath(dataDir))) {
228 // If a new data directory has been created, make wallets
229 // subdirectory too
231 "wallets");
232 }
233 break;
234 } catch (const fs::filesystem_error &) {
235 QMessageBox::critical(nullptr, PACKAGE_NAME,
236 tr("Error: Specified data directory "
237 "\"%1\" cannot be created.")
238 .arg(dataDir));
239 /* fall through, back to choosing screen */
240 }
241 }
242
243 // Additional preferences:
244 prune = intro.ui->prune->isChecked();
245
246 settings.setValue("strDataDir", dataDir);
247 settings.setValue("fReset", false);
248 }
249 /* Only override -datadir if different from the default, to make it possible
250 * to
251 * override -datadir in the bitcoin.conf file in the default data directory
252 * (to be consistent with bitcoind behavior)
253 */
254 if (dataDir != GUIUtil::getDefaultDataDirectory()) {
255 // use OS locale for path setting
257 "-datadir", fs::PathToString(GUIUtil::qstringToBoostPath(dataDir)));
258 }
259 return true;
260}
261
262void Intro::setStatus(int status, const QString &message,
263 quint64 bytesAvailable) {
264 switch (status) {
266 ui->errorMessage->setText(message);
267 ui->errorMessage->setStyleSheet("");
268 break;
270 ui->errorMessage->setText(tr("Error") + ": " + message);
271 ui->errorMessage->setStyleSheet("QLabel { color: #800000 }");
272 break;
273 }
274 /* Indicate number of bytes available */
275 if (status == FreespaceChecker::ST_ERROR) {
276 ui->freeSpace->setText("");
277 } else {
278 m_bytes_available = bytesAvailable;
279 if (ui->prune->isEnabled()) {
280 ui->prune->setChecked(
283 }
285 }
286 /* Don't allow confirm in ERROR state */
287 ui->buttonBox->button(QDialogButtonBox::Ok)
288 ->setEnabled(status != FreespaceChecker::ST_ERROR);
289}
290
292 QString freeString =
293 tr("%n GB of free space available", "", m_bytes_available / GB_BYTES);
295 freeString += " " + tr("(of %n GB needed)", "", m_required_space_gb);
296 ui->freeSpace->setStyleSheet("QLabel { color: #800000 }");
297 } else if (m_bytes_available / GB_BYTES - m_required_space_gb < 10) {
298 freeString +=
299 " " + tr("(%n GB needed for full chain)", "", m_required_space_gb);
300 ui->freeSpace->setStyleSheet("QLabel { color: #999900 }");
301 } else {
302 ui->freeSpace->setStyleSheet("");
303 }
304 ui->freeSpace->setText(freeString + ".");
305}
306
307void Intro::on_dataDirectory_textChanged(const QString &dataDirStr) {
308 /* Disable OK button until check result comes in */
309 ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
310 checkPath(dataDirStr);
311}
312
314 QString dir = QDir::toNativeSeparators(QFileDialog::getExistingDirectory(
315 nullptr, "Choose data directory", ui->dataDirectory->text()));
316 if (!dir.isEmpty()) {
317 ui->dataDirectory->setText(dir);
318 }
319}
320
323}
324
326 ui->dataDirectory->setEnabled(true);
327 ui->ellipsisButton->setEnabled(true);
328}
329
331 thread = new QThread(this);
332 FreespaceChecker *executor = new FreespaceChecker(this);
333 executor->moveToThread(thread);
334
335 connect(executor, &FreespaceChecker::reply, this, &Intro::setStatus);
336 connect(this, &Intro::requestCheck, executor, &FreespaceChecker::check);
337 /* make sure executor object is deleted in its own thread */
338 connect(thread, &QThread::finished, executor, &QObject::deleteLater);
339
340 thread->start();
341}
342
343void Intro::checkPath(const QString &dataDir) {
344 mutex.lock();
345 pathToCheck = dataDir;
346 if (!signalled) {
347 signalled = true;
348 Q_EMIT requestCheck();
349 }
350 mutex.unlock();
351}
352
354 QString retval;
355 mutex.lock();
356 retval = pathToCheck;
357 signalled = false; /* new request can be queued now */
358 mutex.unlock();
359 return retval;
360}
361
362void Intro::UpdatePruneLabels(bool prune_checked) {
364 QString storageRequiresMsg =
365 tr("At least %1 GB of data will be stored in this directory, and it "
366 "will grow over time.");
367 if (prune_checked && m_prune_target_gb <= m_blockchain_size_gb) {
369 storageRequiresMsg =
370 tr("Approximately %1 GB of data will be stored in this directory.");
371 }
372 ui->lblExplanation3->setVisible(prune_checked);
373 ui->sizeWarningLabel->setText(
374 tr("%1 will download and store a copy of the Bitcoin block chain.")
375 .arg(PACKAGE_NAME) +
376 " " + storageRequiresMsg.arg(m_required_space_gb) + " " +
377 tr("The wallet will also be stored in this directory."));
378 this->adjustSize();
379}
ArgsManager gArgs
Definition: args.cpp:40
void SelectParams(const ChainType chain)
Sets the params returned by Params() to those for the given BIP70 chain name.
Definition: chainparams.cpp:50
ChainType GetChainType() const
Looks for -regtest, -testnet and returns the appropriate BIP70 chain name.
Definition: args.cpp:762
bool SoftSetArg(const std::string &strArg, const std::string &strValue)
Set an argument if it doesn't already have a value.
Definition: args.cpp:548
int64_t GetIntArg(const std::string &strArg, int64_t nDefault) const
Return integer argument or default value.
Definition: args.cpp:495
std::string GetArg(const std::string &strArg, const std::string &strDefault) const
Return string argument or default value.
Definition: args.cpp:463
bool GetBoolArg(const std::string &strArg, bool fDefault) const
Return boolean argument or default value.
Definition: args.cpp:525
CChainParams defines various tweakable parameters of a given instance of the Bitcoin system.
Definition: chainparams.h:86
uint64_t AssumedBlockchainSize() const
Minimum free space (in GB) needed for data directory.
Definition: chainparams.h:123
uint64_t AssumedChainStateSize() const
Minimum free space (in GB) needed for data directory when pruned; Does not include prune target.
Definition: chainparams.h:128
virtual const CChainParams & GetChainParams() const =0
FreespaceChecker(Intro *intro)
Definition: intro.cpp:60
Intro * intro
Definition: intro.cpp:55
void reply(int status, const QString &message, quint64 available)
void check()
Definition: intro.cpp:64
Introduction screen (pre-GUI startup).
Definition: intro.h:28
~Intro()
Definition: intro.cpp:156
void setStatus(int status, const QString &message, quint64 bytesAvailable)
Definition: intro.cpp:262
void on_ellipsisButton_clicked()
Definition: intro.cpp:313
QMutex mutex
Definition: intro.h:68
void UpdatePruneLabels(bool prune_checked)
Definition: intro.cpp:362
const int64_t m_blockchain_size_gb
Definition: intro.h:71
void setDataDirectory(const QString &dataDir)
Definition: intro.cpp:167
static bool showIfNeeded(bool &did_show_intro, bool &prune)
Determine data directory.
Definition: intro.cpp:180
uint64_t m_bytes_available
Definition: intro.h:76
QString pathToCheck
Definition: intro.h:70
friend class FreespaceChecker
Definition: intro.h:85
void on_dataDirectory_textChanged(const QString &arg1)
Definition: intro.cpp:307
int64_t m_required_space_gb
Total required space (in GB) depending on user choice (prune or not prune).
Definition: intro.h:75
void UpdateFreeSpaceLabel()
Definition: intro.cpp:291
bool signalled
Definition: intro.h:69
QString getPathToCheck()
Definition: intro.cpp:353
Ui::Intro * ui
Definition: intro.h:66
void requestCheck()
Intro(QWidget *parent=nullptr, int64_t blockchain_size_gb=0, int64_t chain_state_size_gb=0)
Definition: intro.cpp:121
const int64_t m_chain_state_size_gb
Definition: intro.h:72
QString getDataDirectory()
Definition: intro.cpp:163
void checkPath(const QString &dataDir)
Definition: intro.cpp:343
void startThread()
Definition: intro.cpp:330
void on_dataDirDefault_clicked()
Definition: intro.cpp:321
const int64_t m_prune_target_gb
Definition: intro.h:77
QThread * thread
Definition: intro.h:67
void on_dataDirCustom_clicked()
Definition: intro.cpp:325
Path class wrapper to block calls to the fs::path(std::string) implicit constructor and the fs::path:...
Definition: fs.h:30
const Config & GetConfig()
Definition: config.cpp:40
bool TryCreateDirectories(const fs::path &p)
Ignores exceptions thrown by create_directories if the requested directory exists.
Definition: fs_helpers.cpp:288
static constexpr int DEFAULT_PRUNE_TARGET_GB
Definition: guiconstants.h:56
static constexpr uint64_t GB_BYTES
Definition: guiconstants.h:53
static const bool DEFAULT_CHOOSE_DATADIR
Definition: intro.h:12
fs::path qstringToBoostPath(const QString &path)
Convert QString to OS specific boost path through UTF-8.
Definition: guiutil.cpp:787
QString getDefaultDataDirectory()
Determine default data directory for operating system.
Definition: guiutil.cpp:295
static bool exists(const path &p)
Definition: fs.h:107
static std::string PathToString(const path &path)
Convert path object to byte string.
Definition: fs.h:147
static int PruneMiBtoGB(int64_t mib)
Convert configured prune target MiB to displayed GB.
Definition: optionsmodel.h:30