]> Git Repo - VerusCoin.git/blob - src/qt/intro.cpp
Merge pull request #12 from miketout/dev
[VerusCoin.git] / src / qt / intro.cpp
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.
4
5 #include "intro.h"
6 #include "ui_intro.h"
7
8 #include "guiutil.h"
9 #include "scicon.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 = 20LL * 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:
38     FreespaceChecker(Intro *intro);
39
40     enum Status {
41         ST_OK,
42         ST_ERROR
43     };
44
45 public Q_SLOTS:
46     void check();
47
48 Q_SIGNALS:
49     void reply(int status, const QString &message, quint64 available);
50
51 private:
52     Intro *intro;
53 };
54
55 #include "intro.moc"
56
57 FreespaceChecker::FreespaceChecker(Intro *intro)
58 {
59     this->intro = intro;
60 }
61
62 void FreespaceChecker::check()
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 (const fs::filesystem_error&)
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     Q_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
120 Intro::~Intro()
121 {
122     delete ui;
123     /* Ensure thread is finished before it is deleted */
124     Q_EMIT stopThread();
125     thread->wait();
126 }
127
128 QString Intro::getDataDirectory()
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
148 QString Intro::getDefaultDataDirectory()
149 {
150     return GUIUtil::boostPathToQString(GetDefaultDataDir());
151 }
152
153 void Intro::pickDataDirectory()
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(SingleColorIcon(":icons/bitcoin"));
172
173         while(true)
174         {
175             if(!intro.exec())
176             {
177                 /* Cancel clicked */
178                 exit(0);
179             }
180             dataDir = intro.getDataDirectory();
181             try {
182                 TryCreateDirectory(GUIUtil::qstringToBoostPath(dataDir));
183                 break;
184             } catch (const fs::filesystem_error&) {
185                 QMessageBox::critical(0, tr("Bitcoin Core"),
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 komodo.conf file in the default data directory
195      * (to be consistent with bitcoind 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     {
205     case FreespaceChecker::ST_OK:
206         ui->errorMessage->setText(message);
207         ui->errorMessage->setStyleSheet("");
208         break;
209     case FreespaceChecker::ST_ERROR:
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 = tr("%n GB of free space available", "", bytesAvailable/GB_BYTES);
220         if(bytesAvailable < BLOCK_CHAIN_SIZE)
221         {
222             freeString += " " + tr("(of %n GB needed)", "", 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
240 void Intro::on_ellipsisButton_clicked()
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
247 void Intro::on_dataDirDefault_clicked()
248 {
249     setDataDirectory(getDefaultDataDirectory());
250 }
251
252 void Intro::on_dataDirCustom_clicked()
253 {
254     ui->dataDirectory->setEnabled(true);
255     ui->ellipsisButton->setEnabled(true);
256 }
257
258 void Intro::startThread()
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         Q_EMIT requestCheck();
281     }
282     mutex.unlock();
283 }
284
285 QString Intro::getPathToCheck()
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 }
This page took 0.040065 seconds and 4 git commands to generate.