Fabcoin Core  0.16.2
P2P Digital Currency
intro.cpp
Go to the documentation of this file.
1 // Copyright (c) 2011-2017 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 <intro.h>
11 #include <ui_intro.h>
12 
13 #include <guiutil.h>
14 
15 #include <util.h>
16 
17 #include <QFileDialog>
18 #include <QSettings>
19 #include <QMessageBox>
20 
21 #include <cmath>
22 
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 = 1;
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 = 1;
28 /* Total required space (in GB) depending on user choice (prune, not prune) */
29 static uint64_t requiredSpace;
30 
31 /* Check free space asynchronously to prevent hanging the UI thread.
32 
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.
36 
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.
40 */
41 class FreespaceChecker : public QObject
42 {
43  Q_OBJECT
44 
45 public:
47 
48  enum Status {
51  };
52 
53 public Q_SLOTS:
54  void check();
55 
56 Q_SIGNALS:
57  void reply(int status, const QString &message, quint64 available);
58 
59 private:
61 };
62 
63 #include <intro.moc>
64 
66 {
67  this->intro = _intro;
68 }
69 
71 {
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.");
77 
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))
82  {
83  parentDir = parentDir.parent_path();
84 
85  /* Check if we make any progress, break if not to prevent an infinite loop here */
86  if (parentDirOld == parentDir)
87  break;
88 
89  parentDirOld = parentDir;
90  }
91 
92  try {
93  freeBytesAvailable = fs::space(parentDir).available;
94  if(fs::exists(dataDir))
95  {
96  if(fs::is_directory(dataDir))
97  {
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.");
104  }
105  }
106  } catch (const fs::filesystem_error&)
107  {
108  /* Parent directory does not exist or is not accessible */
109  replyStatus = ST_ERROR;
110  replyMessage = tr("Cannot create data directory here.");
111  }
112  Q_EMIT reply(replyStatus, replyMessage, freeBytesAvailable);
113 }
114 
115 
116 Intro::Intro(QWidget *parent) :
117  QDialog(parent),
118  ui(new Ui::Intro),
119  thread(0),
120  signalled(false)
121 {
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)));
125 
126  ui->lblExplanation1->setText(ui->lblExplanation1->text()
127  .arg(tr(PACKAGE_NAME))
128  .arg(BLOCK_CHAIN_SIZE)
129  .arg(2018)
130  .arg(tr("Fabcoin"))
131  );
132  ui->lblExplanation2->setText(ui->lblExplanation2->text().arg(tr(PACKAGE_NAME)));
133 
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.");
142  }
143  ui->lblExplanation3->setVisible(true);
144  } else {
145  ui->lblExplanation3->setVisible(false);
146  }
147  requiredSpace += CHAIN_STATE_SIZE;
148  ui->sizeWarningLabel->setText(
149  tr("%1 will download and store a copy of the Fabcoin block chain.").arg(tr(PACKAGE_NAME)) + " " +
150  storageRequiresMsg.arg(requiredSpace) + " " +
151  tr("The wallet will also be stored in this directory.")
152  );
153  startThread();
154 }
155 
157 {
158  delete ui;
159  /* Ensure thread is finished before it is deleted */
160  Q_EMIT stopThread();
161  thread->wait();
162 }
163 
165 {
166  return ui->dataDirectory->text();
167 }
168 
169 void Intro::setDataDirectory(const QString &dataDir)
170 {
171  ui->dataDirectory->setText(dataDir);
172  if(dataDir == getDefaultDataDirectory())
173  {
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);
181  }
182 }
183 
185 {
187 }
188 
190 {
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();
200 
201  if(!fs::exists(GUIUtil::qstringToBoostPath(dataDir)) || gArgs.GetBoolArg("-choosedatadir", DEFAULT_CHOOSE_DATADIR) || settings.value("fReset", false).toBool() || gArgs.GetBoolArg("-resetguisettings", false))
202  {
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/fabcoin"));
207 
208  while(true)
209  {
210  if(!intro.exec())
211  {
212  /* Cancel clicked */
213  return false;
214  }
215  dataDir = intro.getDataDirectory();
216  try {
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 */
223  }
224  }
225 
226  settings.setValue("strDataDir", dataDir);
227  settings.setValue("fReset", false);
228  }
229  /* Only override -datadir if different from the default, to make it possible to
230  * override -datadir in the fabcoin.conf file in the default data directory
231  * (to be consistent with fabcoind behavior)
232  */
233  if(dataDir != getDefaultDataDirectory())
234  gArgs.SoftSetArg("-datadir", GUIUtil::qstringToBoostPath(dataDir).string()); // use OS locale for path setting
235  return true;
236 }
237 
238 void Intro::setStatus(int status, const QString &message, quint64 bytesAvailable)
239 {
240  switch(status)
241  {
243  ui->errorMessage->setText(message);
244  ui->errorMessage->setStyleSheet("");
245  break;
247  ui->errorMessage->setText(tr("Error") + ": " + message);
248  ui->errorMessage->setStyleSheet("QLabel { color: #800000 }");
249  break;
250  }
251  /* Indicate number of bytes available */
252  if(status == FreespaceChecker::ST_ERROR)
253  {
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)
258  {
259  freeString += " " + tr("(of %n GB needed)", "", requiredSpace);
260  ui->freeSpace->setStyleSheet("QLabel { color: #800000 }");
261  } else {
262  ui->freeSpace->setStyleSheet("");
263  }
264  ui->freeSpace->setText(freeString + ".");
265  }
266  /* Don't allow confirm in ERROR state */
267  ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(status != FreespaceChecker::ST_ERROR);
268 }
269 
270 void Intro::on_dataDirectory_textChanged(const QString &dataDirStr)
271 {
272  /* Disable OK button until check result comes in */
273  ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
274  checkPath(dataDirStr);
275 }
276 
278 {
279  QString dir = QDir::toNativeSeparators(QFileDialog::getExistingDirectory(0, "Choose data directory", ui->dataDirectory->text()));
280  if(!dir.isEmpty())
281  ui->dataDirectory->setText(dir);
282 }
283 
285 {
287 }
288 
290 {
291  ui->dataDirectory->setEnabled(true);
292  ui->ellipsisButton->setEnabled(true);
293 }
294 
296 {
297  thread = new QThread(this);
298  FreespaceChecker *executor = new FreespaceChecker(this);
299  executor->moveToThread(thread);
300 
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()));
306 
307  thread->start();
308 }
309 
310 void Intro::checkPath(const QString &dataDir)
311 {
312  mutex.lock();
313  pathToCheck = dataDir;
314  if(!signalled)
315  {
316  signalled = true;
317  Q_EMIT requestCheck();
318  }
319  mutex.unlock();
320 }
321 
323 {
324  QString retval;
325  mutex.lock();
326  retval = pathToCheck;
327  signalled = false; /* new request can be queued now */
328  mutex.unlock();
329  return retval;
330 }
void reply(int status, const QString &message, quint64 available)
#define PACKAGE_NAME
QRadioButton * dataDirDefault
Definition: ui_intro.h:37
QLabel * welcomeLabel
Definition: ui_intro.h:33
void requestCheck()
Definition: moc_intro.cpp:163
void on_dataDirCustom_clicked()
Definition: intro.cpp:289
bool GetBoolArg(const std::string &strArg, bool fDefault)
Return boolean argument or default value.
Definition: util.cpp:520
QLabel * storageLabel
Definition: ui_intro.h:35
FreespaceChecker(Intro *intro)
Definition: intro.cpp:65
void setupUi(QDialog *Intro)
Definition: ui_intro.h:55
QString getDataDirectory()
Definition: intro.cpp:164
QLabel * lblExplanation2
Definition: ui_intro.h:50
Intro(QWidget *parent=0)
Definition: intro.cpp:116
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:528
bool signalled
Definition: intro.h:68
QRadioButton * dataDirCustom
Definition: ui_intro.h:38
static QString getDefaultDataDirectory()
Determine default data directory for operating system.
Definition: intro.cpp:184
void on_dataDirectory_textChanged(const QString &arg1)
Definition: intro.cpp:270
void setStatus(int status, const QString &message, quint64 bytesAvailable)
Definition: intro.cpp:238
QLabel * errorMessage
Definition: ui_intro.h:48
void checkPath(const QString &dataDir)
Definition: intro.cpp:310
void on_ellipsisButton_clicked()
Definition: intro.cpp:277
QDialogButtonBox * buttonBox
Definition: ui_intro.h:53
QLabel * lblExplanation1
Definition: ui_intro.h:49
bool TryCreateDirectories(const fs::path &p)
Ignores exceptions thrown by Boost&#39;s create_directories if the requested directory exists...
Definition: util.cpp:730
void on_dataDirDefault_clicked()
Definition: intro.cpp:284
QLabel * lblExplanation3
Definition: ui_intro.h:51
QString getPathToCheck()
Definition: intro.cpp:322
QLabel * freeSpace
Definition: ui_intro.h:46
QLineEdit * dataDirectory
Definition: ui_intro.h:43
fs::path GetDefaultDataDir()
Definition: util.cpp:593
ArgsManager gArgs
Definition: util.cpp:94
friend class FreespaceChecker
Definition: intro.h:75
void setDataDirectory(const QString &dataDir)
Definition: intro.cpp:169
fs::path qstringToBoostPath(const QString &path)
Definition: guiutil.cpp:864
Ui::Intro * ui
Definition: intro.h:65
std::string GetArg(const std::string &strArg, const std::string &strDefault)
Return string argument or default value.
Definition: util.cpp:504
void startThread()
Definition: intro.cpp:295
QLabel * sizeWarningLabel
Definition: ui_intro.h:36
void check()
Definition: intro.cpp:70
QString boostPathToQString(const fs::path &path)
Definition: guiutil.cpp:869
~Intro()
Definition: intro.cpp:156
static bool pickDataDirectory()
Determine data directory.
Definition: intro.cpp:189
QMutex mutex
Definition: intro.h:67
Intro * intro
Definition: intro.cpp:60
QThread * thread
Definition: intro.h:66
QString pathToCheck
Definition: intro.h:69
Introduction screen (pre-GUI startup).
Definition: intro.h:24
QPushButton * ellipsisButton
Definition: ui_intro.h:44
void stopThread()
Definition: moc_intro.cpp:169