2ba0ecf428
Migration from boost to std-library equivalents and removal of unreachable code paths. Touches infrastructure only — no consensus rule or wallet serialization changes. Dead code removed: - IRC bootstrap (irc.cpp/h, 417 lines): orphan from pre-Tor era, no callers. - Alert system (alert.cpp/h + sendalert RPC + Qt UI signal, ~500 lines): retired post-V5 fork; old peers' alert messages now hit the unknown-cmd default branch, logged + ignored. - Legacy P2P handlers in main.cpp: "checkpoint" (already a no-op stub since V5 fork master-key removal), "checkorder"/"reply" (2010-era Receive-by-IP feature), plus their unused supporting structures (CRequestTracker, PushRequest overloads, mapRequests/cs_mapRequests, mapReuseKey). - Unreachable RPCs clearwallettransactions and scanforalltxns (~175 lines): defined in rpcwallet.cpp but never registered in the dispatch table. - Stale -alertnotify CLI help text (option was advertised but never wired). boost::filesystem -> std::filesystem (C++17): - 30 source files, 5 headers. namespace fs = boost::filesystem swapped to namespace fs = std::filesystem; boost::filesystem::ifstream/ofstream replaced with std::ifstream/ofstream (path-aware in C++17); fs::system_complete -> fs::absolute; boost::filesystem::filesystem_error -> std::filesystem::filesystem_error. - Build system: dropped Boost::filesystem from link libs and Boost components; PCH includes updated. - Added explicit <filesystem> includes where types were previously available only transitively (db.h, rpcblockchain.cpp). boost::thread -> std::thread (12 files): - sync.h CCriticalSection/CWaitableCriticalSection now alias std::recursive_mutex/std::mutex. boost::unique_lock and boost::condition_variable / boost::mutex::scoped_lock swapped to std equivalents; sync.cpp boost::thread_specific_ptr -> thread_local std::unique_ptr. - init.cpp boost::thread_group rewritten as std::vector<std::thread> with manual join loop. boost::thread::hardware_concurrency -> std::thread::hardware_concurrency. - main.cpp/wallet.cpp -blocknotify/-walletnotify shell-out threads now use std::thread(...).detach() — fixes a latent bug where modern boost::thread destructor would call std::terminate on the joinable thread. - util.cpp NewThread now catches std::system_error. - No interruption_point/interrupt usage anywhere — pure mechanical swap. boost::chrono / boost::posix_time -> std::chrono (3 of 5 files): - util.h: MilliSleep, GetTimeMillis, GetTimeMicros rewritten on std::chrono (system_clock for epoch math, sleep_for for delays). - snapshotnet.cpp: sleep_for swapped. - DoS_tests.cpp: timing harness uses steady_clock. - Skipped: rpcdump.cpp (boost::posix_time::time_input_facet has no clean std::get_time equivalent) and qt/qtipcserver.cpp (locked to boost::posix_time by boost::interprocess::message_queue::timed_receive). Other housekeeping: - Dropped unnecessary "using namespace boost;" from txdb-leveldb.cpp, txdb-rocksdb.cpp, walletdb.cpp, db.cpp (verified no unqualified boost names in those TUs). - Removed unused extern declaration for clearwallettransactions. Build fixes for non-unity builds on MinGW64/GCC 15: - net.cpp: dropped stale #include "irc.h". - addrman.cpp + main.cpp: explicit <cmath> include for sqrt/pow (was arriving transitively via boost headers). - rpcblockchain.cpp + init.cpp: defensive #undef STRICT/ADVISORY/PERMISSIVE since windows.h macros collide with the Checkpoints:: enum values when std headers reorder include flow. - tor_embed_hooks.cpp: triangles_tor_check_interrupted now polls fShutdown instead of boost::this_thread::interruption_requested (we never used boost interruption — the hook was always effectively a no-op). - snapshotnet.cpp: fs::remove error handle uses std::error_code. - serialize.h: added <ios> for std::ios::badbit/failbit (was relying on transitive include via boost). Note: unity builds currently fail on this branch due to std::byte (C++17) colliding with COM 'byte' typedef from shlobj.h when 'using namespace std;' from earlier files in the unity slice leaks into util.cpp's parse of shlobj.h. Build with -DENABLE_UNITY_BUILD=OFF (the default). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
508 lines
18 KiB
C++
508 lines
18 KiB
C++
#include "optionsdialog.h"
|
|
#include "ui_optionsdialog.h"
|
|
|
|
#include "trianglesunits.h"
|
|
#include "monitoreddatamapper.h"
|
|
#include "netbase.h"
|
|
#include "optionsmodel.h"
|
|
#include "dialog_move_handler.h"
|
|
|
|
#include "init.h"
|
|
#include "util.h"
|
|
|
|
#include <filesystem>
|
|
|
|
#include <QDir>
|
|
#include <QFileDialog>
|
|
#include <QGroupBox>
|
|
#include <QIntValidator>
|
|
#include <QLocale>
|
|
#include <QMessageBox>
|
|
#include <QProcess>
|
|
#include <QRegExp>
|
|
#include <QRegExpValidator>
|
|
#include <QSettings>
|
|
|
|
OptionsDialog::OptionsDialog(QWidget *parent) :
|
|
QDialog(parent),
|
|
ui(new Ui::OptionsDialog),
|
|
model(0),
|
|
mapper(0),
|
|
fRestartWarningDisplayed_Proxy(false),
|
|
fRestartWarningDisplayed_Lang(false),
|
|
fProxyIpValid(true),
|
|
dataDirPath(0),
|
|
dataDirFreeSpaceLabel(0)
|
|
{
|
|
ui->setupUi(this);
|
|
setWindowFlags(Qt::CustomizeWindowHint | Qt::FramelessWindowHint | Qt::Window);
|
|
ui->wCaption->installEventFilter(new DialogMoveHandler(this));
|
|
|
|
/* Data Directory section in Main tab */
|
|
m_currentDataDir = QString::fromStdString(GetDataDir(false).string());
|
|
m_pendingDataDir.clear();
|
|
|
|
QGroupBox *groupDataDir = new QGroupBox(tr("Data Directory"), this);
|
|
groupDataDir->setStyleSheet(
|
|
"QGroupBox { border: 1px solid #61280E; margin-top: 8px; padding-top: 16px; color: #f26522; }"
|
|
"QGroupBox::title { subcontrol-origin: margin; left: 10px; padding: 0 3px; }");
|
|
|
|
QVBoxLayout *dataDirLayout = new QVBoxLayout(groupDataDir);
|
|
|
|
QHBoxLayout *dataDirPathLayout = new QHBoxLayout();
|
|
dataDirPath = new QLineEdit(m_currentDataDir, groupDataDir);
|
|
dataDirPath->setReadOnly(true);
|
|
dataDirPath->setStyleSheet("QLineEdit { background-color: #1c1c1c; border: 1px solid #f26522; color: #f26522; padding: 2px; }");
|
|
|
|
QPushButton *dataDirBrowseButton = new QPushButton(tr("Browse..."), groupDataDir);
|
|
dataDirBrowseButton->setStyleSheet(
|
|
"QPushButton { background-color: #000; color: #f26522; border: 1px solid #f26522; padding: 2px 12px; min-height: 20px; }"
|
|
"QPushButton:hover { background-color: #61280E; }"
|
|
"QPushButton:pressed:flat { color: #000; background-color: #f26522; }");
|
|
|
|
dataDirPathLayout->addWidget(dataDirPath);
|
|
dataDirPathLayout->addWidget(dataDirBrowseButton);
|
|
dataDirLayout->addLayout(dataDirPathLayout);
|
|
|
|
dataDirFreeSpaceLabel = new QLabel(groupDataDir);
|
|
dataDirFreeSpaceLabel->setStyleSheet("color: #999; font-size: 11px;");
|
|
dataDirLayout->addWidget(dataDirFreeSpaceLabel);
|
|
|
|
// Insert into Main tab layout, before the vertical spacer (last item)
|
|
QVBoxLayout *mainTabLayout = qobject_cast<QVBoxLayout*>(ui->tabWidget->widget(0)->layout());
|
|
if (mainTabLayout) {
|
|
int spacerIndex = mainTabLayout->count() - 1; // vertical spacer is last
|
|
mainTabLayout->insertWidget(spacerIndex, groupDataDir);
|
|
}
|
|
|
|
connect(dataDirBrowseButton, SIGNAL(clicked()), this, SLOT(on_dataDirBrowseButton_clicked()));
|
|
updateDataDirFreeSpace();
|
|
|
|
/* Network elements init */
|
|
#ifndef USE_UPNP
|
|
ui->mapPortUpnp->setEnabled(false);
|
|
#endif
|
|
|
|
ui->proxyIp->setEnabled(false);
|
|
ui->proxyPort->setEnabled(false);
|
|
ui->proxyPort->setValidator(new QIntValidator(1, 65535, this));
|
|
ui->connectSocks->setText(tr("&Use Tor (SOCKS5):"));
|
|
|
|
ui->socksVersion->setEnabled(false);
|
|
ui->socksVersion->addItem("5", 5);
|
|
ui->socksVersion->addItem("4", 4);
|
|
ui->socksVersion->setCurrentIndex(0);
|
|
|
|
connect(ui->connectSocks, SIGNAL(toggled(bool)), ui->proxyIp, SLOT(setEnabled(bool)));
|
|
connect(ui->connectSocks, SIGNAL(toggled(bool)), ui->proxyPort, SLOT(setEnabled(bool)));
|
|
connect(ui->connectSocks, SIGNAL(toggled(bool)), ui->socksVersion, SLOT(setEnabled(bool)));
|
|
connect(ui->connectSocks, SIGNAL(clicked(bool)), this, SLOT(showRestartWarning_Proxy()));
|
|
connect(ui->connectSocks, SIGNAL(toggled(bool)), this, SLOT(applyTorDefaults(bool)));
|
|
|
|
ui->proxyIp->installEventFilter(this);
|
|
|
|
/* Window elements init */
|
|
#ifdef Q_OS_MAC
|
|
ui->tabWindow->setVisible(false);
|
|
#endif
|
|
|
|
/* Display elements init */
|
|
QDir translations(":translations");
|
|
ui->lang->addItem(QString("(") + tr("default") + QString(")"), QVariant(""));
|
|
foreach(const QString &langStr, translations.entryList())
|
|
{
|
|
QLocale locale(langStr);
|
|
|
|
/** check if the locale name consists of 2 parts (language_country) */
|
|
if(langStr.contains("_"))
|
|
{
|
|
#if QT_VERSION >= 0x040800
|
|
/** display language strings as "native language - native country (locale name)", e.g. "Deutsch - Deutschland (de)" */
|
|
ui->lang->addItem(locale.nativeLanguageName() + QString(" - ") + locale.nativeCountryName() + QString(" (") + langStr + QString(")"), QVariant(langStr));
|
|
#else
|
|
/** display language strings as "language - country (locale name)", e.g. "German - Germany (de)" */
|
|
ui->lang->addItem(QLocale::languageToString(locale.language()) + QString(" - ") + QLocale::countryToString(locale.country()) + QString(" (") + langStr + QString(")"), QVariant(langStr));
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
#if QT_VERSION >= 0x040800
|
|
/** display language strings as "native language (locale name)", e.g. "Deutsch (de)" */
|
|
ui->lang->addItem(locale.nativeLanguageName() + QString(" (") + langStr + QString(")"), QVariant(langStr));
|
|
#else
|
|
/** display language strings as "language (locale name)", e.g. "German (de)" */
|
|
ui->lang->addItem(QLocale::languageToString(locale.language()) + QString(" (") + langStr + QString(")"), QVariant(langStr));
|
|
#endif
|
|
}
|
|
}
|
|
|
|
ui->unit->setModel(new TrianglesUnits(this));
|
|
|
|
/* Widget-to-option mapper */
|
|
mapper = new MonitoredDataMapper(this);
|
|
mapper->setSubmitPolicy(QDataWidgetMapper::ManualSubmit);
|
|
mapper->setOrientation(Qt::Vertical);
|
|
|
|
/* enable apply button when data modified */
|
|
connect(mapper, SIGNAL(viewModified()), this, SLOT(enableApplyButton()));
|
|
/* disable apply button when new data loaded */
|
|
connect(mapper, SIGNAL(currentIndexChanged(int)), this, SLOT(disableApplyButton()));
|
|
/* setup/change UI elements when proxy IP is invalid/valid */
|
|
connect(this, SIGNAL(proxyIpValid(QValidatedLineEdit *, bool)), this, SLOT(handleProxyIpValid(QValidatedLineEdit *, bool)));
|
|
}
|
|
|
|
OptionsDialog::~OptionsDialog()
|
|
{
|
|
delete ui;
|
|
}
|
|
|
|
void OptionsDialog::setModel(OptionsModel *model)
|
|
{
|
|
this->model = model;
|
|
|
|
if(model)
|
|
{
|
|
connect(model, SIGNAL(displayUnitChanged(int)), this, SLOT(updateDisplayUnit()));
|
|
|
|
mapper->setModel(model);
|
|
setMapper();
|
|
mapper->toFirst();
|
|
}
|
|
|
|
/* update the display unit, to not use the default ("TRI") */
|
|
updateDisplayUnit();
|
|
|
|
/* warn only when language selection changes by user action (placed here so init via mapper doesn't trigger this) */
|
|
connect(ui->lang, SIGNAL(valueChanged()), this, SLOT(showRestartWarning_Lang()));
|
|
|
|
/* disable apply button after settings are loaded as there is nothing to save */
|
|
disableApplyButton();
|
|
}
|
|
|
|
void OptionsDialog::setMapper()
|
|
{
|
|
/* Main */
|
|
mapper->addMapping(ui->transactionFee, OptionsModel::Fee);
|
|
mapper->addMapping(ui->reserveBalance, OptionsModel::ReserveBalance);
|
|
mapper->addMapping(ui->trianglesAtStartup, OptionsModel::StartAtStartup);
|
|
mapper->addMapping(ui->detachDatabases, OptionsModel::DetachDatabases);
|
|
|
|
/* Network */
|
|
mapper->addMapping(ui->mapPortUpnp, OptionsModel::MapPortUPnP);
|
|
|
|
mapper->addMapping(ui->connectSocks, OptionsModel::ProxyUse);
|
|
mapper->addMapping(ui->proxyIp, OptionsModel::ProxyIP);
|
|
mapper->addMapping(ui->proxyPort, OptionsModel::ProxyPort);
|
|
mapper->addMapping(ui->socksVersion, OptionsModel::ProxySocksVersion);
|
|
|
|
/* Window */
|
|
#ifndef Q_OS_MAC
|
|
mapper->addMapping(ui->minimizeToTray, OptionsModel::MinimizeToTray);
|
|
mapper->addMapping(ui->minimizeOnClose, OptionsModel::MinimizeOnClose);
|
|
#endif
|
|
|
|
/* Display */
|
|
mapper->addMapping(ui->lang, OptionsModel::Language);
|
|
mapper->addMapping(ui->unit, OptionsModel::DisplayUnit);
|
|
mapper->addMapping(ui->displayAddresses, OptionsModel::DisplayAddresses);
|
|
mapper->addMapping(ui->coinControlFeatures, OptionsModel::CoinControlFeatures);
|
|
mapper->addMapping(ui->showOnionAddress, OptionsModel::ShowOnionAddress);
|
|
}
|
|
|
|
void OptionsDialog::enableApplyButton()
|
|
{
|
|
ui->applyButton->setEnabled(true);
|
|
}
|
|
|
|
void OptionsDialog::disableApplyButton()
|
|
{
|
|
ui->applyButton->setEnabled(false);
|
|
}
|
|
|
|
void OptionsDialog::enableSaveButtons()
|
|
{
|
|
/* prevent enabling of the save buttons when data modified, if there is an invalid proxy address present */
|
|
if(fProxyIpValid)
|
|
setSaveButtonState(true);
|
|
}
|
|
|
|
void OptionsDialog::disableSaveButtons()
|
|
{
|
|
setSaveButtonState(false);
|
|
}
|
|
|
|
void OptionsDialog::setSaveButtonState(bool fState)
|
|
{
|
|
ui->applyButton->setEnabled(fState);
|
|
ui->okButton->setEnabled(fState);
|
|
}
|
|
|
|
void OptionsDialog::on_okButton_clicked()
|
|
{
|
|
mapper->submit();
|
|
if (handleDataDirChange())
|
|
return; // restart flow handles closing
|
|
accept();
|
|
}
|
|
|
|
void OptionsDialog::on_cancelButton_clicked()
|
|
{
|
|
reject();
|
|
}
|
|
|
|
void OptionsDialog::on_applyButton_clicked()
|
|
{
|
|
mapper->submit();
|
|
handleDataDirChange();
|
|
disableApplyButton();
|
|
}
|
|
|
|
void OptionsDialog::showRestartWarning_Proxy()
|
|
{
|
|
if(!fRestartWarningDisplayed_Proxy)
|
|
{
|
|
QMessageBox* msgBox = new QMessageBox(QMessageBox::Warning,
|
|
tr("Warning"),
|
|
tr("This setting will take effect after restarting Triangles!\n" "You shouldn't tamper with proxy settings unless you know exactly what you are doing!"),
|
|
QMessageBox::Ok, this,
|
|
Qt::FramelessWindowHint);
|
|
|
|
msgBox->setIconPixmap(QPixmap(":/msgbox/warning"));
|
|
msgBox->setStyleSheet("QMessageBox { border: 2px solid #e22104;}");
|
|
msgBox->button(QMessageBox::Ok)->setStyleSheet("\
|
|
QMessageBox QPushButton {background-color: #000;color: #f26522;border: 1px solid #f26522;\
|
|
min-width: 120px;max-width: 120px;max-height: 20px;min-height: 20px;}\
|
|
QMessageBox QPushButton:hover {background-color: #61280E;}\
|
|
QMessageBox QPushButton:pressed:flat {color: #000;background-color: #f26522;}\
|
|
");
|
|
msgBox->exec();
|
|
|
|
fRestartWarningDisplayed_Proxy = true;
|
|
}
|
|
}
|
|
|
|
void OptionsDialog::showRestartWarning_Lang()
|
|
{
|
|
if(!fRestartWarningDisplayed_Lang)
|
|
{
|
|
QMessageBox* msgBox = new QMessageBox(QMessageBox::Warning,
|
|
tr("Warning"),
|
|
tr("This setting will take effect after restarting Triangles!"),
|
|
QMessageBox::Ok, this,
|
|
Qt::FramelessWindowHint);
|
|
|
|
|
|
msgBox->setIconPixmap(QPixmap(":/msgbox/warning"));
|
|
msgBox->setStyleSheet("QMessageBox { border: 2px solid #e22104;}");
|
|
msgBox->button(QMessageBox::Ok)->setStyleSheet("\
|
|
QMessageBox QPushButton {background-color: #000;color: #f26522;border: 1px solid #f26522;\
|
|
min-width: 120px;max-width: 120px;max-height: 20px;min-height: 20px;}\
|
|
QMessageBox QPushButton:hover {background-color: #61280E;}\
|
|
QMessageBox QPushButton:pressed:flat {color: #000;background-color: #f26522;}\
|
|
");
|
|
msgBox->exec();
|
|
|
|
fRestartWarningDisplayed_Lang = true;
|
|
}
|
|
}
|
|
|
|
void OptionsDialog::updateDisplayUnit()
|
|
{
|
|
if(model)
|
|
{
|
|
/* Update transactionFee with the current unit */
|
|
ui->transactionFee->setDisplayUnit(model->getDisplayUnit());
|
|
}
|
|
}
|
|
|
|
void OptionsDialog::handleProxyIpValid(QValidatedLineEdit *object, bool fState)
|
|
{
|
|
// this is used in a check before re-enabling the save buttons
|
|
fProxyIpValid = fState;
|
|
|
|
if(fProxyIpValid)
|
|
{
|
|
enableSaveButtons();
|
|
ui->statusLabel->clear();
|
|
}
|
|
else
|
|
{
|
|
disableSaveButtons();
|
|
object->setValid(fProxyIpValid);
|
|
ui->statusLabel->setStyleSheet("QLabel { color: red; }");
|
|
ui->statusLabel->setText(tr("The supplied proxy address is invalid."));
|
|
}
|
|
}
|
|
|
|
void OptionsDialog::applyTorDefaults(bool enabled)
|
|
{
|
|
if (!enabled)
|
|
return;
|
|
|
|
// One-click Tor mode: populate standard local Tor SOCKS settings.
|
|
ui->proxyIp->setText("127.0.0.1");
|
|
ui->proxyPort->setText("9050");
|
|
ui->socksVersion->setCurrentIndex(ui->socksVersion->findData(5));
|
|
}
|
|
|
|
bool OptionsDialog::eventFilter(QObject *object, QEvent *event)
|
|
{
|
|
if(event->type() == QEvent::FocusOut)
|
|
{
|
|
if(object == ui->proxyIp)
|
|
{
|
|
CService addr;
|
|
/* Check proxyIp for a valid IPv4/IPv6 address and emit the proxyIpValid signal */
|
|
emit proxyIpValid(ui->proxyIp, LookupNumeric(ui->proxyIp->text().toStdString().c_str(), addr));
|
|
}
|
|
}
|
|
return QDialog::eventFilter(object, event);
|
|
}
|
|
|
|
void OptionsDialog::on_dataDirBrowseButton_clicked()
|
|
{
|
|
QString dir = QFileDialog::getExistingDirectory(
|
|
this, tr("Choose data directory"), m_currentDataDir);
|
|
if (!dir.isEmpty() && dir != m_currentDataDir)
|
|
{
|
|
m_pendingDataDir = dir;
|
|
dataDirPath->setText(dir);
|
|
updateDataDirFreeSpace();
|
|
enableApplyButton();
|
|
}
|
|
}
|
|
|
|
void OptionsDialog::updateDataDirFreeSpace()
|
|
{
|
|
namespace fs = std::filesystem;
|
|
QString path = dataDirPath->text();
|
|
fs::path fsPath(path.toStdString());
|
|
try {
|
|
while (!fsPath.empty() && !fs::exists(fsPath))
|
|
fsPath = fsPath.parent_path();
|
|
if (!fsPath.empty()) {
|
|
fs::space_info si = fs::space(fsPath);
|
|
double freeGB = (double)si.available / (1024.0 * 1024.0 * 1024.0);
|
|
dataDirFreeSpaceLabel->setText(
|
|
tr("Free space: %1 GB").arg(QString::number(freeGB, 'f', 2)));
|
|
} else {
|
|
dataDirFreeSpaceLabel->setText(tr("Cannot determine free space"));
|
|
}
|
|
} catch (const fs::filesystem_error &) {
|
|
dataDirFreeSpaceLabel->setText(tr("Cannot determine free space"));
|
|
}
|
|
}
|
|
|
|
quint64 OptionsDialog::calculateDirSize(const QString& path)
|
|
{
|
|
namespace fs = std::filesystem;
|
|
quint64 totalSize = 0;
|
|
try {
|
|
for (fs::recursive_directory_iterator it(path.toStdString()), end; it != end; ++it) {
|
|
if (fs::is_regular_file(*it))
|
|
totalSize += fs::file_size(*it);
|
|
}
|
|
} catch (...) {}
|
|
return totalSize;
|
|
}
|
|
|
|
bool OptionsDialog::handleDataDirChange()
|
|
{
|
|
if (m_pendingDataDir.isEmpty() || m_pendingDataDir == m_currentDataDir)
|
|
return false;
|
|
|
|
namespace fs = std::filesystem;
|
|
fs::path destPath(m_pendingDataDir.toStdString());
|
|
|
|
// Check destination is writable
|
|
try {
|
|
fs::create_directories(destPath);
|
|
} catch (const fs::filesystem_error& e) {
|
|
QMessageBox::critical(this, tr("Error"),
|
|
tr("Cannot create directory: %1").arg(QString::fromStdString(e.what())));
|
|
m_pendingDataDir.clear();
|
|
dataDirPath->setText(m_currentDataDir);
|
|
updateDataDirFreeSpace();
|
|
return false;
|
|
}
|
|
|
|
// Check free space vs current data dir size
|
|
quint64 dataDirSize = calculateDirSize(m_currentDataDir);
|
|
try {
|
|
fs::space_info si = fs::space(destPath);
|
|
quint64 required = dataDirSize + (dataDirSize / 10); // 10% headroom
|
|
if (si.available < required) {
|
|
QMessageBox::critical(this, tr("Insufficient Space"),
|
|
tr("The destination has %1 MB free but the data directory requires approximately %2 MB.")
|
|
.arg(si.available / (1024*1024))
|
|
.arg(required / (1024*1024)));
|
|
m_pendingDataDir.clear();
|
|
dataDirPath->setText(m_currentDataDir);
|
|
updateDataDirFreeSpace();
|
|
return false;
|
|
}
|
|
} catch (const fs::filesystem_error&) {
|
|
// If we can't check space, proceed anyway
|
|
}
|
|
|
|
// Save migration state to QSettings
|
|
QSettings settings;
|
|
settings.setValue("strDataDirPrevious", m_currentDataDir);
|
|
settings.setValue("strDataDir", m_pendingDataDir);
|
|
settings.setValue("fPendingDataDirMigration", true);
|
|
|
|
// Ask about restart
|
|
QMessageBox msgBox(this);
|
|
msgBox.setWindowFlags(Qt::FramelessWindowHint);
|
|
msgBox.setWindowTitle(tr("Data Directory Changed"));
|
|
msgBox.setText(tr("The data directory will be moved from:\n%1\n\nTo:\n%2\n\n"
|
|
"This will happen when the wallet restarts.")
|
|
.arg(m_currentDataDir).arg(m_pendingDataDir));
|
|
msgBox.setIcon(QMessageBox::Information);
|
|
msgBox.setIconPixmap(QPixmap(":/msgbox/information"));
|
|
msgBox.setStyleSheet("QMessageBox { border: 2px solid #f26522; background-color: #000; color: #f26522; }");
|
|
|
|
QPushButton *restartBtn = msgBox.addButton(tr("Restart Now"), QMessageBox::AcceptRole);
|
|
QPushButton *laterBtn = msgBox.addButton(tr("Later"), QMessageBox::RejectRole);
|
|
|
|
QString btnStyle =
|
|
"QPushButton { background-color: #000; color: #f26522; border: 1px solid #f26522; "
|
|
"min-width: 120px; max-width: 120px; max-height: 20px; min-height: 20px; }"
|
|
"QPushButton:hover { background-color: #61280E; }"
|
|
"QPushButton:pressed:flat { color: #000; background-color: #f26522; }";
|
|
restartBtn->setStyleSheet(btnStyle);
|
|
laterBtn->setStyleSheet(btnStyle);
|
|
|
|
msgBox.exec();
|
|
|
|
if (msgBox.clickedButton() == restartBtn) {
|
|
performRestart();
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void OptionsDialog::performRestart()
|
|
{
|
|
// Launch a new instance of ourselves
|
|
QString exePath = QApplication::applicationFilePath();
|
|
QStringList args = QApplication::arguments();
|
|
args.removeFirst(); // remove argv[0]
|
|
|
|
// Remove any existing -datadir argument so the new instance
|
|
// reads strDataDir from QSettings and performs migration
|
|
QMutableStringListIterator it(args);
|
|
while (it.hasNext()) {
|
|
QString arg = it.next();
|
|
if (arg.startsWith("-datadir") || arg.startsWith("/datadir"))
|
|
it.remove();
|
|
}
|
|
|
|
// Start new process detached so it survives our shutdown
|
|
QProcess::startDetached(exePath, args);
|
|
|
|
// Close dialog and trigger wallet shutdown
|
|
accept();
|
|
StartShutdown();
|
|
}
|