Dogecoin Core  1.14.2
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 "intro.h"
10 #include "ui_intro.h"
11 
12 #include "guiutil.h"
13 
14 #include "util.h"
15 
16 #include <boost/filesystem.hpp>
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 static const uint64_t BLOCK_CHAIN_SIZE = 55;
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 = 2;
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:
48 
49  enum Status {
51  ST_ERROR
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 "intro.moc"
65 
67 {
68  this->intro = _intro;
69 }
70 
72 {
73  namespace fs = boost::filesystem;
74  QString dataDirStr = intro->getPathToCheck();
75  fs::path dataDir = GUIUtil::qstringToBoostPath(dataDirStr);
76  uint64_t freeBytesAvailable = 0;
77  int replyStatus = ST_OK;
78  QString replyMessage = tr("A new data directory will be created.");
79 
80  /* Find first parent that exists, so that fs::space does not fail */
81  fs::path parentDir = dataDir;
82  fs::path parentDirOld = fs::path();
83  while(parentDir.has_parent_path() && !fs::exists(parentDir))
84  {
85  parentDir = parentDir.parent_path();
86 
87  /* Check if we make any progress, break if not to prevent an infinite loop here */
88  if (parentDirOld == parentDir)
89  break;
90 
91  parentDirOld = parentDir;
92  }
93 
94  try {
95  freeBytesAvailable = fs::space(parentDir).available;
96  if(fs::exists(dataDir))
97  {
98  if(fs::is_directory(dataDir))
99  {
100  QString separator = "<code>" + QDir::toNativeSeparators("/") + tr("name") + "</code>";
101  replyStatus = ST_OK;
102  replyMessage = tr("Directory already exists. Add %1 if you intend to create a new directory here.").arg(separator);
103  } else {
104  replyStatus = ST_ERROR;
105  replyMessage = tr("Path already exists, and is not a directory.");
106  }
107  }
108  } catch (const fs::filesystem_error&)
109  {
110  /* Parent directory does not exist or is not accessible */
111  replyStatus = ST_ERROR;
112  replyMessage = tr("Cannot create data directory here.");
113  }
114  Q_EMIT reply(replyStatus, replyMessage, freeBytesAvailable);
115 }
116 
117 
118 Intro::Intro(QWidget *parent) :
119  QDialog(parent),
120  ui(new Ui::Intro),
121  thread(0),
122  signalled(false)
123 {
124  ui->setupUi(this);
125  ui->welcomeLabel->setText(ui->welcomeLabel->text().arg(tr(PACKAGE_NAME)));
126  ui->storageLabel->setText(ui->storageLabel->text().arg(tr(PACKAGE_NAME)));
127  uint64_t pruneTarget = std::max<int64_t>(0, GetArg("-prune", 0));
128  requiredSpace = BLOCK_CHAIN_SIZE;
129  if (pruneTarget) {
130  uint64_t prunedGBs = std::ceil(pruneTarget * 1024 * 1024.0 / GB_BYTES);
131  if (prunedGBs <= requiredSpace) {
132  requiredSpace = prunedGBs;
133  }
134  }
135  requiredSpace += CHAIN_STATE_SIZE;
136  ui->sizeWarningLabel->setText(ui->sizeWarningLabel->text().arg(tr(PACKAGE_NAME)).arg(requiredSpace));
137  startThread();
138 }
139 
141 {
142  delete ui;
143  /* Ensure thread is finished before it is deleted */
144  Q_EMIT stopThread();
145  thread->wait();
146 }
147 
149 {
150  return ui->dataDirectory->text();
151 }
152 
153 void Intro::setDataDirectory(const QString &dataDir)
154 {
155  ui->dataDirectory->setText(dataDir);
156  if(dataDir == getDefaultDataDirectory())
157  {
158  ui->dataDirDefault->setChecked(true);
159  ui->dataDirectory->setEnabled(false);
160  ui->ellipsisButton->setEnabled(false);
161  } else {
162  ui->dataDirCustom->setChecked(true);
163  ui->dataDirectory->setEnabled(true);
164  ui->ellipsisButton->setEnabled(true);
165  }
166 }
167 
169 {
171 }
172 
174 {
175  namespace fs = boost::filesystem;
176  QSettings settings;
177  /* If data directory provided on command line, no need to look at settings
178  or show a picking dialog */
179  if(!GetArg("-datadir", "").empty())
180  return true;
181  /* 1) Default data directory for operating system */
182  QString dataDir = getDefaultDataDirectory();
183  /* 2) Allow QSettings to override default dir */
184  dataDir = settings.value("strDataDir", dataDir).toString();
185 
186  if(!fs::exists(GUIUtil::qstringToBoostPath(dataDir)) || GetBoolArg("-choosedatadir", DEFAULT_CHOOSE_DATADIR) || settings.value("fReset", false).toBool() || GetBoolArg("-resetguisettings", false))
187  {
188  /* If current default data directory does not exist, let the user choose one */
189  Intro intro;
190  intro.setDataDirectory(dataDir);
191  intro.setWindowIcon(QIcon(":icons/bitcoin"));
192 
193  while(true)
194  {
195  if(!intro.exec())
196  {
197  /* Cancel clicked */
198  return false;
199  }
200  dataDir = intro.getDataDirectory();
201  try {
203  break;
204  } catch (const fs::filesystem_error&) {
205  QMessageBox::critical(0, tr(PACKAGE_NAME),
206  tr("Error: Specified data directory \"%1\" cannot be created.").arg(dataDir));
207  /* fall through, back to choosing screen */
208  }
209  }
210 
211  settings.setValue("strDataDir", dataDir);
212  settings.setValue("fReset", false);
213  }
214  /* Only override -datadir if different from the default, to make it possible to
215  * override -datadir in the bitcoin.conf file in the default data directory
216  * (to be consistent with bitcoind behavior)
217  */
218  if(dataDir != getDefaultDataDirectory())
219  SoftSetArg("-datadir", GUIUtil::qstringToBoostPath(dataDir).string()); // use OS locale for path setting
220  return true;
221 }
222 
223 void Intro::setStatus(int status, const QString &message, quint64 bytesAvailable)
224 {
225  switch(status)
226  {
228  ui->errorMessage->setText(message);
229  ui->errorMessage->setStyleSheet("");
230  break;
232  ui->errorMessage->setText(tr("Error") + ": " + message);
233  ui->errorMessage->setStyleSheet("QLabel { color: #800000 }");
234  break;
235  }
236  /* Indicate number of bytes available */
237  if(status == FreespaceChecker::ST_ERROR)
238  {
239  ui->freeSpace->setText("");
240  } else {
241  QString freeString = tr("%n GB of free space available", "", bytesAvailable/GB_BYTES);
242  if(bytesAvailable < requiredSpace * GB_BYTES)
243  {
244  freeString += " " + tr("(of %n GB needed)", "", requiredSpace);
245  ui->freeSpace->setStyleSheet("QLabel { color: #800000 }");
246  } else {
247  ui->freeSpace->setStyleSheet("");
248  }
249  ui->freeSpace->setText(freeString + ".");
250  }
251  /* Don't allow confirm in ERROR state */
252  ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(status != FreespaceChecker::ST_ERROR);
253 }
254 
255 void Intro::on_dataDirectory_textChanged(const QString &dataDirStr)
256 {
257  /* Disable OK button until check result comes in */
258  ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
259  checkPath(dataDirStr);
260 }
261 
263 {
264  QString dir = QDir::toNativeSeparators(QFileDialog::getExistingDirectory(0, "Choose data directory", ui->dataDirectory->text()));
265  if(!dir.isEmpty())
266  ui->dataDirectory->setText(dir);
267 }
268 
270 {
272 }
273 
275 {
276  ui->dataDirectory->setEnabled(true);
277  ui->ellipsisButton->setEnabled(true);
278 }
279 
281 {
282  thread = new QThread(this);
283  FreespaceChecker *executor = new FreespaceChecker(this);
284  executor->moveToThread(thread);
285 
286  connect(executor, SIGNAL(reply(int,QString,quint64)), this, SLOT(setStatus(int,QString,quint64)));
287  connect(this, SIGNAL(requestCheck()), executor, SLOT(check()));
288  /* make sure executor object is deleted in its own thread */
289  connect(this, SIGNAL(stopThread()), executor, SLOT(deleteLater()));
290  connect(this, SIGNAL(stopThread()), thread, SLOT(quit()));
291 
292  thread->start();
293 }
294 
295 void Intro::checkPath(const QString &dataDir)
296 {
297  mutex.lock();
298  pathToCheck = dataDir;
299  if(!signalled)
300  {
301  signalled = true;
302  Q_EMIT requestCheck();
303  }
304  mutex.unlock();
305 }
306 
308 {
309  QString retval;
310  mutex.lock();
311  retval = pathToCheck;
312  signalled = false; /* new request can be queued now */
313  mutex.unlock();
314  return retval;
315 }
FreespaceChecker(Intro *intro)
Definition: intro.cpp:66
Intro * intro
Definition: intro.cpp:61
void reply(int status, const QString &message, quint64 available)
void check()
Definition: intro.cpp:71
Introduction screen (pre-GUI startup).
Definition: intro.h:25
~Intro()
Definition: intro.cpp:140
void stopThread()
void setStatus(int status, const QString &message, quint64 bytesAvailable)
Definition: intro.cpp:223
void on_ellipsisButton_clicked()
Definition: intro.cpp:262
QMutex mutex
Definition: intro.h:67
void setDataDirectory(const QString &dataDir)
Definition: intro.cpp:153
static bool pickDataDirectory()
Determine data directory.
Definition: intro.cpp:173
QString pathToCheck
Definition: intro.h:69
friend class FreespaceChecker
Definition: intro.h:75
void on_dataDirectory_textChanged(const QString &arg1)
Definition: intro.cpp:255
Intro(QWidget *parent=0)
Definition: intro.cpp:118
bool signalled
Definition: intro.h:68
static QString getDefaultDataDirectory()
Determine default data directory for operating system.
Definition: intro.cpp:168
QString getPathToCheck()
Definition: intro.cpp:307
Ui::Intro * ui
Definition: intro.h:65
void requestCheck()
QString getDataDirectory()
Definition: intro.cpp:148
void checkPath(const QString &dataDir)
Definition: intro.cpp:295
void startThread()
Definition: intro.cpp:280
void on_dataDirDefault_clicked()
Definition: intro.cpp:269
QThread * thread
Definition: intro.h:66
void on_dataDirCustom_clicked()
Definition: intro.cpp:274
boost::filesystem::path qstringToBoostPath(const QString &path)
Definition: guiutil.cpp:870
QString boostPathToQString(const boost::filesystem::path &path)
Definition: guiutil.cpp:875
std::string GetArg(const std::string &strArg, const std::string &strDefault)
Return string argument or default value.
Definition: util.cpp:395
bool TryCreateDirectory(const boost::filesystem::path &p)
Ignores exceptions thrown by Boost's create_directory if the requested directory exists.
Definition: util.cpp:621
bool GetBoolArg(const std::string &strArg, bool fDefault)
Return boolean argument or default value.
Definition: util.cpp:411
bool SoftSetArg(const std::string &strArg, const std::string &strValue)
Set an argument if it doesn't already have a value.
Definition: util.cpp:419
boost::filesystem::path GetDefaultDataDir()
Definition: util.cpp:482