LCOV - code coverage report
Current view: top level - src/qt - intro.cpp (source / functions) Hit Total Coverage
Test: total_coverage.info Lines: 0 131 0.0 %
Date: 2015-10-12 22:39:14 Functions: 0 21 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       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             : 
      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           0 : class FreespaceChecker : public QObject
      33             : {
      34           0 :     Q_OBJECT
      35             : 
      36             : public:
      37             :     FreespaceChecker(Intro *intro);
      38             : 
      39             :     enum Status {
      40             :         ST_OK,
      41             :         ST_ERROR
      42             :     };
      43             : 
      44             : public Q_SLOTS:
      45             :     void check();
      46             : 
      47             : Q_SIGNALS:
      48             :     void reply(int status, const QString &message, quint64 available);
      49             : 
      50             : private:
      51             :     Intro *intro;
      52             : };
      53             : 
      54             : #include "intro.moc"
      55             : 
      56           0 : FreespaceChecker::FreespaceChecker(Intro *intro)
      57             : {
      58           0 :     this->intro = intro;
      59           0 : }
      60             : 
      61           0 : void FreespaceChecker::check()
      62             : {
      63             :     namespace fs = boost::filesystem;
      64           0 :     QString dataDirStr = intro->getPathToCheck();
      65           0 :     fs::path dataDir = GUIUtil::qstringToBoostPath(dataDirStr);
      66           0 :     uint64_t freeBytesAvailable = 0;
      67           0 :     int replyStatus = ST_OK;
      68           0 :     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           0 :     while(parentDir.has_parent_path() && !fs::exists(parentDir))
      74             :     {
      75           0 :         parentDir = parentDir.parent_path();
      76             : 
      77             :         /* Check if we make any progress, break if not to prevent an infinite loop here */
      78           0 :         if (parentDirOld == parentDir)
      79             :             break;
      80             : 
      81             :         parentDirOld = parentDir;
      82             :     }
      83             : 
      84             :     try {
      85           0 :         freeBytesAvailable = fs::space(parentDir).available;
      86           0 :         if(fs::exists(dataDir))
      87             :         {
      88           0 :             if(fs::is_directory(dataDir))
      89             :             {
      90           0 :                 QString separator = "<code>" + QDir::toNativeSeparators("/") + tr("name") + "</code>";
      91           0 :                 replyStatus = ST_OK;
      92           0 :                 replyMessage = tr("Directory already exists. Add %1 if you intend to create a new directory here.").arg(separator);
      93             :             } else {
      94           0 :                 replyStatus = ST_ERROR;
      95           0 :                 replyMessage = tr("Path already exists, and is not a directory.");
      96             :             }
      97             :         }
      98           0 :     } catch (const fs::filesystem_error&)
      99             :     {
     100             :         /* Parent directory does not exist or is not accessible */
     101           0 :         replyStatus = ST_ERROR;
     102           0 :         replyMessage = tr("Cannot create data directory here.");
     103             :     }
     104           0 :     Q_EMIT reply(replyStatus, replyMessage, freeBytesAvailable);
     105           0 : }
     106             : 
     107             : 
     108           0 : Intro::Intro(QWidget *parent) :
     109             :     QDialog(parent),
     110           0 :     ui(new Ui::Intro),
     111             :     thread(0),
     112           0 :     signalled(false)
     113             : {
     114           0 :     ui->setupUi(this);
     115           0 :     ui->sizeWarningLabel->setText(ui->sizeWarningLabel->text().arg(BLOCK_CHAIN_SIZE/GB_BYTES));
     116           0 :     startThread();
     117           0 : }
     118             : 
     119           0 : Intro::~Intro()
     120             : {
     121           0 :     delete ui;
     122             :     /* Ensure thread is finished before it is deleted */
     123           0 :     Q_EMIT stopThread();
     124           0 :     thread->wait();
     125           0 : }
     126             : 
     127           0 : QString Intro::getDataDirectory()
     128             : {
     129           0 :     return ui->dataDirectory->text();
     130             : }
     131             : 
     132           0 : void Intro::setDataDirectory(const QString &dataDir)
     133             : {
     134           0 :     ui->dataDirectory->setText(dataDir);
     135           0 :     if(dataDir == getDefaultDataDirectory())
     136             :     {
     137           0 :         ui->dataDirDefault->setChecked(true);
     138           0 :         ui->dataDirectory->setEnabled(false);
     139           0 :         ui->ellipsisButton->setEnabled(false);
     140             :     } else {
     141           0 :         ui->dataDirCustom->setChecked(true);
     142           0 :         ui->dataDirectory->setEnabled(true);
     143           0 :         ui->ellipsisButton->setEnabled(true);
     144             :     }
     145           0 : }
     146             : 
     147           0 : QString Intro::getDefaultDataDirectory()
     148             : {
     149           0 :     return GUIUtil::boostPathToQString(GetDefaultDataDir());
     150             : }
     151             : 
     152           0 : void Intro::pickDataDirectory()
     153             : {
     154             :     namespace fs = boost::filesystem;
     155           0 :     QSettings settings;
     156             :     /* If data directory provided on command line, no need to look at settings
     157             :        or show a picking dialog */
     158           0 :     if(!GetArg("-datadir", "").empty())
     159           0 :         return;
     160             :     /* 1) Default data directory for operating system */
     161           0 :     QString dataDir = getDefaultDataDirectory();
     162             :     /* 2) Allow QSettings to override default dir */
     163           0 :     dataDir = settings.value("strDataDir", dataDir).toString();
     164             : 
     165           0 :     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           0 :         Intro intro;
     169           0 :         intro.setDataDirectory(dataDir);
     170           0 :         intro.setWindowIcon(QIcon(":icons/bitcoin"));
     171             : 
     172             :         while(true)
     173             :         {
     174           0 :             if(!intro.exec())
     175             :             {
     176             :                 /* Cancel clicked */
     177           0 :                 exit(0);
     178             :             }
     179           0 :             dataDir = intro.getDataDirectory();
     180             :             try {
     181           0 :                 TryCreateDirectory(GUIUtil::qstringToBoostPath(dataDir));
     182             :                 break;
     183           0 :             } catch (const fs::filesystem_error&) {
     184             :                 QMessageBox::critical(0, tr("Bitcoin Core"),
     185           0 :                     tr("Error: Specified data directory \"%1\" cannot be created.").arg(dataDir));
     186             :                 /* fall through, back to choosing screen */
     187             :             }
     188             :         }
     189             : 
     190           0 :         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           0 :     if(dataDir != getDefaultDataDirectory())
     197           0 :         SoftSetArg("-datadir", GUIUtil::qstringToBoostPath(dataDir).string()); // use OS locale for path setting
     198             : }
     199             : 
     200           0 : void Intro::setStatus(int status, const QString &message, quint64 bytesAvailable)
     201             : {
     202           0 :     switch(status)
     203             :     {
     204             :     case FreespaceChecker::ST_OK:
     205           0 :         ui->errorMessage->setText(message);
     206           0 :         ui->errorMessage->setStyleSheet("");
     207           0 :         break;
     208             :     case FreespaceChecker::ST_ERROR:
     209           0 :         ui->errorMessage->setText(tr("Error") + ": " + message);
     210           0 :         ui->errorMessage->setStyleSheet("QLabel { color: #800000 }");
     211           0 :         break;
     212             :     }
     213             :     /* Indicate number of bytes available */
     214           0 :     if(status == FreespaceChecker::ST_ERROR)
     215             :     {
     216           0 :         ui->freeSpace->setText("");
     217             :     } else {
     218           0 :         QString freeString = tr("%n GB of free space available", "", bytesAvailable/GB_BYTES);
     219           0 :         if(bytesAvailable < BLOCK_CHAIN_SIZE)
     220             :         {
     221           0 :             freeString += " " + tr("(of %n GB needed)", "", BLOCK_CHAIN_SIZE/GB_BYTES);
     222           0 :             ui->freeSpace->setStyleSheet("QLabel { color: #800000 }");
     223             :         } else {
     224           0 :             ui->freeSpace->setStyleSheet("");
     225             :         }
     226           0 :         ui->freeSpace->setText(freeString + ".");
     227             :     }
     228             :     /* Don't allow confirm in ERROR state */
     229           0 :     ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(status != FreespaceChecker::ST_ERROR);
     230           0 : }
     231             : 
     232           0 : void Intro::on_dataDirectory_textChanged(const QString &dataDirStr)
     233             : {
     234             :     /* Disable OK button until check result comes in */
     235           0 :     ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
     236           0 :     checkPath(dataDirStr);
     237           0 : }
     238             : 
     239           0 : void Intro::on_ellipsisButton_clicked()
     240             : {
     241           0 :     QString dir = QDir::toNativeSeparators(QFileDialog::getExistingDirectory(0, "Choose data directory", ui->dataDirectory->text()));
     242           0 :     if(!dir.isEmpty())
     243           0 :         ui->dataDirectory->setText(dir);
     244           0 : }
     245             : 
     246           0 : void Intro::on_dataDirDefault_clicked()
     247             : {
     248           0 :     setDataDirectory(getDefaultDataDirectory());
     249           0 : }
     250             : 
     251           0 : void Intro::on_dataDirCustom_clicked()
     252             : {
     253           0 :     ui->dataDirectory->setEnabled(true);
     254           0 :     ui->ellipsisButton->setEnabled(true);
     255           0 : }
     256             : 
     257           0 : void Intro::startThread()
     258             : {
     259           0 :     thread = new QThread(this);
     260           0 :     FreespaceChecker *executor = new FreespaceChecker(this);
     261           0 :     executor->moveToThread(thread);
     262             : 
     263           0 :     connect(executor, SIGNAL(reply(int,QString,quint64)), this, SLOT(setStatus(int,QString,quint64)));
     264           0 :     connect(this, SIGNAL(requestCheck()), executor, SLOT(check()));
     265             :     /*  make sure executor object is deleted in its own thread */
     266           0 :     connect(this, SIGNAL(stopThread()), executor, SLOT(deleteLater()));
     267           0 :     connect(this, SIGNAL(stopThread()), thread, SLOT(quit()));
     268             : 
     269           0 :     thread->start();
     270           0 : }
     271             : 
     272           0 : void Intro::checkPath(const QString &dataDir)
     273             : {
     274           0 :     mutex.lock();
     275           0 :     pathToCheck = dataDir;
     276           0 :     if(!signalled)
     277             :     {
     278           0 :         signalled = true;
     279           0 :         Q_EMIT requestCheck();
     280             :     }
     281           0 :     mutex.unlock();
     282           0 : }
     283             : 
     284           0 : QString Intro::getPathToCheck()
     285             : {
     286             :     QString retval;
     287           0 :     mutex.lock();
     288           0 :     retval = pathToCheck;
     289           0 :     signalled = false; /* new request can be queued now */
     290           0 :     mutex.unlock();
     291           0 :     return retval;
     292           0 : }

Generated by: LCOV version 1.11