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