diff options
author | Wladimir J. van der Laan <laanwj@gmail.com> | 2013-05-19 17:36:01 +0200 |
---|---|---|
committer | Wladimir J. van der Laan <laanwj@gmail.com> | 2013-06-16 10:21:41 +0200 |
commit | be77b637fcf5983286382a9b9677fb97b026abe2 (patch) | |
tree | 7629a57d8c6372b86319bd54a40217ace9ee6805 /src/qt | |
parent | e58154c447670a546936b850f0bba4de087a1583 (diff) |
qt: allow user to choose data directory
This adds an introduction screen that is shown when the client is first
started in which the user can choose a data directory.
It is also possible to force the intro screen to appear using command
line argument `-choosedatadir`.
The user is warned that the client will download and store 10Gb of data.
The intro screen shows how much space is available on the device that
contains the chosen directory and warns if this is less than the 10Gb.
To make it possible to translate the introduction dialog, the initialization
sequence is changed so that translations are
loaded before the data directory. This has the by-effect that it is
no longer possible to specify a language in bitcoin.conf inside the data
directory.
Diffstat (limited to 'src/qt')
-rw-r--r-- | src/qt/bitcoin.cpp | 100 | ||||
-rw-r--r-- | src/qt/forms/intro.ui | 266 | ||||
-rw-r--r-- | src/qt/guiutil.cpp | 3 | ||||
-rw-r--r-- | src/qt/intro.cpp | 270 | ||||
-rw-r--r-- | src/qt/intro.h | 67 |
5 files changed, 665 insertions, 41 deletions
diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index 76e88b36a7..b0c45d6863 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -15,6 +15,7 @@ #include "ui_interface.h" #include "paymentserver.h" #include "splashscreen.h" +#include "intro.h" #include <QMessageBox> #if QT_VERSION < 0x050000 @@ -24,6 +25,7 @@ #include <QTimer> #include <QTranslator> #include <QLibraryInfo> +#include <QSettings> #if defined(BITCOIN_NEED_QT_PLUGINS) && !defined(_BITCOIN_QT_PLUGINS_INCLUDED) #define _BITCOIN_QT_PLUGINS_INCLUDED @@ -110,6 +112,46 @@ static void handleRunawayException(std::exception *e) exit(1); } +/** Set up translations */ +static void initTranslations(QTranslator &qtTranslatorBase, QTranslator &qtTranslator, QTranslator &translatorBase, QTranslator &translator) +{ + QSettings settings; + + // Get desired locale (e.g. "de_DE") + // 1) System default language + QString lang_territory = QLocale::system().name(); + // 2) Language from QSettings + QString lang_territory_qsettings = settings.value("language", "").toString(); + if(!lang_territory_qsettings.isEmpty()) + lang_territory = lang_territory_qsettings; + // 3) -lang command line argument + lang_territory = QString::fromStdString(GetArg("-lang", lang_territory.toStdString())); + + // Convert to "de" only by truncating "_DE" + QString lang = lang_territory; + lang.truncate(lang_territory.lastIndexOf('_')); + + // Load language files for configured locale: + // - First load the translator for the base language, without territory + // - Then load the more specific locale translator + + // Load e.g. qt_de.qm + if (qtTranslatorBase.load("qt_" + lang, QLibraryInfo::location(QLibraryInfo::TranslationsPath))) + QApplication::installTranslator(&qtTranslatorBase); + + // Load e.g. qt_de_DE.qm + if (qtTranslator.load("qt_" + lang_territory, QLibraryInfo::location(QLibraryInfo::TranslationsPath))) + QApplication::installTranslator(&qtTranslator); + + // Load e.g. bitcoin_de.qm (shortcut "de" needs to be defined in bitcoin.qrc) + if (translatorBase.load(lang, ":/translations/")) + QApplication::installTranslator(&translatorBase); + + // Load e.g. bitcoin_de_DE.qm (shortcut "de_DE" needs to be defined in bitcoin.qrc) + if (translator.load(lang_territory, ":/translations/")) + QApplication::installTranslator(&translator); +} + #ifndef BITCOIN_QT_TEST int main(int argc, char *argv[]) { @@ -130,6 +172,22 @@ int main(int argc, char *argv[]) // Register meta types used for QMetaObject::invokeMethod qRegisterMetaType< bool* >(); + // Application identification (must be set before OptionsModel is initialized, + // as it is used to locate QSettings) + QApplication::setOrganizationName("Bitcoin"); + QApplication::setOrganizationDomain("bitcoin.org"); + if (GetBoolArg("-testnet", false)) // Separate UI settings for testnet + QApplication::setApplicationName("Bitcoin-Qt-testnet"); + else + QApplication::setApplicationName("Bitcoin-Qt"); + + // Now that QSettings are accessible, initialize translations + QTranslator qtTranslatorBase, qtTranslator, translatorBase, translator; + initTranslations(qtTranslatorBase, qtTranslator, translatorBase, translator); + + // User language is set up: pick a data directory + Intro::pickDataDirectory(); + // Do this early as we don't want to bother initializing if we are just calling IPC // ... but do it after creating app, so QCoreApplication::arguments is initialized: if (PaymentServer::ipcSendCommandLine()) @@ -142,53 +200,15 @@ int main(int argc, char *argv[]) // ... then bitcoin.conf: if (!boost::filesystem::is_directory(GetDataDir(false))) { - // This message can not be translated, as translation is not initialized yet - // (which not yet possible because lang=XX can be overridden in bitcoin.conf in the data directory) - QMessageBox::critical(0, "Bitcoin", - QString("Error: Specified data directory \"%1\" does not exist.").arg(QString::fromStdString(mapArgs["-datadir"]))); + QMessageBox::critical(0, QObject::tr("Bitcoin"), + QObject::tr("Error: Specified data directory \"%1\" does not exist.").arg(QString::fromStdString(mapArgs["-datadir"]))); return 1; } ReadConfigFile(mapArgs, mapMultiArgs); - // Application identification (must be set before OptionsModel is initialized, - // as it is used to locate QSettings) - QApplication::setOrganizationName("Bitcoin"); - QApplication::setOrganizationDomain("bitcoin.org"); - if (GetBoolArg("-testnet", false)) // Separate UI settings for testnet - QApplication::setApplicationName("Bitcoin-Qt-testnet"); - else - QApplication::setApplicationName("Bitcoin-Qt"); - // ... then GUI settings: OptionsModel optionsModel; - // Get desired locale (e.g. "de_DE") from command line or use system locale - QString lang_territory = QString::fromStdString(GetArg("-lang", QLocale::system().name().toStdString())); - QString lang = lang_territory; - // Convert to "de" only by truncating "_DE" - lang.truncate(lang_territory.lastIndexOf('_')); - - QTranslator qtTranslatorBase, qtTranslator, translatorBase, translator; - // Load language files for configured locale: - // - First load the translator for the base language, without territory - // - Then load the more specific locale translator - - // Load e.g. qt_de.qm - if (qtTranslatorBase.load("qt_" + lang, QLibraryInfo::location(QLibraryInfo::TranslationsPath))) - app.installTranslator(&qtTranslatorBase); - - // Load e.g. qt_de_DE.qm - if (qtTranslator.load("qt_" + lang_territory, QLibraryInfo::location(QLibraryInfo::TranslationsPath))) - app.installTranslator(&qtTranslator); - - // Load e.g. bitcoin_de.qm (shortcut "de" needs to be defined in bitcoin.qrc) - if (translatorBase.load(lang, ":/translations/")) - app.installTranslator(&translatorBase); - - // Load e.g. bitcoin_de_DE.qm (shortcut "de_DE" needs to be defined in bitcoin.qrc) - if (translator.load(lang_territory, ":/translations/")) - app.installTranslator(&translator); - // Subscribe to global signals from core uiInterface.ThreadSafeMessageBox.connect(ThreadSafeMessageBox); uiInterface.ThreadSafeAskFee.connect(ThreadSafeAskFee); diff --git a/src/qt/forms/intro.ui b/src/qt/forms/intro.ui new file mode 100644 index 0000000000..0f6ae5a7d0 --- /dev/null +++ b/src/qt/forms/intro.ui @@ -0,0 +1,266 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>Intro</class> + <widget class="QDialog" name="Intro"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>674</width> + <height>363</height> + </rect> + </property> + <property name="windowTitle"> + <string>Welcome</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <widget class="QLabel" name="label_2"> + <property name="styleSheet"> + <string notr="true">QLabel { font-style:italic; }</string> + </property> + <property name="text"> + <string>Welcome to Bitcoin-Qt.</string> + </property> + <property name="wordWrap"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <spacer name="verticalSpacer_4"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeType"> + <enum>QSizePolicy::Minimum</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>15</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QLabel" name="label_4"> + <property name="text"> + <string>As this is the first time the program is launched, you can choose where Bitcoin-Qt will store its data.</string> + </property> + <property name="wordWrap"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="sizeWarningLabel"> + <property name="text"> + <string>Bitcoin-Qt will download and store a copy of the Bitcoin block chain. At least %1GB of data will be stored in this directory, and it will grow over time. The wallet will also be stored in this directory.</string> + </property> + <property name="wordWrap"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <widget class="QRadioButton" name="dataDirDefault"> + <property name="text"> + <string>Use the default data directory</string> + </property> + </widget> + </item> + <item> + <widget class="QRadioButton" name="dataDirCustom"> + <property name="text"> + <string>Use a custom data directory:</string> + </property> + </widget> + </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout"> + <property name="spacing"> + <number>0</number> + </property> + <property name="sizeConstraint"> + <enum>QLayout::SetDefaultConstraint</enum> + </property> + <item> + <spacer name="horizontalSpacer"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeType"> + <enum>QSizePolicy::Fixed</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>60</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <layout class="QVBoxLayout" name="verticalLayout_2"> + <property name="sizeConstraint"> + <enum>QLayout::SetDefaultConstraint</enum> + </property> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_2"> + <item> + <widget class="QLineEdit" name="dataDirectory"/> + </item> + <item> + <widget class="QPushButton" name="ellipsisButton"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Minimum" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="maximumSize"> + <size> + <width>30</width> + <height>16777215</height> + </size> + </property> + <property name="text"> + <string notr="true">…</string> + </property> + <property name="autoDefault"> + <bool>false</bool> + </property> + </widget> + </item> + </layout> + </item> + <item> + <spacer name="verticalSpacer_3"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeType"> + <enum>QSizePolicy::Fixed</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>5</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QLabel" name="freeSpace"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Expanding"> + <horstretch>1</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string/> + </property> + <property name="wordWrap"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <spacer name="verticalSpacer_2"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeType"> + <enum>QSizePolicy::Fixed</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>5</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QLabel" name="errorMessage"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Expanding"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="textFormat"> + <enum>Qt::RichText</enum> + </property> + <property name="wordWrap"> + <bool>true</bool> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </item> + <item> + <spacer name="verticalSpacer"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QDialogButtonBox" name="buttonBox"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="standardButtons"> + <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> + </property> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections> + <connection> + <sender>buttonBox</sender> + <signal>accepted()</signal> + <receiver>Intro</receiver> + <slot>accept()</slot> + <hints> + <hint type="sourcelabel"> + <x>248</x> + <y>254</y> + </hint> + <hint type="destinationlabel"> + <x>157</x> + <y>274</y> + </hint> + </hints> + </connection> + <connection> + <sender>buttonBox</sender> + <signal>rejected()</signal> + <receiver>Intro</receiver> + <slot>reject()</slot> + <hints> + <hint type="sourcelabel"> + <x>316</x> + <y>260</y> + </hint> + <hint type="destinationlabel"> + <x>286</x> + <y>274</y> + </hint> + </hints> + </connection> + </connections> +</ui> diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp index 88a6e7226e..3d1e91efdc 100644 --- a/src/qt/guiutil.cpp +++ b/src/qt/guiutil.cpp @@ -500,7 +500,8 @@ HelpMessageBox::HelpMessageBox(QWidget *parent) : uiOptions = tr("UI options") + ":\n" + " -lang=<lang> " + tr("Set language, for example \"de_DE\" (default: system locale)") + "\n" + " -min " + tr("Start minimized") + "\n" + - " -splash " + tr("Show splash screen on startup (default: 1)") + "\n"; + " -splash " + tr("Show splash screen on startup (default: 1)") + "\n" + + " -choosedatadir " + tr("Choose data directory on startup (default: 0)") + "\n"; setWindowTitle(tr("Bitcoin-Qt")); setTextFormat(Qt::PlainText); diff --git a/src/qt/intro.cpp b/src/qt/intro.cpp new file mode 100644 index 0000000000..51f3c812e4 --- /dev/null +++ b/src/qt/intro.cpp @@ -0,0 +1,270 @@ +#include "intro.h" +#include "ui_intro.h" +#include "util.h" + +#include <QFileDialog> +#include <QSettings> +#include <QMessageBox> + +#include <boost/filesystem.hpp> + +/* Minimum free space (in bytes) needed for data directory */ +static const uint64 GB_BYTES = 1000000000LL; +static const uint64 BLOCK_CHAIN_SIZE = 10LL * GB_BYTES; + +/* Check free space asynchronously to prevent hanging the UI thread. + + Up to one request to check a path is in flight to this thread; when the check() + function runs, the current path is requested from the associated Intro object. + The reply is sent back through a signal. + + This ensures that no queue of checking requests is built up while the user is + still entering the path, and that always the most recently entered path is checked as + soon as the thread becomes available. +*/ +class FreespaceChecker : public QObject +{ + Q_OBJECT +public: + FreespaceChecker(Intro *intro); + + enum Status { + ST_OK, + ST_ERROR + }; + +public slots: + void check(); + +signals: + void reply(int status, const QString &message, quint64 available); + +private: + Intro *intro; +}; + +#include "intro.moc" + +FreespaceChecker::FreespaceChecker(Intro *intro) +{ + this->intro = intro; +} + +void FreespaceChecker::check() +{ + namespace fs = boost::filesystem; + QString dataDirStr = intro->getPathToCheck(); + fs::path dataDir = fs::path(dataDirStr.toStdString()); + uint64 freeBytesAvailable = 0; + int replyStatus = ST_OK; + QString replyMessage = tr("A new data directory will be created."); + + /* Find first parent that exists, so that fs::space does not fail */ + fs::path parentDir = dataDir; + while(parentDir.has_parent_path() && !fs::exists(parentDir)) + { + parentDir = parentDir.parent_path(); + } + + try { + freeBytesAvailable = fs::space(parentDir).available; + if(fs::exists(dataDir)) + { + if(fs::is_directory(dataDir)) + { + QString separator = QDir::toNativeSeparators("/"); + replyStatus = ST_OK; + replyMessage = tr("Directory already exists. Add <code>%1name</code> if you intend to create a new directory here.").arg(separator); + } else { + replyStatus = ST_ERROR; + replyMessage = tr("Path already exists, and is not a directory."); + } + } + } catch(fs::filesystem_error &e) + { + /* Parent directory does not exist or is not accessible */ + replyStatus = ST_ERROR; + replyMessage = tr("Cannot create data directory here."); + } + emit reply(replyStatus, replyMessage, freeBytesAvailable); +} + + +Intro::Intro(QWidget *parent) : + QDialog(parent), + ui(new Ui::Intro), + thread(0), + signalled(false) +{ + ui->setupUi(this); + ui->sizeWarningLabel->setText(ui->sizeWarningLabel->text().arg(BLOCK_CHAIN_SIZE/GB_BYTES)); + startThread(); +} + +Intro::~Intro() +{ + delete ui; + /* Ensure thread is finished before it is deleted */ + emit stopThread(); + thread->wait(); +} + +QString Intro::getDataDirectory() +{ + return ui->dataDirectory->text(); +} + +void Intro::setDataDirectory(const QString &dataDir) +{ + ui->dataDirectory->setText(dataDir); + if(dataDir == getDefaultDataDirectory()) + { + ui->dataDirDefault->setChecked(true); + ui->dataDirectory->setEnabled(false); + ui->ellipsisButton->setEnabled(false); + } else { + ui->dataDirCustom->setChecked(true); + ui->dataDirectory->setEnabled(true); + ui->ellipsisButton->setEnabled(true); + } +} + +QString Intro::getDefaultDataDirectory() +{ + return QString::fromStdString(GetDefaultDataDir().string()); +} + +void Intro::pickDataDirectory() +{ + namespace fs = boost::filesystem;; + QSettings settings; + /* If data directory provided on command line, no need to look at settings + or show a picking dialog */ + if(!GetArg("-datadir", "").empty()) + return; + /* 1) Default data directory for operating system */ + QString dataDir = getDefaultDataDirectory(); + /* 2) Allow QSettings to override default dir */ + dataDir = settings.value("strDataDir", dataDir).toString(); + + if(!fs::exists(dataDir.toStdString()) || GetBoolArg("-choosedatadir", false)) + { + /* If current default data directory does not exist, let the user choose one */ + Intro intro; + intro.setDataDirectory(dataDir); + while(true) + { + if(!intro.exec()) + { + /* Cancel clicked */ + exit(0); + } + dataDir = intro.getDataDirectory(); + try { + fs::create_directory(dataDir.toStdString()); + break; + } catch(fs::filesystem_error &e) { + QMessageBox::critical(0, QObject::tr("Bitcoin"), + QObject::tr("Error: Specified data directory \"%1\" can not be created.").arg(dataDir)); + /* fall through, back to choosing screen */ + } + } + + settings.setValue("strDataDir", dataDir); + } + SoftSetArg("-datadir", dataDir.toStdString()); +} + +void Intro::setStatus(int status, const QString &message, quint64 bytesAvailable) +{ + switch(status) + { + case FreespaceChecker::ST_OK: + ui->errorMessage->setText(message); + ui->errorMessage->setStyleSheet(""); + break; + case FreespaceChecker::ST_ERROR: + ui->errorMessage->setText(tr("Error") + ": " + message); + ui->errorMessage->setStyleSheet("QLabel { color: #800000 }"); + break; + } + /* Indicate number of bytes available */ + if(status == FreespaceChecker::ST_ERROR) + { + ui->freeSpace->setText(""); + } else { + QString freeString = QString::number(bytesAvailable/GB_BYTES) + tr("GB of free space available"); + if(bytesAvailable < BLOCK_CHAIN_SIZE) + { + freeString += " " + tr("(of %1GB needed)").arg(BLOCK_CHAIN_SIZE/GB_BYTES); + ui->freeSpace->setStyleSheet("QLabel { color: #800000 }"); + } else { + ui->freeSpace->setStyleSheet(""); + } + ui->freeSpace->setText(freeString+"."); + } + /* Don't allow confirm in ERROR state */ + ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(status != FreespaceChecker::ST_ERROR); +} + +void Intro::on_dataDirectory_textChanged(const QString &dataDirStr) +{ + /* Disable OK button until check result comes in */ + ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); + checkPath(dataDirStr); +} + +void Intro::on_ellipsisButton_clicked() +{ + QString dir = QFileDialog::getExistingDirectory(0, "Choose data directory", ui->dataDirectory->text()); + if(!dir.isEmpty()) + ui->dataDirectory->setText(dir); +} + +void Intro::on_dataDirDefault_clicked() +{ + setDataDirectory(getDefaultDataDirectory()); +} + +void Intro::on_dataDirCustom_clicked() +{ + ui->dataDirectory->setEnabled(true); + ui->ellipsisButton->setEnabled(true); +} + +void Intro::startThread() +{ + thread = new QThread(this); + FreespaceChecker *executor = new FreespaceChecker(this); + executor->moveToThread(thread); + + connect(executor, SIGNAL(reply(int,QString,quint64)), this, SLOT(setStatus(int,QString,quint64))); + connect(this, SIGNAL(requestCheck()), executor, SLOT(check())); + /* make sure executor object is deleted in its own thread */ + connect(this, SIGNAL(stopThread()), executor, SLOT(deleteLater())); + connect(this, SIGNAL(stopThread()), thread, SLOT(quit())); + + thread->start(); +} + +void Intro::checkPath(const QString &dataDir) +{ + mutex.lock(); + pathToCheck = dataDir; + if(!signalled) + { + signalled = true; + emit requestCheck(); + } + mutex.unlock(); +} + +QString Intro::getPathToCheck() +{ + QString retval; + mutex.lock(); + retval = pathToCheck; + signalled = false; /* new request can be queued now */ + mutex.unlock(); + return retval; +} diff --git a/src/qt/intro.h b/src/qt/intro.h new file mode 100644 index 0000000000..b246c65a82 --- /dev/null +++ b/src/qt/intro.h @@ -0,0 +1,67 @@ +#ifndef INTRO_H +#define INTRO_H + +#include <QDialog> +#include <QThread> +#include <QMutex> + +namespace Ui { +class Intro; +} +class FreespaceChecker; + +/** Introduction screen (pre-GUI startup). + Allows the user to choose a data directory, + in which the wallet and block chain will be stored. + */ +class Intro : public QDialog +{ + Q_OBJECT + +public: + explicit Intro(QWidget *parent = 0); + ~Intro(); + + QString getDataDirectory(); + void setDataDirectory(const QString &dataDir); + + /** + * Determine data directory. Let the user choose if the current one doesn't exist. + * + * @note do NOT call global GetDataDir() before calling this function, this + * will cause the wrong path to be cached. + */ + static void pickDataDirectory(); + + /** + * Determine default data directory for operating system. + */ + static QString getDefaultDataDirectory(); +signals: + void requestCheck(); + void stopThread(); + +public slots: + void setStatus(int status, const QString &message, quint64 bytesAvailable); + +private slots: + void on_dataDirectory_textChanged(const QString &arg1); + void on_ellipsisButton_clicked(); + void on_dataDirDefault_clicked(); + void on_dataDirCustom_clicked(); + +private: + Ui::Intro *ui; + QThread *thread; + QMutex mutex; + bool signalled; + QString pathToCheck; + + void startThread(); + void checkPath(const QString &dataDir); + QString getPathToCheck(); + + friend class FreespaceChecker; +}; + +#endif // INTRO_H |