Dash Core  0.12.2.1
P2P Digital Currency
intro.cpp
Go to the documentation of this file.
1 // Copyright (c) 2011-2015 The Bitcoin Core developers
2 // Copyright (c) 2014-2017 The Dash Core developers
3 // Distributed under the MIT software license, see the accompanying
4 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
5 
6 #include "intro.h"
7 #include "ui_intro.h"
8 
9 #include "guiutil.h"
10 
11 #include "util.h"
12 
13 #include <boost/filesystem.hpp>
14 
15 #include <QFileDialog>
16 #include <QSettings>
17 #include <QMessageBox>
18 
19 #include <cmath>
20 
21 static const uint64_t GB_BYTES = 1000000000LL;
22 /* Minimum free space (in GB) needed for data directory */
23 static const uint64_t BLOCK_CHAIN_SIZE = 1;
24 /* Minimum free space (in GB) needed for data directory when pruned; Does not include prune target */
25 static const uint64_t CHAIN_STATE_SIZE = 1;
26 /* Total required space (in GB) depending on user choice (prune, not prune) */
27 static uint64_t requiredSpace;
28 
29 /* Check free space asynchronously to prevent hanging the UI thread.
30 
31  Up to one request to check a path is in flight to this thread; when the check()
32  function runs, the current path is requested from the associated Intro 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 checked as
37  soon as the thread becomes available.
38 */
39 class FreespaceChecker : public QObject
40 {
41  Q_OBJECT
42 
43 public:
45 
46  enum Status {
49  };
50 
51 public Q_SLOTS:
52  void check();
53 
54 Q_SIGNALS:
55  void reply(int status, const QString &message, quint64 available);
56 
57 private:
59 };
60 
61 #include "intro.moc"
62 
64 {
65  this->intro = intro;
66 }
67 
69 {
70  namespace fs = boost::filesystem;
71  QString dataDirStr = intro->getPathToCheck();
72  fs::path dataDir = GUIUtil::qstringToBoostPath(dataDirStr);
73  uint64_t freeBytesAvailable = 0;
74  int replyStatus = ST_OK;
75  QString replyMessage = tr("A new data directory will be created.");
76 
77  /* Find first parent that exists, so that fs::space does not fail */
78  fs::path parentDir = dataDir;
79  fs::path parentDirOld = fs::path();
80  while(parentDir.has_parent_path() && !fs::exists(parentDir))
81  {
82  parentDir = parentDir.parent_path();
83 
84  /* Check if we make any progress, break if not to prevent an infinite loop here */
85  if (parentDirOld == parentDir)
86  break;
87 
88  parentDirOld = parentDir;
89  }
90 
91  try {
92  freeBytesAvailable = fs::space(parentDir).available;
93  if(fs::exists(dataDir))
94  {
95  if(fs::is_directory(dataDir))
96  {
97  QString separator = "<code>" + QDir::toNativeSeparators("/") + tr("name") + "</code>";
98  replyStatus = ST_OK;
99  replyMessage = tr("Directory already exists. Add %1 if you intend to create a new directory here.").arg(separator);
100  } else {
101  replyStatus = ST_ERROR;
102  replyMessage = tr("Path already exists, and is not a directory.");
103  }
104  }
105  } catch (const fs::filesystem_error&)
106  {
107  /* Parent directory does not exist or is not accessible */
108  replyStatus = ST_ERROR;
109  replyMessage = tr("Cannot create data directory here.");
110  }
111  Q_EMIT reply(replyStatus, replyMessage, freeBytesAvailable);
112 }
113 
114 
115 Intro::Intro(QWidget *parent) :
116  QDialog(parent),
117  ui(new Ui::Intro),
118  thread(0),
119  signalled(false)
120 {
121  ui->setupUi(this);
122  uint64_t pruneTarget = std::max<int64_t>(0, GetArg("-prune", 0));
124  if (pruneTarget)
125  requiredSpace = CHAIN_STATE_SIZE + std::ceil(pruneTarget * 1024 * 1024.0 / GB_BYTES);
126  ui->sizeWarningLabel->setText(ui->sizeWarningLabel->text().arg(requiredSpace));
127  startThread();
128 }
129 
131 {
132  delete ui;
133  /* Ensure thread is finished before it is deleted */
134  Q_EMIT stopThread();
135  thread->wait();
136 }
137 
139 {
140  return ui->dataDirectory->text();
141 }
142 
143 void Intro::setDataDirectory(const QString &dataDir)
144 {
145  ui->dataDirectory->setText(dataDir);
146  if(dataDir == getDefaultDataDirectory())
147  {
148  ui->dataDirDefault->setChecked(true);
149  ui->dataDirectory->setEnabled(false);
150  ui->ellipsisButton->setEnabled(false);
151  } else {
152  ui->dataDirCustom->setChecked(true);
153  ui->dataDirectory->setEnabled(true);
154  ui->ellipsisButton->setEnabled(true);
155  }
156 }
157 
159 {
161 }
162 
164 {
165  namespace fs = boost::filesystem;
166  QSettings settings;
167  /* If data directory provided on command line, no need to look at settings
168  or show a picking dialog */
169  if(!GetArg("-datadir", "").empty())
170  return;
171  /* 1) Default data directory for operating system */
172  QString dataDirDefaultCurrent = getDefaultDataDirectory();
173  /* 2) Allow QSettings to override default dir */
174  QString dataDir = settings.value("strDataDir", dataDirDefaultCurrent).toString();
175  /* 3) Check to see if default datadir is the one we expect */
176  QString dataDirDefaultSettings = settings.value("strDataDirDefault").toString();
177 
178  if(!fs::exists(GUIUtil::qstringToBoostPath(dataDir)) || GetBoolArg("-choosedatadir", DEFAULT_CHOOSE_DATADIR) || dataDirDefaultCurrent != dataDirDefaultSettings)
179  {
180  /* Let the user choose one */
181  Intro intro;
182  intro.setDataDirectory(dataDirDefaultCurrent);
183  intro.setWindowIcon(QIcon(":icons/bitcoin"));
184 
185  while(true)
186  {
187  if(!intro.exec())
188  {
189  /* Cancel clicked */
190  exit(EXIT_SUCCESS);
191  }
192  dataDir = intro.getDataDirectory();
193  try {
195  break;
196  } catch (const fs::filesystem_error&) {
197  QMessageBox::critical(0, tr("Dash Core"),
198  tr("Error: Specified data directory \"%1\" cannot be created.").arg(dataDir));
199  /* fall through, back to choosing screen */
200  }
201  }
202 
203  settings.setValue("strDataDir", dataDir);
204  settings.setValue("strDataDirDefault", dataDirDefaultCurrent);
205  }
206  /* Only override -datadir if different from the default, to make it possible to
207  * override -datadir in the dash.conf file in the default data directory
208  * (to be consistent with dashd behavior)
209  */
210  if(dataDir != dataDirDefaultCurrent)
211  SoftSetArg("-datadir", GUIUtil::qstringToBoostPath(dataDir).string()); // use OS locale for path setting
212 }
213 
214 void Intro::setStatus(int status, const QString &message, quint64 bytesAvailable)
215 {
216  switch(status)
217  {
219  ui->errorMessage->setText(message);
220  ui->errorMessage->setStyleSheet("");
221  break;
223  ui->errorMessage->setText(tr("Error") + ": " + message);
224  ui->errorMessage->setStyleSheet("QLabel { color: #800000 }");
225  break;
226  }
227  /* Indicate number of bytes available */
228  if(status == FreespaceChecker::ST_ERROR)
229  {
230  ui->freeSpace->setText("");
231  } else {
232  QString freeString = tr("%1 GB of free space available").arg(bytesAvailable/GB_BYTES);
233  if(bytesAvailable < requiredSpace * GB_BYTES)
234  {
235  freeString += " " + tr("(of %1 GB needed)").arg(requiredSpace);
236  ui->freeSpace->setStyleSheet("QLabel { color: #800000 }");
237  } else {
238  ui->freeSpace->setStyleSheet("");
239  }
240  ui->freeSpace->setText(freeString + ".");
241  }
242  /* Don't allow confirm in ERROR state */
243  ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(status != FreespaceChecker::ST_ERROR);
244 }
245 
246 void Intro::on_dataDirectory_textChanged(const QString &dataDirStr)
247 {
248  /* Disable OK button until check result comes in */
249  ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
250  checkPath(dataDirStr);
251 }
252 
254 {
255  QString dir = QDir::toNativeSeparators(QFileDialog::getExistingDirectory(0, "Choose data directory", ui->dataDirectory->text()));
256  if(!dir.isEmpty())
257  ui->dataDirectory->setText(dir);
258 }
259 
261 {
263 }
264 
266 {
267  ui->dataDirectory->setEnabled(true);
268  ui->ellipsisButton->setEnabled(true);
269 }
270 
272 {
273  thread = new QThread(this);
274  FreespaceChecker *executor = new FreespaceChecker(this);
275  executor->moveToThread(thread);
276 
277  connect(executor, SIGNAL(reply(int,QString,quint64)), this, SLOT(setStatus(int,QString,quint64)));
278  connect(this, SIGNAL(requestCheck()), executor, SLOT(check()));
279  /* make sure executor object is deleted in its own thread */
280  connect(this, SIGNAL(stopThread()), executor, SLOT(deleteLater()));
281  connect(this, SIGNAL(stopThread()), thread, SLOT(quit()));
282 
283  thread->start();
284 }
285 
286 void Intro::checkPath(const QString &dataDir)
287 {
288  mutex.lock();
289  pathToCheck = dataDir;
290  if(!signalled)
291  {
292  signalled = true;
293  Q_EMIT requestCheck();
294  }
295  mutex.unlock();
296 }
297 
299 {
300  QString retval;
301  mutex.lock();
303  signalled = false; /* new request can be queued now */
304  mutex.unlock();
305  return retval;
306 }
QPushButton * ellipsisButton
Definition: ui_intro.h:44
static const bool DEFAULT_CHOOSE_DATADIR
Definition: intro.h:12
static void pickDataDirectory()
Definition: intro.cpp:163
QRadioButton * dataDirDefault
Definition: ui_intro.h:37
Intro * intro
Definition: intro.cpp:58
QString boostPathToQString(const boost::filesystem::path &path)
Definition: guiutil.cpp:960
~Intro()
Definition: intro.cpp:130
QString getDataDirectory()
Definition: intro.cpp:138
void reply(int status, const QString &message, quint64 available)
QLabel * errorMessage
Definition: ui_intro.h:48
void setDataDirectory(const QString &dataDir)
Definition: intro.cpp:143
QLabel * sizeWarningLabel
Definition: ui_intro.h:36
static const uint64_t BLOCK_CHAIN_SIZE
Definition: intro.cpp:23
void check()
Definition: intro.cpp:68
QThread * thread
Definition: intro.h:63
static uint64_t requiredSpace
Definition: intro.cpp:27
dictionary settings
QRadioButton * dataDirCustom
Definition: ui_intro.h:38
QMutex mutex
Definition: intro.h:64
void startThread()
Definition: intro.cpp:271
bool GetBoolArg(const std::string &strArg, bool fDefault)
Definition: util.cpp:455
bool SoftSetArg(const std::string &strArg, const std::string &strValue)
Definition: util.cpp:462
void stopThread()
Definition: moc_intro.cpp:169
QString pathToCheck
Definition: intro.h:66
static const uint64_t GB_BYTES
Definition: intro.cpp:21
void requestCheck()
Definition: moc_intro.cpp:163
bool TryCreateDirectory(const boost::filesystem::path &p)
Definition: util.cpp:691
FreespaceChecker(Intro *intro)
Definition: intro.cpp:63
void on_ellipsisButton_clicked()
Definition: intro.cpp:253
QLineEdit * dataDirectory
Definition: ui_intro.h:43
QDialogButtonBox * buttonBox
Definition: ui_intro.h:50
friend class FreespaceChecker
Definition: intro.h:72
Definition: intro.h:24
void on_dataDirectory_textChanged(const QString &arg1)
Definition: intro.cpp:246
QString getPathToCheck()
Definition: intro.cpp:298
Ui::Intro * ui
Definition: intro.h:62
void on_dataDirCustom_clicked()
Definition: intro.cpp:265
void setStatus(int status, const QString &message, quint64 bytesAvailable)
Definition: intro.cpp:214
void on_dataDirDefault_clicked()
Definition: intro.cpp:260
std::string GetArg(const std::string &strArg, const std::string &strDefault)
Definition: util.cpp:441
static QString getDefaultDataDirectory()
Definition: intro.cpp:158
QLabel * freeSpace
Definition: ui_intro.h:46
void setupUi(QDialog *Intro)
Definition: ui_intro.h:52
void checkPath(const QString &dataDir)
Definition: intro.cpp:286
boost::filesystem::path qstringToBoostPath(const QString &path)
Definition: guiutil.cpp:955
Intro(QWidget *parent=0)
Definition: intro.cpp:115
static const uint64_t CHAIN_STATE_SIZE
Definition: intro.cpp:25
boost::filesystem::path GetDefaultDataDir()
Definition: util.cpp:516
bool signalled
Definition: intro.h:65