BSHA3  0.17.99
P2P Blockchain, based on Bitcoin
intro.cpp
Go to the documentation of this file.
1 // Copyright (c) 2011-2018 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)
7 #endif
8 
9 #include <fs.h>
10 #include <qt/intro.h>
11 #include <qt/forms/ui_intro.h>
12 
13 #include <qt/guiutil.h>
14 
15 #include <interfaces/node.h>
16 #include <util.h>
17 
18 #include <QFileDialog>
19 #include <QSettings>
20 #include <QMessageBox>
21 
22 #include <cmath>
23 
24 static const uint64_t GB_BYTES = 1000000000LL;
25 /* Minimum free space (in GB) needed for data directory */
26 constexpr uint64_t BLOCK_CHAIN_SIZE = 8;
27 /* Minimum free space (in GB) needed for data directory when pruned; Does not include prune target */
28 static const uint64_t CHAIN_STATE_SIZE = 1;
29 /* Total required space (in GB) depending on user choice (prune, not prune) */
30 static uint64_t requiredSpace;
31 
32 /* Check free space asynchronously to prevent hanging the UI thread.
33 
34  Up to one request to check a path is in flight to this thread; when the check()
35  function runs, the current path is requested from the associated Intro object.
36  The reply is sent back through a signal.
37 
38  This ensures that no queue of checking requests is built up while the user is
39  still entering the path, and that always the most recently entered path is checked as
40  soon as the thread becomes available.
41 */
42 class FreespaceChecker : public QObject
43 {
44  Q_OBJECT
45 
46 public:
47  explicit FreespaceChecker(Intro *intro);
48 
49  enum Status {
52  };
53 
54 public Q_SLOTS:
55  void check();
56 
57 Q_SIGNALS:
58  void reply(int status, const QString &message, quint64 available);
59 
60 private:
62 };
63 
64 #include <qt/intro.moc>
65 
67 {
68  this->intro = _intro;
69 }
70 
72 {
73  QString dataDirStr = intro->getPathToCheck();
74  fs::path dataDir = GUIUtil::qstringToBoostPath(dataDirStr);
75  uint64_t freeBytesAvailable = 0;
76  int replyStatus = ST_OK;
77  QString replyMessage = tr("A new data directory will be created.");
78 
79  /* Find first parent that exists, so that fs::space does not fail */
80  fs::path parentDir = dataDir;
81  fs::path parentDirOld = fs::path();
82  while(parentDir.has_parent_path() && !fs::exists(parentDir))
83  {
84  parentDir = parentDir.parent_path();
85 
86  /* Check if we make any progress, break if not to prevent an infinite loop here */
87  if (parentDirOld == parentDir)
88  break;
89 
90  parentDirOld = parentDir;
91  }
92 
93  try {
94  freeBytesAvailable = fs::space(parentDir).available;
95  if(fs::exists(dataDir))
96  {
97  if(fs::is_directory(dataDir))
98  {
99  QString separator = "<code>" + QDir::toNativeSeparators("/") + tr("name") + "</code>";
100  replyStatus = ST_OK;
101  replyMessage = tr("Directory already exists. Add %1 if you intend to create a new directory here.").arg(separator);
102  } else {
103  replyStatus = ST_ERROR;
104  replyMessage = tr("Path already exists, and is not a directory.");
105  }
106  }
107  } catch (const fs::filesystem_error&)
108  {
109  /* Parent directory does not exist or is not accessible */
110  replyStatus = ST_ERROR;
111  replyMessage = tr("Cannot create data directory here.");
112  }
113  Q_EMIT reply(replyStatus, replyMessage, freeBytesAvailable);
114 }
115 
116 
117 Intro::Intro(QWidget *parent) :
118  QDialog(parent),
119  ui(new Ui::Intro),
120  thread(0),
121  signalled(false)
122 {
123  ui->setupUi(this);
124  ui->welcomeLabel->setText(ui->welcomeLabel->text().arg(tr(PACKAGE_NAME)));
125  ui->storageLabel->setText(ui->storageLabel->text().arg(tr(PACKAGE_NAME)));
126 
127  ui->lblExplanation1->setText(ui->lblExplanation1->text()
128  .arg(tr(PACKAGE_NAME))
129  .arg(BLOCK_CHAIN_SIZE)
130  .arg(2018)
131  .arg(tr("BSHA3"))
132  );
133  ui->lblExplanation2->setText(ui->lblExplanation2->text().arg(tr(PACKAGE_NAME)));
134 
135  uint64_t pruneTarget = std::max<int64_t>(0, gArgs.GetArg("-prune", 0));
136  requiredSpace = BLOCK_CHAIN_SIZE;
137  QString storageRequiresMsg = tr("At least %1 GB of data will be stored in this directory, and it will grow over time.");
138  if (pruneTarget) {
139  uint64_t prunedGBs = std::ceil(pruneTarget * 1024 * 1024.0 / GB_BYTES);
140  if (prunedGBs <= requiredSpace) {
141  requiredSpace = prunedGBs;
142  storageRequiresMsg = tr("Approximately %1 GB of data will be stored in this directory.");
143  }
144  ui->lblExplanation3->setVisible(true);
145  } else {
146  ui->lblExplanation3->setVisible(false);
147  }
148  requiredSpace += CHAIN_STATE_SIZE;
149  ui->sizeWarningLabel->setText(
150  tr("%1 will download and store a copy of the BSHA3 blockchain.").arg(tr(PACKAGE_NAME)) + " " +
151  storageRequiresMsg.arg(requiredSpace) + " " +
152  tr("The wallet will also be stored in this directory.")
153  );
154  startThread();
155 }
156 
158 {
159  delete ui;
160  /* Ensure thread is finished before it is deleted */
161  Q_EMIT stopThread();
162  thread->wait();
163 }
164 
166 {
167  return ui->dataDirectory->text();
168 }
169 
170 void Intro::setDataDirectory(const QString &dataDir)
171 {
172  ui->dataDirectory->setText(dataDir);
173  if(dataDir == getDefaultDataDirectory())
174  {
175  ui->dataDirDefault->setChecked(true);
176  ui->dataDirectory->setEnabled(false);
177  ui->ellipsisButton->setEnabled(false);
178  } else {
179  ui->dataDirCustom->setChecked(true);
180  ui->dataDirectory->setEnabled(true);
181  ui->ellipsisButton->setEnabled(true);
182  }
183 }
184 
186 {
188 }
189 
191 {
192  QSettings settings;
193  /* If data directory provided on command line, no need to look at settings
194  or show a picking dialog */
195  if(!gArgs.GetArg("-datadir", "").empty())
196  return true;
197  /* 1) Default data directory for operating system */
198  QString dataDir = getDefaultDataDirectory();
199  /* 2) Allow QSettings to override default dir */
200  dataDir = settings.value("strDataDir", dataDir).toString();
201 
202  if(!fs::exists(GUIUtil::qstringToBoostPath(dataDir)) || gArgs.GetBoolArg("-choosedatadir", DEFAULT_CHOOSE_DATADIR) || settings.value("fReset", false).toBool() || gArgs.GetBoolArg("-resetguisettings", false))
203  {
204  /* If current default data directory does not exist, let the user choose one */
205  Intro intro;
206  intro.setDataDirectory(dataDir);
207  intro.setWindowIcon(QIcon(":icons/bitcoin"));
208 
209  while(true)
210  {
211  if(!intro.exec())
212  {
213  /* Cancel clicked */
214  return false;
215  }
216  dataDir = intro.getDataDirectory();
217  try {
219  // If a new data directory has been created, make wallets subdirectory too
221  }
222  break;
223  } catch (const fs::filesystem_error&) {
224  QMessageBox::critical(0, tr(PACKAGE_NAME),
225  tr("Error: Specified data directory \"%1\" cannot be created.").arg(dataDir));
226  /* fall through, back to choosing screen */
227  }
228  }
229 
230  settings.setValue("strDataDir", dataDir);
231  settings.setValue("fReset", false);
232  }
233  /* Only override -datadir if different from the default, to make it possible to
234  * override -datadir in the bitcoin.conf file in the default data directory
235  * (to be consistent with bsha3d behavior)
236  */
237  if(dataDir != getDefaultDataDirectory()) {
238  node.softSetArg("-datadir", GUIUtil::qstringToBoostPath(dataDir).string()); // use OS locale for path setting
239  }
240  return true;
241 }
242 
243 void Intro::setStatus(int status, const QString &message, quint64 bytesAvailable)
244 {
245  switch(status)
246  {
248  ui->errorMessage->setText(message);
249  ui->errorMessage->setStyleSheet("");
250  break;
252  ui->errorMessage->setText(tr("Error") + ": " + message);
253  ui->errorMessage->setStyleSheet("QLabel { color: #800000 }");
254  break;
255  }
256  /* Indicate number of bytes available */
257  if(status == FreespaceChecker::ST_ERROR)
258  {
259  ui->freeSpace->setText("");
260  } else {
261  QString freeString = tr("%n GB of free space available", "", bytesAvailable/GB_BYTES);
262  if(bytesAvailable < requiredSpace * GB_BYTES)
263  {
264  freeString += " " + tr("(of %n GB needed)", "", requiredSpace);
265  ui->freeSpace->setStyleSheet("QLabel { color: #800000 }");
266  } else {
267  ui->freeSpace->setStyleSheet("");
268  }
269  ui->freeSpace->setText(freeString + ".");
270  }
271  /* Don't allow confirm in ERROR state */
272  ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(status != FreespaceChecker::ST_ERROR);
273 }
274 
275 void Intro::on_dataDirectory_textChanged(const QString &dataDirStr)
276 {
277  /* Disable OK button until check result comes in */
278  ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
279  checkPath(dataDirStr);
280 }
281 
283 {
284  QString dir = QDir::toNativeSeparators(QFileDialog::getExistingDirectory(0, "Choose data directory", ui->dataDirectory->text()));
285  if(!dir.isEmpty())
286  ui->dataDirectory->setText(dir);
287 }
288 
290 {
292 }
293 
295 {
296  ui->dataDirectory->setEnabled(true);
297  ui->ellipsisButton->setEnabled(true);
298 }
299 
301 {
302  thread = new QThread(this);
303  FreespaceChecker *executor = new FreespaceChecker(this);
304  executor->moveToThread(thread);
305 
306  connect(executor, &FreespaceChecker::reply, this, &Intro::setStatus);
307  connect(this, &Intro::requestCheck, executor, &FreespaceChecker::check);
308  /* make sure executor object is deleted in its own thread */
309  connect(this, &Intro::stopThread, executor, &QObject::deleteLater);
310  connect(this, &Intro::stopThread, thread, &QThread::quit);
311 
312  thread->start();
313 }
314 
315 void Intro::checkPath(const QString &dataDir)
316 {
317  mutex.lock();
318  pathToCheck = dataDir;
319  if(!signalled)
320  {
321  signalled = true;
322  Q_EMIT requestCheck();
323  }
324  mutex.unlock();
325 }
326 
328 {
329  QString retval;
330  mutex.lock();
331  retval = pathToCheck;
332  signalled = false; /* new request can be queued now */
333  mutex.unlock();
334  return retval;
335 }
void reply(int status, const QString &message, quint64 available)
void requestCheck()
void on_dataDirCustom_clicked()
Definition: intro.cpp:294
#define PACKAGE_NAME
FreespaceChecker(Intro *intro)
Definition: intro.cpp:66
bool GetBoolArg(const std::string &strArg, bool fDefault) const
Return boolean argument or default value.
Definition: util.cpp:542
QString getDataDirectory()
Definition: intro.cpp:165
Intro(QWidget *parent=0)
Definition: intro.cpp:117
bool signalled
Definition: intro.h:72
static QString getDefaultDataDirectory()
Determine default data directory for operating system.
Definition: intro.cpp:185
static bool pickDataDirectory(interfaces::Node &node)
Determine data directory.
Definition: intro.cpp:190
void on_dataDirectory_textChanged(const QString &arg1)
Definition: intro.cpp:275
void setStatus(int status, const QString &message, quint64 bytesAvailable)
Definition: intro.cpp:243
void checkPath(const QString &dataDir)
Definition: intro.cpp:315
constexpr uint64_t BLOCK_CHAIN_SIZE
Definition: intro.cpp:26
void on_ellipsisButton_clicked()
Definition: intro.cpp:282
bool TryCreateDirectories(const fs::path &p)
Ignores exceptions thrown by Boost&#39;s create_directories if the requested directory exists...
Definition: util.cpp:1015
void on_dataDirDefault_clicked()
Definition: intro.cpp:289
QString getPathToCheck()
Definition: intro.cpp:327
fs::path GetDefaultDataDir()
Definition: util.cpp:705
ArgsManager gArgs
Definition: util.cpp:88
friend class FreespaceChecker
Definition: intro.h:79
void setDataDirectory(const QString &dataDir)
Definition: intro.cpp:170
fs::path qstringToBoostPath(const QString &path)
Definition: guiutil.cpp:763
Ui::Intro * ui
Definition: intro.h:69
void startThread()
Definition: intro.cpp:300
std::string GetArg(const std::string &strArg, const std::string &strDefault) const
Return string argument or default value.
Definition: util.cpp:526
void check()
Definition: intro.cpp:71
virtual bool softSetArg(const std::string &arg, const std::string &value)=0
Set a command line argument if it doesn&#39;t already have a value.
QString boostPathToQString(const fs::path &path)
Definition: guiutil.cpp:768
~Intro()
Definition: intro.cpp:157
QMutex mutex
Definition: intro.h:71
Top-level interface for a bitcoin node (bsha3d process).
Definition: node.h:35
Intro * intro
Definition: intro.cpp:61
QThread * thread
Definition: intro.h:70
QString pathToCheck
Definition: intro.h:73
Introduction screen (pre-GUI startup).
Definition: intro.h:28
void stopThread()