[test] Add getblockchaininfo functional test
[bitcoinplatinum.git] / src / qt / intro.cpp
blob0ff95d850275c3b78cd30750142a3bca3b59e883
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.
5 #if defined(HAVE_CONFIG_H)
6 #include "config/bitcoin-config.h"
7 #endif
9 #include "fs.h"
10 #include "intro.h"
11 #include "ui_intro.h"
13 #include "guiutil.h"
15 #include "util.h"
17 #include <QFileDialog>
18 #include <QSettings>
19 #include <QMessageBox>
21 #include <cmath>
23 static const uint64_t GB_BYTES = 1000000000LL;
24 /* Minimum free space (in GB) needed for data directory */
25 static const uint64_t BLOCK_CHAIN_SIZE = 150;
26 /* Minimum free space (in GB) needed for data directory when pruned; Does not include prune target */
27 static const uint64_t CHAIN_STATE_SIZE = 3;
28 /* Total required space (in GB) depending on user choice (prune, not prune) */
29 static uint64_t requiredSpace;
31 /* Check free space asynchronously to prevent hanging the UI thread.
33 Up to one request to check a path is in flight to this thread; when the check()
34 function runs, the current path is requested from the associated Intro object.
35 The reply is sent back through a signal.
37 This ensures that no queue of checking requests is built up while the user is
38 still entering the path, and that always the most recently entered path is checked as
39 soon as the thread becomes available.
41 class FreespaceChecker : public QObject
43 Q_OBJECT
45 public:
46 explicit FreespaceChecker(Intro *intro);
48 enum Status {
49 ST_OK,
50 ST_ERROR
53 public Q_SLOTS:
54 void check();
56 Q_SIGNALS:
57 void reply(int status, const QString &message, quint64 available);
59 private:
60 Intro *intro;
63 #include "intro.moc"
65 FreespaceChecker::FreespaceChecker(Intro *_intro)
67 this->intro = _intro;
70 void FreespaceChecker::check()
72 QString dataDirStr = intro->getPathToCheck();
73 fs::path dataDir = GUIUtil::qstringToBoostPath(dataDirStr);
74 uint64_t freeBytesAvailable = 0;
75 int replyStatus = ST_OK;
76 QString replyMessage = tr("A new data directory will be created.");
78 /* Find first parent that exists, so that fs::space does not fail */
79 fs::path parentDir = dataDir;
80 fs::path parentDirOld = fs::path();
81 while(parentDir.has_parent_path() && !fs::exists(parentDir))
83 parentDir = parentDir.parent_path();
85 /* Check if we make any progress, break if not to prevent an infinite loop here */
86 if (parentDirOld == parentDir)
87 break;
89 parentDirOld = parentDir;
92 try {
93 freeBytesAvailable = fs::space(parentDir).available;
94 if(fs::exists(dataDir))
96 if(fs::is_directory(dataDir))
98 QString separator = "<code>" + QDir::toNativeSeparators("/") + tr("name") + "</code>";
99 replyStatus = ST_OK;
100 replyMessage = tr("Directory already exists. Add %1 if you intend to create a new directory here.").arg(separator);
101 } else {
102 replyStatus = ST_ERROR;
103 replyMessage = tr("Path already exists, and is not a directory.");
106 } catch (const fs::filesystem_error&)
108 /* Parent directory does not exist or is not accessible */
109 replyStatus = ST_ERROR;
110 replyMessage = tr("Cannot create data directory here.");
112 Q_EMIT reply(replyStatus, replyMessage, freeBytesAvailable);
116 Intro::Intro(QWidget *parent) :
117 QDialog(parent),
118 ui(new Ui::Intro),
119 thread(0),
120 signalled(false)
122 ui->setupUi(this);
123 ui->welcomeLabel->setText(ui->welcomeLabel->text().arg(tr(PACKAGE_NAME)));
124 ui->storageLabel->setText(ui->storageLabel->text().arg(tr(PACKAGE_NAME)));
126 ui->lblExplanation1->setText(ui->lblExplanation1->text()
127 .arg(tr(PACKAGE_NAME))
128 .arg(BLOCK_CHAIN_SIZE)
129 .arg(2009)
130 .arg(tr("Bitcoin"))
132 ui->lblExplanation2->setText(ui->lblExplanation2->text().arg(tr(PACKAGE_NAME)));
134 uint64_t pruneTarget = std::max<int64_t>(0, gArgs.GetArg("-prune", 0));
135 requiredSpace = BLOCK_CHAIN_SIZE;
136 QString storageRequiresMsg = tr("At least %1 GB of data will be stored in this directory, and it will grow over time.");
137 if (pruneTarget) {
138 uint64_t prunedGBs = std::ceil(pruneTarget * 1024 * 1024.0 / GB_BYTES);
139 if (prunedGBs <= requiredSpace) {
140 requiredSpace = prunedGBs;
141 storageRequiresMsg = tr("Approximately %1 GB of data will be stored in this directory.");
143 ui->lblExplanation3->setVisible(true);
144 } else {
145 ui->lblExplanation3->setVisible(false);
147 requiredSpace += CHAIN_STATE_SIZE;
148 ui->sizeWarningLabel->setText(
149 tr("%1 will download and store a copy of the Bitcoin block chain.").arg(tr(PACKAGE_NAME)) + " " +
150 storageRequiresMsg.arg(requiredSpace) + " " +
151 tr("The wallet will also be stored in this directory.")
153 startThread();
156 Intro::~Intro()
158 delete ui;
159 /* Ensure thread is finished before it is deleted */
160 Q_EMIT stopThread();
161 thread->wait();
164 QString Intro::getDataDirectory()
166 return ui->dataDirectory->text();
169 void Intro::setDataDirectory(const QString &dataDir)
171 ui->dataDirectory->setText(dataDir);
172 if(dataDir == getDefaultDataDirectory())
174 ui->dataDirDefault->setChecked(true);
175 ui->dataDirectory->setEnabled(false);
176 ui->ellipsisButton->setEnabled(false);
177 } else {
178 ui->dataDirCustom->setChecked(true);
179 ui->dataDirectory->setEnabled(true);
180 ui->ellipsisButton->setEnabled(true);
184 QString Intro::getDefaultDataDirectory()
186 return GUIUtil::boostPathToQString(GetDefaultDataDir());
189 bool Intro::pickDataDirectory()
191 QSettings settings;
192 /* If data directory provided on command line, no need to look at settings
193 or show a picking dialog */
194 if(!gArgs.GetArg("-datadir", "").empty())
195 return true;
196 /* 1) Default data directory for operating system */
197 QString dataDir = getDefaultDataDirectory();
198 /* 2) Allow QSettings to override default dir */
199 dataDir = settings.value("strDataDir", dataDir).toString();
201 if(!fs::exists(GUIUtil::qstringToBoostPath(dataDir)) || gArgs.GetBoolArg("-choosedatadir", DEFAULT_CHOOSE_DATADIR) || settings.value("fReset", false).toBool() || gArgs.GetBoolArg("-resetguisettings", false))
203 /* If current default data directory does not exist, let the user choose one */
204 Intro intro;
205 intro.setDataDirectory(dataDir);
206 intro.setWindowIcon(QIcon(":icons/bitcoin"));
208 while(true)
210 if(!intro.exec())
212 /* Cancel clicked */
213 return false;
215 dataDir = intro.getDataDirectory();
216 try {
217 TryCreateDirectories(GUIUtil::qstringToBoostPath(dataDir));
218 break;
219 } catch (const fs::filesystem_error&) {
220 QMessageBox::critical(0, tr(PACKAGE_NAME),
221 tr("Error: Specified data directory \"%1\" cannot be created.").arg(dataDir));
222 /* fall through, back to choosing screen */
226 settings.setValue("strDataDir", dataDir);
227 settings.setValue("fReset", false);
229 /* Only override -datadir if different from the default, to make it possible to
230 * override -datadir in the bitcoin.conf file in the default data directory
231 * (to be consistent with bitcoind behavior)
233 if(dataDir != getDefaultDataDirectory())
234 gArgs.SoftSetArg("-datadir", GUIUtil::qstringToBoostPath(dataDir).string()); // use OS locale for path setting
235 return true;
238 void Intro::setStatus(int status, const QString &message, quint64 bytesAvailable)
240 switch(status)
242 case FreespaceChecker::ST_OK:
243 ui->errorMessage->setText(message);
244 ui->errorMessage->setStyleSheet("");
245 break;
246 case FreespaceChecker::ST_ERROR:
247 ui->errorMessage->setText(tr("Error") + ": " + message);
248 ui->errorMessage->setStyleSheet("QLabel { color: #800000 }");
249 break;
251 /* Indicate number of bytes available */
252 if(status == FreespaceChecker::ST_ERROR)
254 ui->freeSpace->setText("");
255 } else {
256 QString freeString = tr("%n GB of free space available", "", bytesAvailable/GB_BYTES);
257 if(bytesAvailable < requiredSpace * GB_BYTES)
259 freeString += " " + tr("(of %n GB needed)", "", requiredSpace);
260 ui->freeSpace->setStyleSheet("QLabel { color: #800000 }");
261 } else {
262 ui->freeSpace->setStyleSheet("");
264 ui->freeSpace->setText(freeString + ".");
266 /* Don't allow confirm in ERROR state */
267 ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(status != FreespaceChecker::ST_ERROR);
270 void Intro::on_dataDirectory_textChanged(const QString &dataDirStr)
272 /* Disable OK button until check result comes in */
273 ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
274 checkPath(dataDirStr);
277 void Intro::on_ellipsisButton_clicked()
279 QString dir = QDir::toNativeSeparators(QFileDialog::getExistingDirectory(0, "Choose data directory", ui->dataDirectory->text()));
280 if(!dir.isEmpty())
281 ui->dataDirectory->setText(dir);
284 void Intro::on_dataDirDefault_clicked()
286 setDataDirectory(getDefaultDataDirectory());
289 void Intro::on_dataDirCustom_clicked()
291 ui->dataDirectory->setEnabled(true);
292 ui->ellipsisButton->setEnabled(true);
295 void Intro::startThread()
297 thread = new QThread(this);
298 FreespaceChecker *executor = new FreespaceChecker(this);
299 executor->moveToThread(thread);
301 connect(executor, SIGNAL(reply(int,QString,quint64)), this, SLOT(setStatus(int,QString,quint64)));
302 connect(this, SIGNAL(requestCheck()), executor, SLOT(check()));
303 /* make sure executor object is deleted in its own thread */
304 connect(this, SIGNAL(stopThread()), executor, SLOT(deleteLater()));
305 connect(this, SIGNAL(stopThread()), thread, SLOT(quit()));
307 thread->start();
310 void Intro::checkPath(const QString &dataDir)
312 mutex.lock();
313 pathToCheck = dataDir;
314 if(!signalled)
316 signalled = true;
317 Q_EMIT requestCheck();
319 mutex.unlock();
322 QString Intro::getPathToCheck()
324 QString retval;
325 mutex.lock();
326 retval = pathToCheck;
327 signalled = false; /* new request can be queued now */
328 mutex.unlock();
329 return retval;