1 // Copyright (c) 2011-2014 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.
12 #include <boost/filesystem.hpp>
14 #include <QFileDialog>
16 #include <QMessageBox>
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 = 1LL * GB_BYTES;
22 /* Check free space asynchronously to prevent hanging the UI thread.
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.
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.
32 class FreespaceChecker : public QObject
37 FreespaceChecker(Intro *intro);
48 void reply(int status, const QString &message, quint64 available);
56 FreespaceChecker::FreespaceChecker(Intro *intro)
61 void FreespaceChecker::check()
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.");
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))
75 parentDir = parentDir.parent_path();
77 /* Check if we make any progress, break if not to prevent an infinite loop here */
78 if (parentDirOld == parentDir)
81 parentDirOld = parentDir;
85 freeBytesAvailable = fs::space(parentDir).available;
86 if(fs::exists(dataDir))
88 if(fs::is_directory(dataDir))
90 QString separator = "<code>" + QDir::toNativeSeparators("/") + tr("name") + "</code>";
92 replyMessage = tr("Directory already exists. Add %1 if you intend to create a new directory here.").arg(separator);
94 replyStatus = ST_ERROR;
95 replyMessage = tr("Path already exists, and is not a directory.");
98 } catch (const fs::filesystem_error&)
100 /* Parent directory does not exist or is not accessible */
101 replyStatus = ST_ERROR;
102 replyMessage = tr("Cannot create data directory here.");
104 emit reply(replyStatus, replyMessage, freeBytesAvailable);
108 Intro::Intro(QWidget *parent) :
115 ui->sizeWarningLabel->setText(ui->sizeWarningLabel->text().arg(BLOCK_CHAIN_SIZE/GB_BYTES));
122 /* Ensure thread is finished before it is deleted */
127 QString Intro::getDataDirectory()
129 return ui->dataDirectory->text();
132 void Intro::setDataDirectory(const QString &dataDir)
134 ui->dataDirectory->setText(dataDir);
135 if(dataDir == getDefaultDataDirectory())
137 ui->dataDirDefault->setChecked(true);
138 ui->dataDirectory->setEnabled(false);
139 ui->ellipsisButton->setEnabled(false);
141 ui->dataDirCustom->setChecked(true);
142 ui->dataDirectory->setEnabled(true);
143 ui->ellipsisButton->setEnabled(true);
147 QString Intro::getDefaultDataDirectory()
149 return GUIUtil::boostPathToQString(GetDefaultDataDir());
152 void Intro::pickDataDirectory()
154 namespace fs = boost::filesystem;
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())
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();
165 if(!fs::exists(GUIUtil::qstringToBoostPath(dataDir)) || GetBoolArg("-choosedatadir", false))
167 /* If current default data directory does not exist, let the user choose one */
169 intro.setDataDirectory(dataDir);
170 intro.setWindowIcon(QIcon(":icons/bitcoin"));
179 dataDir = intro.getDataDirectory();
181 boost::filesystem::create_directory(GUIUtil::qstringToBoostPath(dataDir));
183 } catch (const fs::filesystem_error&) {
184 QMessageBox::critical(0, tr("Novacoin-qt"),
185 tr("Error: Specified data directory \"%1\" cannot be created.").arg(dataDir));
186 /* fall through, back to choosing screen */
190 settings.setValue("strDataDir", dataDir);
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)
196 if(dataDir != getDefaultDataDirectory())
197 SoftSetArg("-datadir", GUIUtil::qstringToBoostPath(dataDir).string()); // use OS locale for path setting
200 void Intro::setStatus(int status, const QString &message, quint64 bytesAvailable)
204 case FreespaceChecker::ST_OK:
205 ui->errorMessage->setText(message);
206 ui->errorMessage->setStyleSheet("");
208 case FreespaceChecker::ST_ERROR:
209 ui->errorMessage->setText(tr("Error") + ": " + message);
210 ui->errorMessage->setStyleSheet("QLabel { color: #800000 }");
213 /* Indicate number of bytes available */
214 if(status == FreespaceChecker::ST_ERROR)
216 ui->freeSpace->setText("");
218 QString freeString = tr("%n GB of free space available", "", bytesAvailable/GB_BYTES);
219 if(bytesAvailable < BLOCK_CHAIN_SIZE)
221 freeString += " " + tr("(of %n GB needed)", "", BLOCK_CHAIN_SIZE/GB_BYTES);
222 ui->freeSpace->setStyleSheet("QLabel { color: #800000 }");
224 ui->freeSpace->setStyleSheet("");
226 ui->freeSpace->setText(freeString + ".");
228 /* Don't allow confirm in ERROR state */
229 ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(status != FreespaceChecker::ST_ERROR);
232 void Intro::on_dataDirectory_textChanged(const QString &dataDirStr)
234 /* Disable OK button until check result comes in */
235 ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
236 checkPath(dataDirStr);
239 void Intro::on_ellipsisButton_clicked()
241 QString dir = QDir::toNativeSeparators(QFileDialog::getExistingDirectory(0, "Choose data directory", ui->dataDirectory->text()));
243 ui->dataDirectory->setText(dir);
246 void Intro::on_dataDirDefault_clicked()
248 setDataDirectory(getDefaultDataDirectory());
251 void Intro::on_dataDirCustom_clicked()
253 ui->dataDirectory->setEnabled(true);
254 ui->ellipsisButton->setEnabled(true);
257 void Intro::startThread()
259 thread = new QThread(this);
260 FreespaceChecker *executor = new FreespaceChecker(this);
261 executor->moveToThread(thread);
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()));
272 void Intro::checkPath(const QString &dataDir)
275 pathToCheck = dataDir;
284 QString Intro::getPathToCheck()
288 retval = pathToCheck;
289 signalled = false; /* new request can be queued now */