Master Core  v0.0.9 - 2abfd2849db8ba7a83957c64eb976b406713c123
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Pages
intro.cpp
Go to the documentation of this file.
1 // Copyright (c) 2011-2014 The Bitcoin developers
2 // Distributed under the MIT/X11 software license, see the accompanying
3 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
4 
5 #include "intro.h"
6 #include "ui_intro.h"
7 
8 #include "guiutil.h"
9 
10 #include "util.h"
11 
12 #include <boost/filesystem.hpp>
13 
14 #include <QFileDialog>
15 #include <QSettings>
16 #include <QMessageBox>
17 
18 /* Minimum free space (in bytes) needed for data directory */
19 static const uint64_t GB_BYTES = 1000000000LL;
20 static const uint64_t BLOCK_CHAIN_SIZE = 20LL * GB_BYTES;
21 
22 /* Check free space asynchronously to prevent hanging the UI thread.
23 
24  Up to one request to check a path is in flight to this thread; when the check()
25  function runs, the current path is requested from the associated Intro object.
26  The reply is sent back through a signal.
27 
28  This ensures that no queue of checking requests is built up while the user is
29  still entering the path, and that always the most recently entered path is checked as
30  soon as the thread becomes available.
31 */
32 class FreespaceChecker : public QObject
33 {
34  Q_OBJECT
35 
36 public:
38 
39  enum Status {
42  };
43 
44 public slots:
45  void check();
46 
47 signals:
48  void reply(int status, const QString &message, quint64 available);
49 
50 private:
52 };
53 
54 #include "intro.moc"
55 
57 {
58  this->intro = intro;
59 }
60 
62 {
63  namespace fs = boost::filesystem;
64  QString dataDirStr = intro->getPathToCheck();
65  fs::path dataDir = GUIUtil::qstringToBoostPath(dataDirStr);
66  uint64_t freeBytesAvailable = 0;
67  int replyStatus = ST_OK;
68  QString replyMessage = tr("A new data directory will be created.");
69 
70  /* Find first parent that exists, so that fs::space does not fail */
71  fs::path parentDir = dataDir;
72  fs::path parentDirOld = fs::path();
73  while(parentDir.has_parent_path() && !fs::exists(parentDir))
74  {
75  parentDir = parentDir.parent_path();
76 
77  /* Check if we make any progress, break if not to prevent an infinite loop here */
78  if (parentDirOld == parentDir)
79  break;
80 
81  parentDirOld = parentDir;
82  }
83 
84  try {
85  freeBytesAvailable = fs::space(parentDir).available;
86  if(fs::exists(dataDir))
87  {
88  if(fs::is_directory(dataDir))
89  {
90  QString separator = "<code>" + QDir::toNativeSeparators("/") + tr("name") + "</code>";
91  replyStatus = ST_OK;
92  replyMessage = tr("Directory already exists. Add %1 if you intend to create a new directory here.").arg(separator);
93  } else {
94  replyStatus = ST_ERROR;
95  replyMessage = tr("Path already exists, and is not a directory.");
96  }
97  }
98  } catch(fs::filesystem_error &e)
99  {
100  /* Parent directory does not exist or is not accessible */
101  replyStatus = ST_ERROR;
102  replyMessage = tr("Cannot create data directory here.");
103  }
104  emit reply(replyStatus, replyMessage, freeBytesAvailable);
105 }
106 
107 
109  QDialog(parent),
110  ui(new Ui::Intro),
111  thread(0),
112  signalled(false)
113 {
114  ui->setupUi(this);
116  startThread();
117 }
118 
120 {
121  delete ui;
122  /* Ensure thread is finished before it is deleted */
123  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  {
137  ui->dataDirDefault->setChecked(true);
138  ui->dataDirectory->setEnabled(false);
139  ui->ellipsisButton->setEnabled(false);
140  } else {
141  ui->dataDirCustom->setChecked(true);
142  ui->dataDirectory->setEnabled(true);
143  ui->ellipsisButton->setEnabled(true);
144  }
145 }
146 
148 {
150 }
151 
153 {
154  namespace fs = boost::filesystem;
155  QSettings settings;
156  /* If data directory provided on command line, no need to look at settings
157  or show a picking dialog */
158  if(!GetArg("-datadir", "").empty())
159  return;
160  /* 1) Default data directory for operating system */
161  QString dataDir = getDefaultDataDirectory();
162  /* 2) Allow QSettings to override default dir */
163  dataDir = settings.value("strDataDir", dataDir).toString();
164 
165  if(!fs::exists(GUIUtil::qstringToBoostPath(dataDir)) || GetBoolArg("-choosedatadir", false))
166  {
167  /* If current default data directory does not exist, let the user choose one */
168  Intro intro;
169  intro.setDataDirectory(dataDir);
170  intro.setWindowIcon(QIcon(":icons/bitcoin"));
171 
172  while(true)
173  {
174  if(!intro.exec())
175  {
176  /* Cancel clicked */
177  exit(0);
178  }
179  dataDir = intro.getDataDirectory();
180  try {
182  break;
183  } catch(fs::filesystem_error &e) {
184  QMessageBox::critical(0, tr("Bitcoin"),
185  tr("Error: Specified data directory \"%1\" can not be created.").arg(dataDir));
186  /* fall through, back to choosing screen */
187  }
188  }
189 
190  settings.setValue("strDataDir", dataDir);
191  }
192  /* Only override -datadir if different from the default, to make it possible to
193  * override -datadir in the bitcoin.conf file in the default data directory
194  * (to be consistent with bitcoind behavior)
195  */
196  if(dataDir != getDefaultDataDirectory())
197  SoftSetArg("-datadir", GUIUtil::qstringToBoostPath(dataDir).string()); // use OS locale for path setting
198 }
199 
200 void Intro::setStatus(int status, const QString &message, quint64 bytesAvailable)
201 {
202  switch(status)
203  {
205  ui->errorMessage->setText(message);
206  ui->errorMessage->setStyleSheet("");
207  break;
209  ui->errorMessage->setText(tr("Error") + ": " + message);
210  ui->errorMessage->setStyleSheet("QLabel { color: #800000 }");
211  break;
212  }
213  /* Indicate number of bytes available */
214  if(status == FreespaceChecker::ST_ERROR)
215  {
216  ui->freeSpace->setText("");
217  } else {
218  QString freeString = QString::number(bytesAvailable/GB_BYTES) + tr("GB of free space available");
219  if(bytesAvailable < BLOCK_CHAIN_SIZE)
220  {
221  freeString += " " + tr("(of %1GB needed)").arg(BLOCK_CHAIN_SIZE/GB_BYTES);
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  {
278  signalled = true;
279  emit requestCheck();
280  }
281  mutex.unlock();
282 }
283 
285 {
286  QString retval;
287  mutex.lock();
288  retval = pathToCheck;
289  signalled = false; /* new request can be queued now */
290  mutex.unlock();
291  return retval;
292 }
void reply(int status, const QString &message, quint64 available)
QRadioButton * dataDirDefault
Definition: ui_intro.h:36
void requestCheck()
Definition: moc_intro.cpp:113
void on_dataDirCustom_clicked()
Definition: intro.cpp:251
FreespaceChecker(Intro *intro)
Definition: intro.cpp:56
static const uint64_t BLOCK_CHAIN_SIZE
Definition: intro.cpp:20
void setupUi(QDialog *Intro)
Definition: ui_intro.h:51
QString getDataDirectory()
Definition: intro.cpp:127
boost::filesystem::path qstringToBoostPath(const QString &path)
Definition: guiutil.cpp:773
Intro(QWidget *parent=0)
Definition: intro.cpp:108
bool signalled
Definition: intro.h:63
QRadioButton * dataDirCustom
Definition: ui_intro.h:37
static QString getDefaultDataDirectory()
Determine default data directory for operating system.
Definition: intro.cpp:147
bool GetBoolArg(const std::string &strArg, bool fDefault)
Return boolean argument or default value.
Definition: util.cpp:519
bool SoftSetArg(const std::string &strArg, const std::string &strValue)
Set an argument if it doesn't already have a value.
Definition: util.cpp:530
void on_dataDirectory_textChanged(const QString &arg1)
Definition: intro.cpp:232
void setStatus(int status, const QString &message, quint64 bytesAvailable)
Definition: intro.cpp:200
QLabel * errorMessage
Definition: ui_intro.h:47
void checkPath(const QString &dataDir)
Definition: intro.cpp:272
void on_ellipsisButton_clicked()
Definition: intro.cpp:239
QDialogButtonBox * buttonBox
Definition: ui_intro.h:49
void on_dataDirDefault_clicked()
Definition: intro.cpp:246
static const uint64_t GB_BYTES
Definition: intro.cpp:19
bool TryCreateDirectory(const boost::filesystem::path &p)
Definition: util.cpp:1078
QString getPathToCheck()
Definition: intro.cpp:284
QLabel * freeSpace
Definition: ui_intro.h:45
QLineEdit * dataDirectory
Definition: ui_intro.h:42
friend class FreespaceChecker
Definition: intro.h:70
void setDataDirectory(const QString &dataDir)
Definition: intro.cpp:132
Ui::Intro * ui
Definition: intro.h:60
void startThread()
Definition: intro.cpp:257
static void pickDataDirectory()
Determine data directory.
Definition: intro.cpp:152
QLabel * sizeWarningLabel
Definition: ui_intro.h:35
void check()
Definition: intro.cpp:61
std::string GetArg(const std::string &strArg, const std::string &strDefault)
Return string argument or default value.
Definition: util.cpp:505
QString boostPathToQString(const boost::filesystem::path &path)
Definition: guiutil.cpp:778
~Intro()
Definition: intro.cpp:119
QMutex mutex
Definition: intro.h:62
Intro * intro
Definition: intro.cpp:51
QThread * thread
Definition: intro.h:61
QString pathToCheck
Definition: intro.h:64
Introduction screen (pre-GUI startup).
Definition: intro.h:22
QPushButton * ellipsisButton
Definition: ui_intro.h:43
void stopThread()
Definition: moc_intro.cpp:119
boost::filesystem::path GetDefaultDataDir()
Definition: util.cpp:941