Raven Core  3.0.0
P2P Digital Currency
intro.cpp
Go to the documentation of this file.
1 // Copyright (c) 2011-2016 The Bitcoin Core developers
2 // Copyright (c) 2017-2019 The Raven 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 #if defined(HAVE_CONFIG_H)
7 #include "config/raven-config.h"
8 #endif
9 
10 #include "fs.h"
11 #include "intro.h"
12 #include "ui_intro.h"
13 
14 #include "guiutil.h"
15 
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 static const uint64_t BLOCK_CHAIN_SIZE = 5;
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 = 3;
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 "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("Raven"))
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 Raven block chain.").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/raven"));
208 
209  while(true)
210  {
211  if(!intro.exec())
212  {
213  /* Cancel clicked */
214  return false;
215  }
216  dataDir = intro.getDataDirectory();
217  try {
219  break;
220  } catch (const fs::filesystem_error&) {
221  QMessageBox::critical(0, tr(PACKAGE_NAME),
222  tr("Error: Specified data directory \"%1\" cannot be created.").arg(dataDir));
223  /* fall through, back to choosing screen */
224  }
225  }
226 
227  settings.setValue("strDataDir", dataDir);
228  settings.setValue("fReset", false);
229  }
230  /* Only override -datadir if different from the default, to make it possible to
231  * override -datadir in the raven.conf file in the default data directory
232  * (to be consistent with ravend behavior)
233  */
234  if(dataDir != getDefaultDataDirectory())
235  gArgs.SoftSetArg("-datadir", GUIUtil::qstringToBoostPath(dataDir).string()); // use OS locale for path setting
236  return true;
237 }
238 
239 void Intro::setStatus(int status, const QString &message, quint64 bytesAvailable)
240 {
241  switch(status)
242  {
244  ui->errorMessage->setText(message);
245  ui->errorMessage->setStyleSheet("");
246  break;
248  ui->errorMessage->setText(tr("Error") + ": " + message);
249  ui->errorMessage->setStyleSheet("QLabel { color: #800000 }");
250  break;
251  }
252  /* Indicate number of bytes available */
253  if(status == FreespaceChecker::ST_ERROR)
254  {
255  ui->freeSpace->setText("");
256  } else {
257  QString freeString = tr("%n GB of free space available", "", bytesAvailable/GB_BYTES);
258  if(bytesAvailable < requiredSpace * GB_BYTES)
259  {
260  freeString += " " + tr("(of %n GB needed)", "", requiredSpace);
261  ui->freeSpace->setStyleSheet("QLabel { color: #800000 }");
262  } else {
263  ui->freeSpace->setStyleSheet("");
264  }
265  ui->freeSpace->setText(freeString + ".");
266  }
267  /* Don't allow confirm in ERROR state */
268  ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(status != FreespaceChecker::ST_ERROR);
269 }
270 
271 void Intro::on_dataDirectory_textChanged(const QString &dataDirStr)
272 {
273  /* Disable OK button until check result comes in */
274  ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
275  checkPath(dataDirStr);
276 }
277 
279 {
280  QString dir = QDir::toNativeSeparators(QFileDialog::getExistingDirectory(0, "Choose data directory", ui->dataDirectory->text()));
281  if(!dir.isEmpty())
282  ui->dataDirectory->setText(dir);
283 }
284 
286 {
288 }
289 
291 {
292  ui->dataDirectory->setEnabled(true);
293  ui->ellipsisButton->setEnabled(true);
294 }
295 
297 {
298  thread = new QThread(this);
299  FreespaceChecker *executor = new FreespaceChecker(this);
300  executor->moveToThread(thread);
301 
302  connect(executor, SIGNAL(reply(int,QString,quint64)), this, SLOT(setStatus(int,QString,quint64)));
303  connect(this, SIGNAL(requestCheck()), executor, SLOT(check()));
304  /* make sure executor object is deleted in its own thread */
305  connect(this, SIGNAL(stopThread()), executor, SLOT(deleteLater()));
306  connect(this, SIGNAL(stopThread()), thread, SLOT(quit()));
307 
308  thread->start();
309 }
310 
311 void Intro::checkPath(const QString &dataDir)
312 {
313  mutex.lock();
314  pathToCheck = dataDir;
315  if(!signalled)
316  {
317  signalled = true;
318  Q_EMIT requestCheck();
319  }
320  mutex.unlock();
321 }
322 
324 {
325  QString retval;
326  mutex.lock();
327  retval = pathToCheck;
328  signalled = false; /* new request can be queued now */
329  mutex.unlock();
330  return retval;
331 }
void reply(int status, const QString &message, quint64 available)
void requestCheck()
void on_dataDirCustom_clicked()
Definition: intro.cpp:290
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:470
QString getDataDirectory()
Definition: intro.cpp:165
#define PACKAGE_NAME
Definition: raven-config.h:350
Intro(QWidget *parent=0)
Definition: intro.cpp:117
bool SoftSetArg(const std::string &strArg, const std::string &strValue)
Set an argument if it doesn&#39;t already have a value.
Definition: util.cpp:478
bool signalled
Definition: intro.h:69
static QString getDefaultDataDirectory()
Determine default data directory for operating system.
Definition: intro.cpp:185
void on_dataDirectory_textChanged(const QString &arg1)
Definition: intro.cpp:271
void setStatus(int status, const QString &message, quint64 bytesAvailable)
Definition: intro.cpp:239
void checkPath(const QString &dataDir)
Definition: intro.cpp:311
void on_ellipsisButton_clicked()
Definition: intro.cpp:278
bool TryCreateDirectories(const fs::path &p)
Ignores exceptions thrown by Boost&#39;s create_directories if the requested directory exists...
Definition: util.cpp:684
void on_dataDirDefault_clicked()
Definition: intro.cpp:285
QString getPathToCheck()
Definition: intro.cpp:323
fs::path GetDefaultDataDir()
Definition: util.cpp:542
ArgsManager gArgs
Definition: util.cpp:94
friend class FreespaceChecker
Definition: intro.h:76
void setDataDirectory(const QString &dataDir)
Definition: intro.cpp:170
fs::path qstringToBoostPath(const QString &path)
Definition: guiutil.cpp:951
Ui::Intro * ui
Definition: intro.h:66
void startThread()
Definition: intro.cpp:296
std::string GetArg(const std::string &strArg, const std::string &strDefault) const
Return string argument or default value.
Definition: util.cpp:454
void check()
Definition: intro.cpp:71
QString boostPathToQString(const fs::path &path)
Definition: guiutil.cpp:956
~Intro()
Definition: intro.cpp:157
static bool pickDataDirectory()
Determine data directory.
Definition: intro.cpp:190
QMutex mutex
Definition: intro.h:68
Intro * intro
Definition: intro.cpp:61
QThread * thread
Definition: intro.h:67
QString pathToCheck
Definition: intro.h:70
Introduction screen (pre-GUI startup).
Definition: intro.h:25
void stopThread()