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