diff options
Diffstat (limited to 'src/qt')
49 files changed, 743 insertions, 702 deletions
diff --git a/src/qt/addressbookpage.cpp b/src/qt/addressbookpage.cpp index d59a4345f3..a82bd5f73e 100644 --- a/src/qt/addressbookpage.cpp +++ b/src/qt/addressbookpage.cpp @@ -19,6 +19,11 @@ #include <QMenu> #include <QMessageBox> #include <QSortFilterProxyModel> +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) +#include <QRegularExpression> +#else +#include <QRegExp> +#endif class AddressBookSortFilterProxyModel final : public QSortFilterProxyModel { @@ -46,12 +51,13 @@ protected: auto address = model->index(row, AddressTableModel::Address, parent); - if (filterRegExp().indexIn(model->data(address).toString()) < 0 && - filterRegExp().indexIn(model->data(label).toString()) < 0) { - return false; - } - - return true; +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) + const auto pattern = filterRegularExpression(); +#else + const auto pattern = filterRegExp(); +#endif + return (model->data(address).toString().contains(pattern) || + model->data(label).toString().contains(pattern)); } }; diff --git a/src/qt/addresstablemodel.cpp b/src/qt/addresstablemodel.cpp index dcab631d98..27ee9509e6 100644 --- a/src/qt/addresstablemodel.cpp +++ b/src/qt/addresstablemodel.cpp @@ -30,7 +30,7 @@ struct AddressTableEntry QString label; QString address; - AddressTableEntry() {} + AddressTableEntry() = default; AddressTableEntry(Type _type, const QString &_label, const QString &_address): type(_type), label(_label), address(_address) {} }; diff --git a/src/qt/bantablemodel.cpp b/src/qt/bantablemodel.cpp index e004fba308..3d0be69302 100644 --- a/src/qt/bantablemodel.cpp +++ b/src/qt/bantablemodel.cpp @@ -89,10 +89,7 @@ BanTableModel::BanTableModel(interfaces::Node& node, QObject* parent) : refresh(); } -BanTableModel::~BanTableModel() -{ - // Intentionally left empty -} +BanTableModel::~BanTableModel() = default; int BanTableModel::rowCount(const QModelIndex &parent) const { diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index c6b884e40a..53fbac0106 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -95,6 +95,8 @@ static void RegisterMetaTypes() qRegisterMetaType<std::function<void()>>("std::function<void()>"); qRegisterMetaType<QMessageBox::Icon>("QMessageBox::Icon"); qRegisterMetaType<interfaces::BlockAndHeaderTipInfo>("interfaces::BlockAndHeaderTipInfo"); + + qRegisterMetaTypeStreamOperators<BitcoinUnit>("BitcoinUnit"); } static QString GetLangTerritory() @@ -259,7 +261,7 @@ void BitcoinApplication::createPaymentServer() void BitcoinApplication::createOptionsModel(bool resetSettings) { - optionsModel = new OptionsModel(this, resetSettings); + optionsModel = new OptionsModel(node(), this, resetSettings); } void BitcoinApplication::createWindow(const NetworkStyle *networkStyle) @@ -290,7 +292,6 @@ void BitcoinApplication::createNode(interfaces::Init& init) { assert(!m_node); m_node = init.makeNode(); - if (optionsModel) optionsModel->setNode(*m_node); if (m_splash) m_splash->setNode(*m_node); } @@ -306,7 +307,9 @@ void BitcoinApplication::startThread() /* communication to and from thread */ connect(&m_executor.value(), &InitExecutor::initializeResult, this, &BitcoinApplication::initializeResult); - connect(&m_executor.value(), &InitExecutor::shutdownResult, this, &QCoreApplication::quit); + connect(&m_executor.value(), &InitExecutor::shutdownResult, this, [] { + QCoreApplication::exit(0); + }); connect(&m_executor.value(), &InitExecutor::runawayException, this, &BitcoinApplication::handleRunawayException); connect(this, &BitcoinApplication::requestedInitialize, &m_executor.value(), &InitExecutor::initialize); connect(this, &BitcoinApplication::requestedShutdown, &m_executor.value(), &InitExecutor::shutdown); @@ -631,6 +634,12 @@ int GuiMain(int argc, char* argv[]) // Allow parameter interaction before we create the options model app.parameterSetup(); GUIUtil::LogQtInfo(); + + if (gArgs.GetBoolArg("-splash", DEFAULT_SPLASHSCREEN) && !gArgs.GetBoolArg("-min", false)) + app.createSplashScreen(networkStyle.data()); + + app.createNode(*init); + // Load GUI settings from QSettings app.createOptionsModel(gArgs.GetBoolArg("-resetguisettings", false)); @@ -639,11 +648,6 @@ int GuiMain(int argc, char* argv[]) app.InitPruneSetting(prune_MiB); } - if (gArgs.GetBoolArg("-splash", DEFAULT_SPLASHSCREEN) && !gArgs.GetBoolArg("-min", false)) - app.createSplashScreen(networkStyle.data()); - - app.createNode(*init); - int rv = EXIT_SUCCESS; try { diff --git a/src/qt/bitcoinamountfield.cpp b/src/qt/bitcoinamountfield.cpp index a257e250e0..c92aecd095 100644 --- a/src/qt/bitcoinamountfield.cpp +++ b/src/qt/bitcoinamountfield.cpp @@ -14,6 +14,9 @@ #include <QHBoxLayout> #include <QKeyEvent> #include <QLineEdit> +#include <QVariant> + +#include <cassert> /** QSpinBox that uses fixed-point numbers internally and uses our own * formatting/parsing functions. @@ -96,7 +99,7 @@ public: setValue(val); } - void setDisplayUnit(int unit) + void setDisplayUnit(BitcoinUnit unit) { bool valid = false; CAmount val = value(&valid); @@ -122,7 +125,7 @@ public: const QFontMetrics fm(fontMetrics()); int h = lineEdit()->minimumSizeHint().height(); - int w = GUIUtil::TextWidth(fm, BitcoinUnits::format(BitcoinUnits::BTC, BitcoinUnits::maxMoney(), false, BitcoinUnits::SeparatorStyle::ALWAYS)); + int w = GUIUtil::TextWidth(fm, BitcoinUnits::format(BitcoinUnit::BTC, BitcoinUnits::maxMoney(), false, BitcoinUnits::SeparatorStyle::ALWAYS)); w += 2; // cursor blinking space QStyleOptionSpinBox opt; @@ -141,14 +144,13 @@ public: opt.rect = rect(); - cachedMinimumSizeHint = style()->sizeFromContents(QStyle::CT_SpinBox, &opt, hint, this) - .expandedTo(QApplication::globalStrut()); + cachedMinimumSizeHint = style()->sizeFromContents(QStyle::CT_SpinBox, &opt, hint, this); } return cachedMinimumSizeHint; } private: - int currentUnit{BitcoinUnits::BTC}; + BitcoinUnit currentUnit{BitcoinUnit::BTC}; CAmount singleStep{CAmount(100000)}; // satoshis mutable QSize cachedMinimumSizeHint; bool m_allow_empty{true}; @@ -326,14 +328,14 @@ void BitcoinAmountField::unitChanged(int idx) unit->setToolTip(unit->itemData(idx, Qt::ToolTipRole).toString()); // Determine new unit ID - int newUnit = unit->itemData(idx, BitcoinUnits::UnitRole).toInt(); - - amount->setDisplayUnit(newUnit); + QVariant new_unit = unit->currentData(BitcoinUnits::UnitRole); + assert(new_unit.isValid()); + amount->setDisplayUnit(new_unit.value<BitcoinUnit>()); } -void BitcoinAmountField::setDisplayUnit(int newUnit) +void BitcoinAmountField::setDisplayUnit(BitcoinUnit new_unit) { - unit->setValue(newUnit); + unit->setValue(QVariant::fromValue(new_unit)); } void BitcoinAmountField::setSingleStep(const CAmount& step) diff --git a/src/qt/bitcoinamountfield.h b/src/qt/bitcoinamountfield.h index 366a6fc4b5..a40cd38332 100644 --- a/src/qt/bitcoinamountfield.h +++ b/src/qt/bitcoinamountfield.h @@ -6,6 +6,7 @@ #define BITCOIN_QT_BITCOINAMOUNTFIELD_H #include <consensus/amount.h> +#include <qt/bitcoinunits.h> #include <QWidget> @@ -52,7 +53,7 @@ public: bool validate(); /** Change unit used to display amount. */ - void setDisplayUnit(int unit); + void setDisplayUnit(BitcoinUnit new_unit); /** Make field empty and ready for new input. */ void clear(); diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 85e3c23085..bfcdf6f316 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -26,7 +26,7 @@ #include <qt/walletview.h> #endif // ENABLE_WALLET -#ifdef Q_OS_MAC +#ifdef Q_OS_MACOS #include <qt/macdockiconhandler.h> #endif @@ -41,6 +41,7 @@ #include <validation.h> #include <QAction> +#include <QActionGroup> #include <QApplication> #include <QComboBox> #include <QCursor> @@ -68,7 +69,7 @@ const std::string BitcoinGUI::DEFAULT_UIPLATFORM = -#if defined(Q_OS_MAC) +#if defined(Q_OS_MACOS) "macosx" #elif defined(Q_OS_WIN) "windows" @@ -218,7 +219,7 @@ BitcoinGUI::BitcoinGUI(interfaces::Node& node, const PlatformStyle *_platformSty connect(labelBlocksIcon, &GUIUtil::ClickableLabel::clicked, this, &BitcoinGUI::showModalOverlay); connect(progressBar, &GUIUtil::ClickableProgressBar::clicked, this, &BitcoinGUI::showModalOverlay); -#ifdef Q_OS_MAC +#ifdef Q_OS_MACOS m_app_nap_inhibitor = new CAppNapInhibitor; #endif @@ -234,7 +235,7 @@ BitcoinGUI::~BitcoinGUI() settings.setValue("MainWindowGeometry", saveGeometry()); if(trayIcon) // Hide tray icon, as deleting will let it linger until quit (on Ubuntu) trayIcon->hide(); -#ifdef Q_OS_MAC +#ifdef Q_OS_MACOS delete m_app_nap_inhibitor; delete appMenuBar; MacDockIconHandler::cleanup(); @@ -355,7 +356,7 @@ void BitcoinGUI::createActions() showHelpMessageAction->setStatusTip(tr("Show the %1 help message to get a list with possible Bitcoin command-line options").arg(PACKAGE_NAME)); m_mask_values_action = new QAction(tr("&Mask values"), this); - m_mask_values_action->setShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_M)); + m_mask_values_action->setShortcut(QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::Key_M)); m_mask_values_action->setStatusTip(tr("Mask the values in the Overview tab")); m_mask_values_action->setCheckable(true); @@ -426,13 +427,13 @@ void BitcoinGUI::createActions() } #endif // ENABLE_WALLET - connect(new QShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_C), this), &QShortcut::activated, this, &BitcoinGUI::showDebugWindowActivateConsole); - connect(new QShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_D), this), &QShortcut::activated, this, &BitcoinGUI::showDebugWindow); + connect(new QShortcut(QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::Key_C), this), &QShortcut::activated, this, &BitcoinGUI::showDebugWindowActivateConsole); + connect(new QShortcut(QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::Key_D), this), &QShortcut::activated, this, &BitcoinGUI::showDebugWindow); } void BitcoinGUI::createMenuBar() { -#ifdef Q_OS_MAC +#ifdef Q_OS_MACOS // Create a decoupled menu bar on Mac which stays even if the window is closed appMenuBar = new QMenuBar(); #else @@ -481,7 +482,7 @@ void BitcoinGUI::createMenuBar() minimize_action->setEnabled(window != nullptr && (window->flags() & Qt::Dialog) != Qt::Dialog && window->windowState() != Qt::WindowMinimized); }); -#ifdef Q_OS_MAC +#ifdef Q_OS_MACOS QAction* zoom_action = window_menu->addAction(tr("Zoom")); connect(zoom_action, &QAction::triggered, [] { QWindow* window = qApp->focusWindow(); @@ -498,7 +499,7 @@ void BitcoinGUI::createMenuBar() #endif if (walletFrame) { -#ifdef Q_OS_MAC +#ifdef Q_OS_MACOS window_menu->addSeparator(); QAction* main_window_action = window_menu->addAction(tr("Main Window")); connect(main_window_action, &QAction::triggered, [this] { @@ -754,7 +755,7 @@ void BitcoinGUI::createTrayIcon() { assert(QSystemTrayIcon::isSystemTrayAvailable()); -#ifndef Q_OS_MAC +#ifndef Q_OS_MACOS if (QSystemTrayIcon::isSystemTrayAvailable()) { trayIcon = new QSystemTrayIcon(m_network_style->getTrayAndWindowIcon(), this); QString toolTip = tr("%1 client").arg(PACKAGE_NAME) + " " + m_network_style->getTitleAddText(); @@ -765,17 +766,17 @@ void BitcoinGUI::createTrayIcon() void BitcoinGUI::createTrayIconMenu() { -#ifndef Q_OS_MAC +#ifndef Q_OS_MACOS if (!trayIcon) return; -#endif // Q_OS_MAC +#endif // Q_OS_MACOS // Configuration of the tray icon (or Dock icon) menu. QAction* show_hide_action{nullptr}; -#ifndef Q_OS_MAC +#ifndef Q_OS_MACOS // Note: On macOS, the Dock icon's menu already has Show / Hide action. show_hide_action = trayIconMenu->addAction(QString(), this, &BitcoinGUI::toggleHidden); trayIconMenu->addSeparator(); -#endif // Q_OS_MAC +#endif // Q_OS_MACOS QAction* send_action{nullptr}; QAction* receive_action{nullptr}; @@ -793,7 +794,7 @@ void BitcoinGUI::createTrayIconMenu() options_action->setMenuRole(QAction::PreferencesRole); QAction* node_window_action = trayIconMenu->addAction(openRPCConsoleAction->text(), openRPCConsoleAction, &QAction::trigger); QAction* quit_action{nullptr}; -#ifndef Q_OS_MAC +#ifndef Q_OS_MACOS // Note: On macOS, the Dock icon's menu already has Quit action. trayIconMenu->addSeparator(); quit_action = trayIconMenu->addAction(quitAction->text(), quitAction, &QAction::trigger); @@ -813,7 +814,7 @@ void BitcoinGUI::createTrayIconMenu() activateWindow(); }); trayIconMenu->setAsDockMenu(); -#endif // Q_OS_MAC +#endif // Q_OS_MACOS connect( // Using QSystemTrayIcon::Context is not reliable. @@ -1005,7 +1006,7 @@ void BitcoinGUI::openOptionsDialogWithTab(OptionsDialog::Tab tab) void BitcoinGUI::setNumBlocks(int count, const QDateTime& blockDate, double nVerificationProgress, bool header, SynchronizationState sync_state) { // Disabling macOS App Nap on initial sync, disk and reindex operations. -#ifdef Q_OS_MAC +#ifdef Q_OS_MACOS if (sync_state == SynchronizationState::POST_INIT) { m_app_nap_inhibitor->enableAppNap(); } else { @@ -1191,7 +1192,7 @@ void BitcoinGUI::changeEvent(QEvent *e) QMainWindow::changeEvent(e); -#ifndef Q_OS_MAC // Ignored on Mac +#ifndef Q_OS_MACOS // Ignored on Mac if(e->type() == QEvent::WindowStateChange) { if(clientModel && clientModel->getOptionsModel() && clientModel->getOptionsModel()->getMinimizeToTray()) @@ -1214,7 +1215,7 @@ void BitcoinGUI::changeEvent(QEvent *e) void BitcoinGUI::closeEvent(QCloseEvent *event) { -#ifndef Q_OS_MAC // Ignored on Mac +#ifndef Q_OS_MACOS // Ignored on Mac if(clientModel && clientModel->getOptionsModel()) { if(!clientModel->getOptionsModel()->getMinimizeOnClose()) @@ -1244,7 +1245,7 @@ void BitcoinGUI::showEvent(QShowEvent *event) } #ifdef ENABLE_WALLET -void BitcoinGUI::incomingTransaction(const QString& date, int unit, const CAmount& amount, const QString& type, const QString& address, const QString& label, const QString& walletName) +void BitcoinGUI::incomingTransaction(const QString& date, BitcoinUnit unit, const CAmount& amount, const QString& type, const QString& address, const QString& label, const QString& walletName) { // On new transaction, make an info balloon QString msg = tr("Date: %1\n").arg(date) + @@ -1495,11 +1496,10 @@ UnitDisplayStatusBarControl::UnitDisplayStatusBarControl(const PlatformStyle *pl { createContextMenu(); setToolTip(tr("Unit to show amounts in. Click to select another unit.")); - QList<BitcoinUnits::Unit> units = BitcoinUnits::availableUnits(); + QList<BitcoinUnit> units = BitcoinUnits::availableUnits(); int max_width = 0; const QFontMetrics fm(font()); - for (const BitcoinUnits::Unit unit : units) - { + for (const BitcoinUnit unit : units) { max_width = qMax(max_width, GUIUtil::TextWidth(fm, BitcoinUnits::longName(unit))); } setMinimumSize(max_width, 0); @@ -1529,8 +1529,8 @@ void UnitDisplayStatusBarControl::changeEvent(QEvent* e) void UnitDisplayStatusBarControl::createContextMenu() { menu = new QMenu(this); - for (const BitcoinUnits::Unit u : BitcoinUnits::availableUnits()) { - menu->addAction(BitcoinUnits::longName(u))->setData(QVariant(u)); + for (const BitcoinUnit u : BitcoinUnits::availableUnits()) { + menu->addAction(BitcoinUnits::longName(u))->setData(QVariant::fromValue(u)); } connect(menu, &QMenu::triggered, this, &UnitDisplayStatusBarControl::onMenuSelection); } @@ -1551,7 +1551,7 @@ void UnitDisplayStatusBarControl::setOptionsModel(OptionsModel *_optionsModel) } /** When Display Units are changed on OptionsModel it will refresh the display text of the control on the status bar */ -void UnitDisplayStatusBarControl::updateDisplayUnit(int newUnits) +void UnitDisplayStatusBarControl::updateDisplayUnit(BitcoinUnit newUnits) { setText(BitcoinUnits::longName(newUnits)); } diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h index 0ae5f7331e..6272d5d947 100644 --- a/src/qt/bitcoingui.h +++ b/src/qt/bitcoingui.h @@ -9,6 +9,7 @@ #include <config/bitcoin-config.h> #endif +#include <qt/bitcoinunits.h> #include <qt/guiutil.h> #include <qt/optionsdialog.h> @@ -21,7 +22,7 @@ #include <QPoint> #include <QSystemTrayIcon> -#ifdef Q_OS_MAC +#ifdef Q_OS_MACOS #include <qt/macos_appnap.h> #endif @@ -174,7 +175,7 @@ private: QMenu* m_network_context_menu = new QMenu(this); -#ifdef Q_OS_MAC +#ifdef Q_OS_MACOS CAppNapInhibitor* m_app_nap_inhibitor = nullptr; #endif @@ -260,7 +261,7 @@ public Q_SLOTS: bool handlePaymentRequest(const SendCoinsRecipient& recipient); /** Show incoming transaction notification for new transactions. */ - void incomingTransaction(const QString& date, int unit, const CAmount& amount, const QString& type, const QString& address, const QString& label, const QString& walletName); + void incomingTransaction(const QString& date, BitcoinUnit unit, const CAmount& amount, const QString& type, const QString& address, const QString& label, const QString& walletName); #endif // ENABLE_WALLET private: @@ -303,7 +304,7 @@ public Q_SLOTS: /** Show window if hidden, unminimize when minimized, rise when obscured or show if hidden and fToggleHidden is true */ void showNormalIfMinimized() { showNormalIfMinimized(false); } void showNormalIfMinimized(bool fToggleHidden); - /** Simply calls showNormalIfMinimized(true) for use in SLOT() macro */ + /** Simply calls showNormalIfMinimized(true) */ void toggleHidden(); /** called by a timer to check if ShutdownRequested() has been set **/ @@ -341,7 +342,7 @@ private: private Q_SLOTS: /** When Display Units are changed on OptionsModel it will refresh the display text of the control on the status bar */ - void updateDisplayUnit(int newUnits); + void updateDisplayUnit(BitcoinUnit newUnits); /** Tells underlying optionsModel to update its current display unit. */ void onMenuSelection(QAction* action); }; diff --git a/src/qt/bitcoinunits.cpp b/src/qt/bitcoinunits.cpp index 69caf64d5c..fe3eb3240b 100644 --- a/src/qt/bitcoinunits.cpp +++ b/src/qt/bitcoinunits.cpp @@ -18,94 +18,75 @@ BitcoinUnits::BitcoinUnits(QObject *parent): { } -QList<BitcoinUnits::Unit> BitcoinUnits::availableUnits() +QList<BitcoinUnit> BitcoinUnits::availableUnits() { - QList<BitcoinUnits::Unit> unitlist; - unitlist.append(BTC); - unitlist.append(mBTC); - unitlist.append(uBTC); - unitlist.append(SAT); + QList<BitcoinUnit> unitlist; + unitlist.append(Unit::BTC); + unitlist.append(Unit::mBTC); + unitlist.append(Unit::uBTC); + unitlist.append(Unit::SAT); return unitlist; } -bool BitcoinUnits::valid(int unit) +QString BitcoinUnits::longName(Unit unit) { - switch(unit) - { - case BTC: - case mBTC: - case uBTC: - case SAT: - return true; - default: - return false; - } -} - -QString BitcoinUnits::longName(int unit) -{ - switch(unit) - { - case BTC: return QString("BTC"); - case mBTC: return QString("mBTC"); - case uBTC: return QString::fromUtf8("µBTC (bits)"); - case SAT: return QString("Satoshi (sat)"); - default: return QString("???"); - } + switch (unit) { + case Unit::BTC: return QString("BTC"); + case Unit::mBTC: return QString("mBTC"); + case Unit::uBTC: return QString::fromUtf8("µBTC (bits)"); + case Unit::SAT: return QString("Satoshi (sat)"); + } // no default case, so the compiler can warn about missing cases + assert(false); } -QString BitcoinUnits::shortName(int unit) +QString BitcoinUnits::shortName(Unit unit) { - switch(unit) - { - case uBTC: return QString::fromUtf8("bits"); - case SAT: return QString("sat"); - default: return longName(unit); - } + switch (unit) { + case Unit::BTC: return longName(unit); + case Unit::mBTC: return longName(unit); + case Unit::uBTC: return QString("bits"); + case Unit::SAT: return QString("sat"); + } // no default case, so the compiler can warn about missing cases + assert(false); } -QString BitcoinUnits::description(int unit) +QString BitcoinUnits::description(Unit unit) { - switch(unit) - { - case BTC: return QString("Bitcoins"); - case mBTC: return QString("Milli-Bitcoins (1 / 1" THIN_SP_UTF8 "000)"); - case uBTC: return QString("Micro-Bitcoins (bits) (1 / 1" THIN_SP_UTF8 "000" THIN_SP_UTF8 "000)"); - case SAT: return QString("Satoshi (sat) (1 / 100" THIN_SP_UTF8 "000" THIN_SP_UTF8 "000)"); - default: return QString("???"); - } + switch (unit) { + case Unit::BTC: return QString("Bitcoins"); + case Unit::mBTC: return QString("Milli-Bitcoins (1 / 1" THIN_SP_UTF8 "000)"); + case Unit::uBTC: return QString("Micro-Bitcoins (bits) (1 / 1" THIN_SP_UTF8 "000" THIN_SP_UTF8 "000)"); + case Unit::SAT: return QString("Satoshi (sat) (1 / 100" THIN_SP_UTF8 "000" THIN_SP_UTF8 "000)"); + } // no default case, so the compiler can warn about missing cases + assert(false); } -qint64 BitcoinUnits::factor(int unit) +qint64 BitcoinUnits::factor(Unit unit) { - switch(unit) - { - case BTC: return 100000000; - case mBTC: return 100000; - case uBTC: return 100; - case SAT: return 1; - default: return 100000000; - } + switch (unit) { + case Unit::BTC: return 100'000'000; + case Unit::mBTC: return 100'000; + case Unit::uBTC: return 100; + case Unit::SAT: return 1; + } // no default case, so the compiler can warn about missing cases + assert(false); } -int BitcoinUnits::decimals(int unit) +int BitcoinUnits::decimals(Unit unit) { - switch(unit) - { - case BTC: return 8; - case mBTC: return 5; - case uBTC: return 2; - case SAT: return 0; - default: return 0; - } + switch (unit) { + case Unit::BTC: return 8; + case Unit::mBTC: return 5; + case Unit::uBTC: return 2; + case Unit::SAT: return 0; + } // no default case, so the compiler can warn about missing cases + assert(false); } -QString BitcoinUnits::format(int unit, const CAmount& nIn, bool fPlus, SeparatorStyle separators, bool justify) +QString BitcoinUnits::format(Unit unit, const CAmount& nIn, bool fPlus, SeparatorStyle separators, bool justify) { // Note: not using straight sprintf here because we do NOT want // localized number formatting. - if(!valid(unit)) - return QString(); // Refuse to format invalid unit qint64 n = (qint64)nIn; qint64 coin = factor(unit); int num_decimals = decimals(unit); @@ -147,19 +128,19 @@ QString BitcoinUnits::format(int unit, const CAmount& nIn, bool fPlus, Separator // Please take care to use formatHtmlWithUnit instead, when // appropriate. -QString BitcoinUnits::formatWithUnit(int unit, const CAmount& amount, bool plussign, SeparatorStyle separators) +QString BitcoinUnits::formatWithUnit(Unit unit, const CAmount& amount, bool plussign, SeparatorStyle separators) { return format(unit, amount, plussign, separators) + QString(" ") + shortName(unit); } -QString BitcoinUnits::formatHtmlWithUnit(int unit, const CAmount& amount, bool plussign, SeparatorStyle separators) +QString BitcoinUnits::formatHtmlWithUnit(Unit unit, const CAmount& amount, bool plussign, SeparatorStyle separators) { QString str(formatWithUnit(unit, amount, plussign, separators)); str.replace(QChar(THIN_SP_CP), QString(THIN_SP_HTML)); return QString("<span style='white-space: nowrap;'>%1</span>").arg(str); } -QString BitcoinUnits::formatWithPrivacy(int unit, const CAmount& amount, SeparatorStyle separators, bool privacy) +QString BitcoinUnits::formatWithPrivacy(Unit unit, const CAmount& amount, SeparatorStyle separators, bool privacy) { assert(amount >= 0); QString value; @@ -171,10 +152,11 @@ QString BitcoinUnits::formatWithPrivacy(int unit, const CAmount& amount, Separat return value + QString(" ") + shortName(unit); } -bool BitcoinUnits::parse(int unit, const QString &value, CAmount *val_out) +bool BitcoinUnits::parse(Unit unit, const QString& value, CAmount* val_out) { - if(!valid(unit) || value.isEmpty()) + if (value.isEmpty()) { return false; // Refuse to parse invalid unit or empty string + } int num_decimals = decimals(unit); // Ignore spaces and thin spaces when parsing @@ -210,14 +192,9 @@ bool BitcoinUnits::parse(int unit, const QString &value, CAmount *val_out) return ok; } -QString BitcoinUnits::getAmountColumnTitle(int unit) +QString BitcoinUnits::getAmountColumnTitle(Unit unit) { - QString amountTitle = QObject::tr("Amount"); - if (BitcoinUnits::valid(unit)) - { - amountTitle += " ("+BitcoinUnits::shortName(unit) + ")"; - } - return amountTitle; + return QObject::tr("Amount") + " (" + shortName(unit) + ")"; } int BitcoinUnits::rowCount(const QModelIndex &parent) const @@ -240,7 +217,7 @@ QVariant BitcoinUnits::data(const QModelIndex &index, int role) const case Qt::ToolTipRole: return QVariant(description(unit)); case UnitRole: - return QVariant(static_cast<int>(unit)); + return QVariant::fromValue(unit); } } return QVariant(); @@ -250,3 +227,40 @@ CAmount BitcoinUnits::maxMoney() { return MAX_MONEY; } + +namespace { +qint8 ToQint8(BitcoinUnit unit) +{ + switch (unit) { + case BitcoinUnit::BTC: return 0; + case BitcoinUnit::mBTC: return 1; + case BitcoinUnit::uBTC: return 2; + case BitcoinUnit::SAT: return 3; + } // no default case, so the compiler can warn about missing cases + assert(false); +} + +BitcoinUnit FromQint8(qint8 num) +{ + switch (num) { + case 0: return BitcoinUnit::BTC; + case 1: return BitcoinUnit::mBTC; + case 2: return BitcoinUnit::uBTC; + case 3: return BitcoinUnit::SAT; + } + assert(false); +} +} // namespace + +QDataStream& operator<<(QDataStream& out, const BitcoinUnit& unit) +{ + return out << ToQint8(unit); +} + +QDataStream& operator>>(QDataStream& in, BitcoinUnit& unit) +{ + qint8 input; + in >> input; + unit = FromQint8(input); + return in; +} diff --git a/src/qt/bitcoinunits.h b/src/qt/bitcoinunits.h index 9fedec0d4f..b3b5a8fc18 100644 --- a/src/qt/bitcoinunits.h +++ b/src/qt/bitcoinunits.h @@ -8,6 +8,7 @@ #include <consensus/amount.h> #include <QAbstractListModel> +#include <QDataStream> #include <QString> // U+2009 THIN SPACE = UTF-8 E2 80 89 @@ -38,13 +39,13 @@ public: /** Bitcoin units. @note Source: https://en.bitcoin.it/wiki/Units . Please add only sensible ones */ - enum Unit - { + enum class Unit { BTC, mBTC, uBTC, SAT }; + Q_ENUM(Unit) enum class SeparatorStyle { @@ -59,30 +60,28 @@ public: //! Get list of units, for drop-down box static QList<Unit> availableUnits(); - //! Is unit ID valid? - static bool valid(int unit); //! Long name - static QString longName(int unit); + static QString longName(Unit unit); //! Short name - static QString shortName(int unit); + static QString shortName(Unit unit); //! Longer description - static QString description(int unit); + static QString description(Unit unit); //! Number of Satoshis (1e-8) per unit - static qint64 factor(int unit); + static qint64 factor(Unit unit); //! Number of decimals left - static int decimals(int unit); + static int decimals(Unit unit); //! Format as string - static QString format(int unit, const CAmount& amount, bool plussign = false, SeparatorStyle separators = SeparatorStyle::STANDARD, bool justify = false); + static QString format(Unit unit, const CAmount& amount, bool plussign = false, SeparatorStyle separators = SeparatorStyle::STANDARD, bool justify = false); //! Format as string (with unit) - static QString formatWithUnit(int unit, const CAmount& amount, bool plussign=false, SeparatorStyle separators=SeparatorStyle::STANDARD); + static QString formatWithUnit(Unit unit, const CAmount& amount, bool plussign = false, SeparatorStyle separators = SeparatorStyle::STANDARD); //! Format as HTML string (with unit) - static QString formatHtmlWithUnit(int unit, const CAmount& amount, bool plussign=false, SeparatorStyle separators=SeparatorStyle::STANDARD); + static QString formatHtmlWithUnit(Unit unit, const CAmount& amount, bool plussign = false, SeparatorStyle separators = SeparatorStyle::STANDARD); //! Format as string (with unit) of fixed length to preserve privacy, if it is set. - static QString formatWithPrivacy(int unit, const CAmount& amount, SeparatorStyle separators, bool privacy); + static QString formatWithPrivacy(Unit unit, const CAmount& amount, SeparatorStyle separators, bool privacy); //! Parse string to coin amount - static bool parse(int unit, const QString &value, CAmount *val_out); + static bool parse(Unit unit, const QString& value, CAmount* val_out); //! Gets title for amount column including current display unit if optionsModel reference available */ - static QString getAmountColumnTitle(int unit); + static QString getAmountColumnTitle(Unit unit); ///@} //! @name AbstractListModel implementation @@ -107,8 +106,11 @@ public: static CAmount maxMoney(); private: - QList<BitcoinUnits::Unit> unitlist; + QList<Unit> unitlist; }; typedef BitcoinUnits::Unit BitcoinUnit; +QDataStream& operator<<(QDataStream& out, const BitcoinUnit& unit); +QDataStream& operator>>(QDataStream& in, BitcoinUnit& unit); + #endif // BITCOIN_QT_BITCOINUNITS_H diff --git a/src/qt/clientmodel.cpp b/src/qt/clientmodel.cpp index 4327d31787..f41da519df 100644 --- a/src/qt/clientmodel.cpp +++ b/src/qt/clientmodel.cpp @@ -21,9 +21,9 @@ #include <validation.h> #include <stdint.h> -#include <functional> #include <QDebug> +#include <QMetaObject> #include <QThread> #include <QTimer> @@ -148,21 +148,6 @@ uint256 ClientModel::getBestBlockHash() return m_cached_tip_blocks; } -void ClientModel::updateNumConnections(int numConnections) -{ - Q_EMIT numConnectionsChanged(numConnections); -} - -void ClientModel::updateNetworkActive(bool networkActive) -{ - Q_EMIT networkActiveChanged(networkActive); -} - -void ClientModel::updateAlert() -{ - Q_EMIT alertsChanged(getStatusBarWarnings()); -} - enum BlockSource ClientModel::getBlockSource() const { if (m_node.getReindex()) @@ -230,94 +215,65 @@ QString ClientModel::blocksDir() const return GUIUtil::PathToQString(gArgs.GetBlocksDirPath()); } -void ClientModel::updateBanlist() -{ - banTableModel->refresh(); -} - -// Handlers for core signals -static void ShowProgress(ClientModel *clientmodel, const std::string &title, int nProgress) -{ - // emits signal "showProgress" - bool invoked = QMetaObject::invokeMethod(clientmodel, "showProgress", Qt::QueuedConnection, - Q_ARG(QString, QString::fromStdString(title)), - Q_ARG(int, nProgress)); - assert(invoked); -} - -static void NotifyNumConnectionsChanged(ClientModel *clientmodel, int newNumConnections) -{ - // Too noisy: qDebug() << "NotifyNumConnectionsChanged: " + QString::number(newNumConnections); - bool invoked = QMetaObject::invokeMethod(clientmodel, "updateNumConnections", Qt::QueuedConnection, - Q_ARG(int, newNumConnections)); - assert(invoked); -} - -static void NotifyNetworkActiveChanged(ClientModel *clientmodel, bool networkActive) -{ - bool invoked = QMetaObject::invokeMethod(clientmodel, "updateNetworkActive", Qt::QueuedConnection, - Q_ARG(bool, networkActive)); - assert(invoked); -} - -static void NotifyAlertChanged(ClientModel *clientmodel) -{ - qDebug() << "NotifyAlertChanged"; - bool invoked = QMetaObject::invokeMethod(clientmodel, "updateAlert", Qt::QueuedConnection); - assert(invoked); -} - -static void BannedListChanged(ClientModel *clientmodel) -{ - qDebug() << QString("%1: Requesting update for peer banlist").arg(__func__); - bool invoked = QMetaObject::invokeMethod(clientmodel, "updateBanlist", Qt::QueuedConnection); - assert(invoked); -} - -static void BlockTipChanged(ClientModel* clientmodel, SynchronizationState sync_state, interfaces::BlockTip tip, double verificationProgress, bool fHeader) +void ClientModel::TipChanged(SynchronizationState sync_state, interfaces::BlockTip tip, double verification_progress, bool header) { - if (fHeader) { + if (header) { // cache best headers time and height to reduce future cs_main locks - clientmodel->cachedBestHeaderHeight = tip.block_height; - clientmodel->cachedBestHeaderTime = tip.block_time; + cachedBestHeaderHeight = tip.block_height; + cachedBestHeaderTime = tip.block_time; } else { - clientmodel->m_cached_num_blocks = tip.block_height; - WITH_LOCK(clientmodel->m_cached_tip_mutex, clientmodel->m_cached_tip_blocks = tip.block_hash;); + m_cached_num_blocks = tip.block_height; + WITH_LOCK(m_cached_tip_mutex, m_cached_tip_blocks = tip.block_hash;); } // Throttle GUI notifications about (a) blocks during initial sync, and (b) both blocks and headers during reindex. - const bool throttle = (sync_state != SynchronizationState::POST_INIT && !fHeader) || sync_state == SynchronizationState::INIT_REINDEX; + const bool throttle = (sync_state != SynchronizationState::POST_INIT && !header) || sync_state == SynchronizationState::INIT_REINDEX; const int64_t now = throttle ? GetTimeMillis() : 0; - int64_t& nLastUpdateNotification = fHeader ? nLastHeaderTipUpdateNotification : nLastBlockTipUpdateNotification; + int64_t& nLastUpdateNotification = header ? nLastHeaderTipUpdateNotification : nLastBlockTipUpdateNotification; if (throttle && now < nLastUpdateNotification + count_milliseconds(MODEL_UPDATE_DELAY)) { return; } - bool invoked = QMetaObject::invokeMethod(clientmodel, "numBlocksChanged", Qt::QueuedConnection, - Q_ARG(int, tip.block_height), - Q_ARG(QDateTime, QDateTime::fromSecsSinceEpoch(tip.block_time)), - Q_ARG(double, verificationProgress), - Q_ARG(bool, fHeader), - Q_ARG(SynchronizationState, sync_state)); - assert(invoked); + Q_EMIT numBlocksChanged(tip.block_height, QDateTime::fromSecsSinceEpoch(tip.block_time), verification_progress, header, sync_state); nLastUpdateNotification = now; } void ClientModel::subscribeToCoreSignals() { - // Connect signals to client - m_handler_show_progress = m_node.handleShowProgress(std::bind(ShowProgress, this, std::placeholders::_1, std::placeholders::_2)); - m_handler_notify_num_connections_changed = m_node.handleNotifyNumConnectionsChanged(std::bind(NotifyNumConnectionsChanged, this, std::placeholders::_1)); - m_handler_notify_network_active_changed = m_node.handleNotifyNetworkActiveChanged(std::bind(NotifyNetworkActiveChanged, this, std::placeholders::_1)); - m_handler_notify_alert_changed = m_node.handleNotifyAlertChanged(std::bind(NotifyAlertChanged, this)); - m_handler_banned_list_changed = m_node.handleBannedListChanged(std::bind(BannedListChanged, this)); - m_handler_notify_block_tip = m_node.handleNotifyBlockTip(std::bind(BlockTipChanged, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, false)); - m_handler_notify_header_tip = m_node.handleNotifyHeaderTip(std::bind(BlockTipChanged, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, true)); + m_handler_show_progress = m_node.handleShowProgress( + [this](const std::string& title, int progress, [[maybe_unused]] bool resume_possible) { + Q_EMIT showProgress(QString::fromStdString(title), progress); + }); + m_handler_notify_num_connections_changed = m_node.handleNotifyNumConnectionsChanged( + [this](int new_num_connections) { + Q_EMIT numConnectionsChanged(new_num_connections); + }); + m_handler_notify_network_active_changed = m_node.handleNotifyNetworkActiveChanged( + [this](bool network_active) { + Q_EMIT networkActiveChanged(network_active); + }); + m_handler_notify_alert_changed = m_node.handleNotifyAlertChanged( + [this]() { + qDebug() << "ClientModel: NotifyAlertChanged"; + Q_EMIT alertsChanged(getStatusBarWarnings()); + }); + m_handler_banned_list_changed = m_node.handleBannedListChanged( + [this]() { + qDebug() << "ClienModel: Requesting update for peer banlist"; + QMetaObject::invokeMethod(banTableModel, [this] { banTableModel->refresh(); }); + }); + m_handler_notify_block_tip = m_node.handleNotifyBlockTip( + [this](SynchronizationState sync_state, interfaces::BlockTip tip, double verification_progress) { + TipChanged(sync_state, tip, verification_progress, /*header=*/false); + }); + m_handler_notify_header_tip = m_node.handleNotifyHeaderTip( + [this](SynchronizationState sync_state, interfaces::BlockTip tip, double verification_progress) { + TipChanged(sync_state, tip, verification_progress, /*header=*/true); + }); } void ClientModel::unsubscribeFromCoreSignals() { - // Disconnect signals from client m_handler_show_progress->disconnect(); m_handler_notify_num_connections_changed->disconnect(); m_handler_notify_network_active_changed->disconnect(); diff --git a/src/qt/clientmodel.h b/src/qt/clientmodel.h index 846691c0c0..b10ffb4523 100644 --- a/src/qt/clientmodel.h +++ b/src/qt/clientmodel.h @@ -23,6 +23,7 @@ enum class SynchronizationState; namespace interfaces { class Handler; class Node; +struct BlockTip; } QT_BEGIN_NAMESPACE @@ -61,7 +62,7 @@ public: //! Return number of connections, default is in- and outbound (total) int getNumConnections(unsigned int flags = CONNECTIONS_ALL) const; int getNumBlocks() const; - uint256 getBestBlockHash(); + uint256 getBestBlockHash() EXCLUSIVE_LOCKS_REQUIRED(!m_cached_tip_mutex); int getHeaderTipHeight() const; int64_t getHeaderTipTime() const; @@ -104,6 +105,7 @@ private: //! A thread to interact with m_node asynchronously QThread* const m_thread; + void TipChanged(SynchronizationState sync_state, interfaces::BlockTip tip, double verification_progress, bool header); void subscribeToCoreSignals(); void unsubscribeFromCoreSignals(); @@ -120,12 +122,6 @@ Q_SIGNALS: // Show progress dialog e.g. for verifychain void showProgress(const QString &title, int nProgress); - -public Q_SLOTS: - void updateNumConnections(int numConnections); - void updateNetworkActive(bool networkActive); - void updateAlert(); - void updateBanlist(); }; #endif // BITCOIN_QT_CLIENTMODEL_H diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp index d3103492a4..bd9a90a890 100644 --- a/src/qt/coincontroldialog.cpp +++ b/src/qt/coincontroldialog.cpp @@ -507,7 +507,7 @@ void CoinControlDialog::updateLabels(CCoinControl& m_coin_control, WalletModel * } // actually update labels - int nDisplayUnit = BitcoinUnits::BTC; + BitcoinUnit nDisplayUnit = BitcoinUnit::BTC; if (model && model->getOptionsModel()) nDisplayUnit = model->getOptionsModel()->getDisplayUnit(); @@ -588,9 +588,9 @@ void CoinControlDialog::updateView() ui->treeWidget->setEnabled(false); // performance, otherwise updateLabels would be called for every checked checkbox ui->treeWidget->setAlternatingRowColors(!treeMode); QFlags<Qt::ItemFlag> flgCheckbox = Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsUserCheckable; - QFlags<Qt::ItemFlag> flgTristate = Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsUserCheckable | Qt::ItemIsTristate; + QFlags<Qt::ItemFlag> flgTristate = Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsUserCheckable | Qt::ItemIsAutoTristate; - int nDisplayUnit = model->getOptionsModel()->getDisplayUnit(); + BitcoinUnit nDisplayUnit = model->getOptionsModel()->getDisplayUnit(); for (const auto& coins : model->wallet().listCoins()) { CCoinControlWidgetItem* itemWalletAddress{nullptr}; diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp index 362601b512..e3c6d8a624 100644 --- a/src/qt/guiutil.cpp +++ b/src/qt/guiutil.cpp @@ -60,6 +60,7 @@ #include <QSettings> #include <QShortcut> #include <QSize> +#include <QStandardPaths> #include <QString> #include <QTextDocument> // for Qt::mightBeRichText #include <QThread> @@ -73,13 +74,15 @@ #include <string> #include <vector> -#if defined(Q_OS_MAC) +#if defined(Q_OS_MACOS) #include <QProcess> void ForceActivation(); #endif +using namespace std::chrono_literals; + namespace GUIUtil { QString dateTimeStr(const QDateTime &date) @@ -175,8 +178,7 @@ bool parseBitcoinURI(const QUrl &uri, SendCoinsRecipient *out) { if(!i->second.isEmpty()) { - if(!BitcoinUnits::parse(BitcoinUnits::BTC, i->second, &rv.amount)) - { + if (!BitcoinUnits::parse(BitcoinUnit::BTC, i->second, &rv.amount)) { return false; } } @@ -208,7 +210,7 @@ QString formatBitcoinURI(const SendCoinsRecipient &info) if (info.amount) { - ret += QString("?amount=%1").arg(BitcoinUnits::format(BitcoinUnits::BTC, info.amount, false, BitcoinUnits::SeparatorStyle::NEVER)); + ret += QString("?amount=%1").arg(BitcoinUnits::format(BitcoinUnit::BTC, info.amount, false, BitcoinUnits::SeparatorStyle::NEVER)); paramCount++; } @@ -397,7 +399,7 @@ bool isObscured(QWidget *w) void bringToFront(QWidget* w) { -#ifdef Q_OS_MAC +#ifdef Q_OS_MACOS ForceActivation(); #endif @@ -441,7 +443,7 @@ bool openBitcoinConf() /* Open bitcoin.conf with the associated application */ bool res = QDesktopServices::openUrl(QUrl::fromLocalFile(PathToQString(pathConfig))); -#ifdef Q_OS_MAC +#ifdef Q_OS_MACOS // Workaround for macOS-specific behavior; see #15409. if (!res) { res = QProcess::startDetached("/usr/bin/open", QStringList{"-t", PathToQString(pathConfig)}); @@ -506,7 +508,7 @@ fs::path static StartupShortcutPath() return GetSpecialFolderPath(CSIDL_STARTUP) / "Bitcoin.lnk"; if (chain == CBaseChainParams::TESTNET) // Remove this special case when CBaseChainParams::TESTNET = "testnet4" return GetSpecialFolderPath(CSIDL_STARTUP) / "Bitcoin (testnet).lnk"; - return GetSpecialFolderPath(CSIDL_STARTUP) / strprintf("Bitcoin (%s).lnk", chain); + return GetSpecialFolderPath(CSIDL_STARTUP) / fs::u8path(strprintf("Bitcoin (%s).lnk", chain)); } bool GetStartOnSystemStartup() @@ -587,7 +589,7 @@ fs::path static GetAutostartFilePath() std::string chain = gArgs.GetChainName(); if (chain == CBaseChainParams::MAIN) return GetAutostartDir() / "bitcoin.desktop"; - return GetAutostartDir() / strprintf("bitcoin-%s.desktop", chain); + return GetAutostartDir() / fs::u8path(strprintf("bitcoin-%s.desktop", chain)); } bool GetStartOnSystemStartup() @@ -728,6 +730,16 @@ QString formatDurationStr(std::chrono::seconds dur) return str_list.join(" "); } +QString FormatPeerAge(std::chrono::seconds time_connected) +{ + const auto time_now{GetTime<std::chrono::seconds>()}; + const auto age{time_now - time_connected}; + if (age >= 24h) return QObject::tr("%1 d").arg(age / 24h); + if (age >= 1h) return QObject::tr("%1 h").arg(age / 1h); + if (age >= 1min) return QObject::tr("%1 m").arg(age / 1min); + return QObject::tr("%1 s").arg(age / 1s); +} + QString formatServicesStr(quint64 mask) { QStringList strList; @@ -870,7 +882,7 @@ bool ItemDelegate::eventFilter(QObject *object, QEvent *event) void PolishProgressDialog(QProgressDialog* dialog) { -#ifdef Q_OS_MAC +#ifdef Q_OS_MACOS // Workaround for macOS-only Qt bug; see: QTBUG-65750, QTBUG-70357. const int margin = TextWidth(dialog->fontMetrics(), ("X")); dialog->resize(dialog->width() + 2 * margin, dialog->height()); diff --git a/src/qt/guiutil.h b/src/qt/guiutil.h index 0224b18b4e..e38ac6026a 100644 --- a/src/qt/guiutil.h +++ b/src/qt/guiutil.h @@ -223,6 +223,9 @@ namespace GUIUtil /** Convert seconds into a QString with days, hours, mins, secs */ QString formatDurationStr(std::chrono::seconds dur); + /** Convert peer connection time to a QString denominated in the most relevant unit. */ + QString FormatPeerAge(std::chrono::seconds time_connected); + /** Format CNodeStats.nServices bitmask into a user-readable string */ QString formatServicesStr(quint64 mask); @@ -358,18 +361,6 @@ namespace GUIUtil #endif } - /** - * Queue a function to run in an object's event loop. This can be - * replaced by a call to the QMetaObject::invokeMethod functor overload after Qt 5.10, but - * for now use a QObject::connect for compatibility with older Qt versions, based on - * https://stackoverflow.com/questions/21646467/how-to-execute-a-functor-or-a-lambda-in-a-given-thread-in-qt-gcd-style - */ - template <typename Fn> - void ObjectInvoke(QObject* object, Fn&& function, Qt::ConnectionType connection = Qt::QueuedConnection) - { - QObject source; - QObject::connect(&source, &QObject::destroyed, object, std::forward<Fn>(function), connection); - } /** * Replaces a plain text link with an HTML tagged one. diff --git a/src/qt/initexecutor.cpp b/src/qt/initexecutor.cpp index 24ae7ba73d..d269dfec71 100644 --- a/src/qt/initexecutor.cpp +++ b/src/qt/initexecutor.cpp @@ -5,13 +5,13 @@ #include <qt/initexecutor.h> #include <interfaces/node.h> -#include <qt/guiutil.h> #include <util/system.h> #include <util/threadnames.h> #include <exception> #include <QDebug> +#include <QMetaObject> #include <QObject> #include <QString> #include <QThread> @@ -39,7 +39,7 @@ void InitExecutor::handleRunawayException(const std::exception* e) void InitExecutor::initialize() { - GUIUtil::ObjectInvoke(&m_context, [this] { + QMetaObject::invokeMethod(&m_context, [this] { try { util::ThreadRename("qt-init"); qDebug() << "Running initialization in thread"; @@ -56,7 +56,7 @@ void InitExecutor::initialize() void InitExecutor::shutdown() { - GUIUtil::ObjectInvoke(&m_context, [this] { + QMetaObject::invokeMethod(&m_context, [this] { try { qDebug() << "Running Shutdown in thread"; m_node.appShutdown(); diff --git a/src/qt/intro.cpp b/src/qt/intro.cpp index 63b4055092..f4928951fe 100644 --- a/src/qt/intro.cpp +++ b/src/qt/intro.cpp @@ -226,7 +226,7 @@ bool Intro::showIfNeeded(bool& did_show_intro, int64_t& prune_MiB) } /* If current default data directory does not exist, let the user choose one */ - Intro intro(0, Params().AssumedBlockchainSize(), Params().AssumedChainStateSize()); + Intro intro(nullptr, Params().AssumedBlockchainSize(), Params().AssumedChainStateSize()); intro.setDataDirectory(dataDir); intro.setWindowIcon(QIcon(":icons/bitcoin")); did_show_intro = true; diff --git a/src/qt/notificator.cpp b/src/qt/notificator.cpp index b097ef080c..b311a9f32e 100644 --- a/src/qt/notificator.cpp +++ b/src/qt/notificator.cpp @@ -14,10 +14,11 @@ #include <QTemporaryFile> #include <QVariant> #ifdef USE_DBUS -#include <stdint.h> +#include <QDBusMetaType> #include <QtDBus> +#include <stdint.h> #endif -#ifdef Q_OS_MAC +#ifdef Q_OS_MACOS #include <qt/macnotificationhandler.h> #endif @@ -49,7 +50,7 @@ Notificator::Notificator(const QString &_programName, QSystemTrayIcon *_trayIcon mode = Freedesktop; } #endif -#ifdef Q_OS_MAC +#ifdef Q_OS_MACOS // check if users OS has support for NSUserNotification if( MacNotificationHandler::instance()->hasUserNotificationCenterSupport()) { mode = UserNotificationCenter; @@ -70,11 +71,9 @@ Notificator::~Notificator() class FreedesktopImage { public: - FreedesktopImage() {} + FreedesktopImage() = default; explicit FreedesktopImage(const QImage &img); - static int metaType(); - // Image to variant that can be marshalled over DBus static QVariant toVariant(const QImage &img); @@ -136,15 +135,10 @@ const QDBusArgument &operator>>(const QDBusArgument &a, FreedesktopImage &i) return a; } -int FreedesktopImage::metaType() -{ - return qDBusRegisterMetaType<FreedesktopImage>(); -} - QVariant FreedesktopImage::toVariant(const QImage &img) { FreedesktopImage fimg(img); - return QVariant(FreedesktopImage::metaType(), &fimg); + return QVariant(qDBusRegisterMetaType<FreedesktopImage>(), &fimg); } void Notificator::notifyDBus(Class cls, const QString &title, const QString &text, const QIcon &icon, int millisTimeout) @@ -216,7 +210,7 @@ void Notificator::notifySystray(Class cls, const QString &title, const QString & trayIcon->showMessage(title, text, sicon, millisTimeout); } -#ifdef Q_OS_MAC +#ifdef Q_OS_MACOS void Notificator::notifyMacUserNotificationCenter(const QString &title, const QString &text) { // icon is not supported by the user notification center yet. OSX will use the app icon. @@ -236,7 +230,7 @@ void Notificator::notify(Class cls, const QString &title, const QString &text, c case QSystemTray: notifySystray(cls, title, text, millisTimeout); break; -#ifdef Q_OS_MAC +#ifdef Q_OS_MACOS case UserNotificationCenter: notifyMacUserNotificationCenter(title, text); break; diff --git a/src/qt/notificator.h b/src/qt/notificator.h index 7d80b43e43..642d2b29ae 100644 --- a/src/qt/notificator.h +++ b/src/qt/notificator.h @@ -69,7 +69,7 @@ private: void notifyDBus(Class cls, const QString &title, const QString &text, const QIcon &icon, int millisTimeout); #endif void notifySystray(Class cls, const QString &title, const QString &text, int millisTimeout); -#ifdef Q_OS_MAC +#ifdef Q_OS_MACOS void notifyMacUserNotificationCenter(const QString &title, const QString &text); #endif }; diff --git a/src/qt/optionsdialog.cpp b/src/qt/optionsdialog.cpp index f90765fe5b..e6ff43a379 100644 --- a/src/qt/optionsdialog.cpp +++ b/src/qt/optionsdialog.cpp @@ -78,7 +78,7 @@ OptionsDialog::OptionsDialog(QWidget *parent, bool enableWallet) : connect(ui->connectSocksTor, &QPushButton::toggled, this, &OptionsDialog::updateProxyValidationState); /* Window elements init */ -#ifdef Q_OS_MAC +#ifdef Q_OS_MACOS /* remove Window tab on Mac */ ui->tabWidget->removeTab(ui->tabWidget->indexOf(ui->tabWindow)); /* hide launch at startup option on macOS */ @@ -261,7 +261,7 @@ void OptionsDialog::setMapper() mapper->addMapping(ui->proxyPortTor, OptionsModel::ProxyPortTor); /* Window */ -#ifndef Q_OS_MAC +#ifndef Q_OS_MACOS if (QSystemTrayIcon::isSystemTrayAvailable()) { mapper->addMapping(ui->showTrayIcon, OptionsModel::ShowTrayIcon); mapper->addMapping(ui->minimizeToTray, OptionsModel::MinimizeToTray); diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp index 52bda59748..612d3009c1 100644 --- a/src/qt/optionsmodel.cpp +++ b/src/qt/optionsmodel.cpp @@ -24,13 +24,14 @@ #include <QLatin1Char> #include <QSettings> #include <QStringList> +#include <QVariant> const char *DEFAULT_GUI_PROXY_HOST = "127.0.0.1"; static const QString GetDefaultProxyAddress(); -OptionsModel::OptionsModel(QObject *parent, bool resetSettings) : - QAbstractListModel(parent) +OptionsModel::OptionsModel(interfaces::Node& node, QObject *parent, bool resetSettings) : + QAbstractListModel(parent), m_node{node} { Init(resetSettings); } @@ -71,9 +72,16 @@ void OptionsModel::Init(bool resetSettings) fMinimizeOnClose = settings.value("fMinimizeOnClose").toBool(); // Display - if (!settings.contains("nDisplayUnit")) - settings.setValue("nDisplayUnit", BitcoinUnits::BTC); - nDisplayUnit = settings.value("nDisplayUnit").toInt(); + if (!settings.contains("DisplayBitcoinUnit")) { + settings.setValue("DisplayBitcoinUnit", QVariant::fromValue(BitcoinUnit::BTC)); + } + QVariant unit = settings.value("DisplayBitcoinUnit"); + if (unit.canConvert<BitcoinUnit>()) { + m_display_bitcoin_unit = unit.value<BitcoinUnit>(); + } else { + m_display_bitcoin_unit = BitcoinUnit::BTC; + settings.setValue("DisplayBitcoinUnit", QVariant::fromValue(m_display_bitcoin_unit)); + } if (!settings.contains("strThirdPartyTxUrls")) settings.setValue("strThirdPartyTxUrls", ""); @@ -327,273 +335,282 @@ QVariant OptionsModel::data(const QModelIndex & index, int role) const { if(role == Qt::EditRole) { - QSettings settings; - switch(index.row()) - { - case StartAtStartup: - return GUIUtil::GetStartOnSystemStartup(); - case ShowTrayIcon: - return m_show_tray_icon; - case MinimizeToTray: - return fMinimizeToTray; - case MapPortUPnP: + return getOption(OptionID(index.row())); + } + return QVariant(); +} + +// write QSettings values +bool OptionsModel::setData(const QModelIndex & index, const QVariant & value, int role) +{ + bool successful = true; /* set to false on parse error */ + if(role == Qt::EditRole) + { + successful = setOption(OptionID(index.row()), value); + } + + Q_EMIT dataChanged(index, index); + + return successful; +} + +QVariant OptionsModel::getOption(OptionID option) const +{ + QSettings settings; + switch (option) { + case StartAtStartup: + return GUIUtil::GetStartOnSystemStartup(); + case ShowTrayIcon: + return m_show_tray_icon; + case MinimizeToTray: + return fMinimizeToTray; + case MapPortUPnP: #ifdef USE_UPNP - return settings.value("fUseUPnP"); + return settings.value("fUseUPnP"); #else - return false; + return false; #endif // USE_UPNP - case MapPortNatpmp: + case MapPortNatpmp: #ifdef USE_NATPMP - return settings.value("fUseNatpmp"); + return settings.value("fUseNatpmp"); #else - return false; + return false; #endif // USE_NATPMP - case MinimizeOnClose: - return fMinimizeOnClose; - - // default proxy - case ProxyUse: - return settings.value("fUseProxy", false); - case ProxyIP: - return GetProxySetting(settings, "addrProxy").ip; - case ProxyPort: - return GetProxySetting(settings, "addrProxy").port; - - // separate Tor proxy - case ProxyUseTor: - return settings.value("fUseSeparateProxyTor", false); - case ProxyIPTor: - return GetProxySetting(settings, "addrSeparateProxyTor").ip; - case ProxyPortTor: - return GetProxySetting(settings, "addrSeparateProxyTor").port; + case MinimizeOnClose: + return fMinimizeOnClose; + + // default proxy + case ProxyUse: + return settings.value("fUseProxy", false); + case ProxyIP: + return GetProxySetting(settings, "addrProxy").ip; + case ProxyPort: + return GetProxySetting(settings, "addrProxy").port; + + // separate Tor proxy + case ProxyUseTor: + return settings.value("fUseSeparateProxyTor", false); + case ProxyIPTor: + return GetProxySetting(settings, "addrSeparateProxyTor").ip; + case ProxyPortTor: + return GetProxySetting(settings, "addrSeparateProxyTor").port; #ifdef ENABLE_WALLET - case SpendZeroConfChange: - return settings.value("bSpendZeroConfChange"); - case ExternalSignerPath: - return settings.value("external_signer_path"); - case SubFeeFromAmount: - return m_sub_fee_from_amount; + case SpendZeroConfChange: + return settings.value("bSpendZeroConfChange"); + case ExternalSignerPath: + return settings.value("external_signer_path"); + case SubFeeFromAmount: + return m_sub_fee_from_amount; #endif - case DisplayUnit: - return nDisplayUnit; - case ThirdPartyTxUrls: - return strThirdPartyTxUrls; - case Language: - return settings.value("language"); - case UseEmbeddedMonospacedFont: - return m_use_embedded_monospaced_font; - case CoinControlFeatures: - return fCoinControlFeatures; - case EnablePSBTControls: - return settings.value("enable_psbt_controls"); - case Prune: - return settings.value("bPrune"); - case PruneSize: - return settings.value("nPruneSize"); - case DatabaseCache: - return settings.value("nDatabaseCache"); - case ThreadsScriptVerif: - return settings.value("nThreadsScriptVerif"); - case Listen: - return settings.value("fListen"); - case Server: - return settings.value("server"); - default: - return QVariant(); - } + case DisplayUnit: + return QVariant::fromValue(m_display_bitcoin_unit); + case ThirdPartyTxUrls: + return strThirdPartyTxUrls; + case Language: + return settings.value("language"); + case UseEmbeddedMonospacedFont: + return m_use_embedded_monospaced_font; + case CoinControlFeatures: + return fCoinControlFeatures; + case EnablePSBTControls: + return settings.value("enable_psbt_controls"); + case Prune: + return settings.value("bPrune"); + case PruneSize: + return settings.value("nPruneSize"); + case DatabaseCache: + return settings.value("nDatabaseCache"); + case ThreadsScriptVerif: + return settings.value("nThreadsScriptVerif"); + case Listen: + return settings.value("fListen"); + case Server: + return settings.value("server"); + default: + return QVariant(); } - return QVariant(); } -// write QSettings values -bool OptionsModel::setData(const QModelIndex & index, const QVariant & value, int role) +bool OptionsModel::setOption(OptionID option, const QVariant& value) { bool successful = true; /* set to false on parse error */ - if(role == Qt::EditRole) - { - QSettings settings; - switch(index.row()) - { - case StartAtStartup: - successful = GUIUtil::SetStartOnSystemStartup(value.toBool()); - break; - case ShowTrayIcon: - m_show_tray_icon = value.toBool(); - settings.setValue("fHideTrayIcon", !m_show_tray_icon); - Q_EMIT showTrayIconChanged(m_show_tray_icon); - break; - case MinimizeToTray: - fMinimizeToTray = value.toBool(); - settings.setValue("fMinimizeToTray", fMinimizeToTray); - break; - case MapPortUPnP: // core option - can be changed on-the-fly - settings.setValue("fUseUPnP", value.toBool()); - break; - case MapPortNatpmp: // core option - can be changed on-the-fly - settings.setValue("fUseNatpmp", value.toBool()); - break; - case MinimizeOnClose: - fMinimizeOnClose = value.toBool(); - settings.setValue("fMinimizeOnClose", fMinimizeOnClose); - break; - - // default proxy - case ProxyUse: - if (settings.value("fUseProxy") != value) { - settings.setValue("fUseProxy", value.toBool()); - setRestartRequired(true); - } - break; - case ProxyIP: { - auto ip_port = GetProxySetting(settings, "addrProxy"); - if (!ip_port.is_set || ip_port.ip != value.toString()) { - ip_port.ip = value.toString(); - SetProxySetting(settings, "addrProxy", ip_port); - setRestartRequired(true); - } - } + QSettings settings; + + switch (option) { + case StartAtStartup: + successful = GUIUtil::SetStartOnSystemStartup(value.toBool()); break; - case ProxyPort: { - auto ip_port = GetProxySetting(settings, "addrProxy"); - if (!ip_port.is_set || ip_port.port != value.toString()) { - ip_port.port = value.toString(); - SetProxySetting(settings, "addrProxy", ip_port); - setRestartRequired(true); - } - } + case ShowTrayIcon: + m_show_tray_icon = value.toBool(); + settings.setValue("fHideTrayIcon", !m_show_tray_icon); + Q_EMIT showTrayIconChanged(m_show_tray_icon); + break; + case MinimizeToTray: + fMinimizeToTray = value.toBool(); + settings.setValue("fMinimizeToTray", fMinimizeToTray); + break; + case MapPortUPnP: // core option - can be changed on-the-fly + settings.setValue("fUseUPnP", value.toBool()); + break; + case MapPortNatpmp: // core option - can be changed on-the-fly + settings.setValue("fUseNatpmp", value.toBool()); + break; + case MinimizeOnClose: + fMinimizeOnClose = value.toBool(); + settings.setValue("fMinimizeOnClose", fMinimizeOnClose); break; - // separate Tor proxy - case ProxyUseTor: - if (settings.value("fUseSeparateProxyTor") != value) { - settings.setValue("fUseSeparateProxyTor", value.toBool()); - setRestartRequired(true); - } - break; - case ProxyIPTor: { - auto ip_port = GetProxySetting(settings, "addrSeparateProxyTor"); - if (!ip_port.is_set || ip_port.ip != value.toString()) { - ip_port.ip = value.toString(); - SetProxySetting(settings, "addrSeparateProxyTor", ip_port); - setRestartRequired(true); - } + // default proxy + case ProxyUse: + if (settings.value("fUseProxy") != value) { + settings.setValue("fUseProxy", value.toBool()); + setRestartRequired(true); } break; - case ProxyPortTor: { - auto ip_port = GetProxySetting(settings, "addrSeparateProxyTor"); - if (!ip_port.is_set || ip_port.port != value.toString()) { - ip_port.port = value.toString(); - SetProxySetting(settings, "addrSeparateProxyTor", ip_port); - setRestartRequired(true); - } + case ProxyIP: { + auto ip_port = GetProxySetting(settings, "addrProxy"); + if (!ip_port.is_set || ip_port.ip != value.toString()) { + ip_port.ip = value.toString(); + SetProxySetting(settings, "addrProxy", ip_port); + setRestartRequired(true); + } + } + break; + case ProxyPort: { + auto ip_port = GetProxySetting(settings, "addrProxy"); + if (!ip_port.is_set || ip_port.port != value.toString()) { + ip_port.port = value.toString(); + SetProxySetting(settings, "addrProxy", ip_port); + setRestartRequired(true); + } + } + break; + + // separate Tor proxy + case ProxyUseTor: + if (settings.value("fUseSeparateProxyTor") != value) { + settings.setValue("fUseSeparateProxyTor", value.toBool()); + setRestartRequired(true); } break; + case ProxyIPTor: { + auto ip_port = GetProxySetting(settings, "addrSeparateProxyTor"); + if (!ip_port.is_set || ip_port.ip != value.toString()) { + ip_port.ip = value.toString(); + SetProxySetting(settings, "addrSeparateProxyTor", ip_port); + setRestartRequired(true); + } + } + break; + case ProxyPortTor: { + auto ip_port = GetProxySetting(settings, "addrSeparateProxyTor"); + if (!ip_port.is_set || ip_port.port != value.toString()) { + ip_port.port = value.toString(); + SetProxySetting(settings, "addrSeparateProxyTor", ip_port); + setRestartRequired(true); + } + } + break; #ifdef ENABLE_WALLET - case SpendZeroConfChange: - if (settings.value("bSpendZeroConfChange") != value) { - settings.setValue("bSpendZeroConfChange", value); - setRestartRequired(true); - } - break; - case ExternalSignerPath: - if (settings.value("external_signer_path") != value.toString()) { - settings.setValue("external_signer_path", value.toString()); - setRestartRequired(true); - } - break; - case SubFeeFromAmount: - m_sub_fee_from_amount = value.toBool(); - settings.setValue("SubFeeFromAmount", m_sub_fee_from_amount); - break; + case SpendZeroConfChange: + if (settings.value("bSpendZeroConfChange") != value) { + settings.setValue("bSpendZeroConfChange", value); + setRestartRequired(true); + } + break; + case ExternalSignerPath: + if (settings.value("external_signer_path") != value.toString()) { + settings.setValue("external_signer_path", value.toString()); + setRestartRequired(true); + } + break; + case SubFeeFromAmount: + m_sub_fee_from_amount = value.toBool(); + settings.setValue("SubFeeFromAmount", m_sub_fee_from_amount); + break; #endif - case DisplayUnit: - setDisplayUnit(value); - break; - case ThirdPartyTxUrls: - if (strThirdPartyTxUrls != value.toString()) { - strThirdPartyTxUrls = value.toString(); - settings.setValue("strThirdPartyTxUrls", strThirdPartyTxUrls); - setRestartRequired(true); - } - break; - case Language: - if (settings.value("language") != value) { - settings.setValue("language", value); - setRestartRequired(true); - } - break; - case UseEmbeddedMonospacedFont: - m_use_embedded_monospaced_font = value.toBool(); - settings.setValue("UseEmbeddedMonospacedFont", m_use_embedded_monospaced_font); - Q_EMIT useEmbeddedMonospacedFontChanged(m_use_embedded_monospaced_font); - break; - case CoinControlFeatures: - fCoinControlFeatures = value.toBool(); - settings.setValue("fCoinControlFeatures", fCoinControlFeatures); - Q_EMIT coinControlFeaturesChanged(fCoinControlFeatures); - break; - case EnablePSBTControls: - m_enable_psbt_controls = value.toBool(); - settings.setValue("enable_psbt_controls", m_enable_psbt_controls); - break; - case Prune: - if (settings.value("bPrune") != value) { - settings.setValue("bPrune", value); - setRestartRequired(true); - } - break; - case PruneSize: - if (settings.value("nPruneSize") != value) { - settings.setValue("nPruneSize", value); - setRestartRequired(true); - } - break; - case DatabaseCache: - if (settings.value("nDatabaseCache") != value) { - settings.setValue("nDatabaseCache", value); - setRestartRequired(true); - } - break; - case ThreadsScriptVerif: - if (settings.value("nThreadsScriptVerif") != value) { - settings.setValue("nThreadsScriptVerif", value); - setRestartRequired(true); - } - break; - case Listen: - if (settings.value("fListen") != value) { - settings.setValue("fListen", value); - setRestartRequired(true); - } - break; - case Server: - if (settings.value("server") != value) { - settings.setValue("server", value); - setRestartRequired(true); - } - break; - default: - break; + case DisplayUnit: + setDisplayUnit(value); + break; + case ThirdPartyTxUrls: + if (strThirdPartyTxUrls != value.toString()) { + strThirdPartyTxUrls = value.toString(); + settings.setValue("strThirdPartyTxUrls", strThirdPartyTxUrls); + setRestartRequired(true); + } + break; + case Language: + if (settings.value("language") != value) { + settings.setValue("language", value); + setRestartRequired(true); + } + break; + case UseEmbeddedMonospacedFont: + m_use_embedded_monospaced_font = value.toBool(); + settings.setValue("UseEmbeddedMonospacedFont", m_use_embedded_monospaced_font); + Q_EMIT useEmbeddedMonospacedFontChanged(m_use_embedded_monospaced_font); + break; + case CoinControlFeatures: + fCoinControlFeatures = value.toBool(); + settings.setValue("fCoinControlFeatures", fCoinControlFeatures); + Q_EMIT coinControlFeaturesChanged(fCoinControlFeatures); + break; + case EnablePSBTControls: + m_enable_psbt_controls = value.toBool(); + settings.setValue("enable_psbt_controls", m_enable_psbt_controls); + break; + case Prune: + if (settings.value("bPrune") != value) { + settings.setValue("bPrune", value); + setRestartRequired(true); + } + break; + case PruneSize: + if (settings.value("nPruneSize") != value) { + settings.setValue("nPruneSize", value); + setRestartRequired(true); + } + break; + case DatabaseCache: + if (settings.value("nDatabaseCache") != value) { + settings.setValue("nDatabaseCache", value); + setRestartRequired(true); } + break; + case ThreadsScriptVerif: + if (settings.value("nThreadsScriptVerif") != value) { + settings.setValue("nThreadsScriptVerif", value); + setRestartRequired(true); + } + break; + case Listen: + if (settings.value("fListen") != value) { + settings.setValue("fListen", value); + setRestartRequired(true); + } + break; + case Server: + if (settings.value("server") != value) { + settings.setValue("server", value); + setRestartRequired(true); + } + break; + default: + break; } - Q_EMIT dataChanged(index, index); - return successful; } -/** Updates current unit in memory, settings and emits displayUnitChanged(newUnit) signal */ -void OptionsModel::setDisplayUnit(const QVariant &value) +void OptionsModel::setDisplayUnit(const QVariant& new_unit) { - if (!value.isNull()) - { - QSettings settings; - nDisplayUnit = value.toInt(); - settings.setValue("nDisplayUnit", nDisplayUnit); - Q_EMIT displayUnitChanged(nDisplayUnit); - } + if (new_unit.isNull() || new_unit.value<BitcoinUnit>() == m_display_bitcoin_unit) return; + m_display_bitcoin_unit = new_unit.value<BitcoinUnit>(); + QSettings settings; + settings.setValue("DisplayBitcoinUnit", QVariant::fromValue(m_display_bitcoin_unit)); + Q_EMIT displayUnitChanged(m_display_bitcoin_unit); } void OptionsModel::setRestartRequired(bool fRequired) diff --git a/src/qt/optionsmodel.h b/src/qt/optionsmodel.h index bb9a8c1f8c..92f80ecf21 100644 --- a/src/qt/optionsmodel.h +++ b/src/qt/optionsmodel.h @@ -6,6 +6,7 @@ #define BITCOIN_QT_OPTIONSMODEL_H #include <cstdint> +#include <qt/bitcoinunits.h> #include <qt/guiconstants.h> #include <QAbstractListModel> @@ -40,7 +41,7 @@ class OptionsModel : public QAbstractListModel Q_OBJECT public: - explicit OptionsModel(QObject *parent = nullptr, bool resetSettings = false); + explicit OptionsModel(interfaces::Node& node, QObject *parent = nullptr, bool resetSettings = false); enum OptionID { StartAtStartup, // bool @@ -55,7 +56,7 @@ public: ProxyUseTor, // bool ProxyIPTor, // QString ProxyPortTor, // int - DisplayUnit, // BitcoinUnits::Unit + DisplayUnit, // BitcoinUnit ThirdPartyTxUrls, // QString Language, // QString UseEmbeddedMonospacedFont, // bool @@ -79,14 +80,16 @@ public: int rowCount(const QModelIndex & parent = QModelIndex()) const override; QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const override; bool setData(const QModelIndex & index, const QVariant & value, int role = Qt::EditRole) override; - /** Updates current unit in memory, settings and emits displayUnitChanged(newUnit) signal */ - void setDisplayUnit(const QVariant &value); + QVariant getOption(OptionID option) const; + bool setOption(OptionID option, const QVariant& value); + /** Updates current unit in memory, settings and emits displayUnitChanged(new_unit) signal */ + void setDisplayUnit(const QVariant& new_unit); /* Explicit getters */ bool getShowTrayIcon() const { return m_show_tray_icon; } bool getMinimizeToTray() const { return fMinimizeToTray; } bool getMinimizeOnClose() const { return fMinimizeOnClose; } - int getDisplayUnit() const { return nDisplayUnit; } + BitcoinUnit getDisplayUnit() const { return m_display_bitcoin_unit; } QString getThirdPartyTxUrls() const { return strThirdPartyTxUrls; } bool getUseEmbeddedMonospacedFont() const { return m_use_embedded_monospaced_font; } bool getCoinControlFeatures() const { return fCoinControlFeatures; } @@ -102,17 +105,16 @@ public: void setRestartRequired(bool fRequired); bool isRestartRequired() const; - interfaces::Node& node() const { assert(m_node); return *m_node; } - void setNode(interfaces::Node& node) { assert(!m_node); m_node = &node; } + interfaces::Node& node() const { return m_node; } private: - interfaces::Node* m_node = nullptr; + interfaces::Node& m_node; /* Qt-only settings */ bool m_show_tray_icon; bool fMinimizeToTray; bool fMinimizeOnClose; QString language; - int nDisplayUnit; + BitcoinUnit m_display_bitcoin_unit; QString strThirdPartyTxUrls; bool m_use_embedded_monospaced_font; bool fCoinControlFeatures; @@ -127,7 +129,7 @@ private: // Check settings version and upgrade default values if required void checkAndMigrate(); Q_SIGNALS: - void displayUnitChanged(int unit); + void displayUnitChanged(BitcoinUnit unit); void coinControlFeaturesChanged(bool); void showTrayIconChanged(bool); void useEmbeddedMonospacedFontChanged(bool); diff --git a/src/qt/overviewpage.cpp b/src/qt/overviewpage.cpp index 7127706463..a8133f481e 100644 --- a/src/qt/overviewpage.cpp +++ b/src/qt/overviewpage.cpp @@ -34,9 +34,8 @@ class TxViewDelegate : public QAbstractItemDelegate { Q_OBJECT public: - explicit TxViewDelegate(const PlatformStyle *_platformStyle, QObject *parent=nullptr): - QAbstractItemDelegate(parent), unit(BitcoinUnits::BTC), - platformStyle(_platformStyle) + explicit TxViewDelegate(const PlatformStyle* _platformStyle, QObject* parent = nullptr) + : QAbstractItemDelegate(parent), platformStyle(_platformStyle) { connect(this, &TxViewDelegate::width_changed, this, &TxViewDelegate::sizeHintChanged); } @@ -125,7 +124,7 @@ public: return {DECORATION_SIZE + 8 + minimum_text_width, DECORATION_SIZE}; } - int unit; + BitcoinUnit unit{BitcoinUnit::BTC}; Q_SIGNALS: //! An intermediate signal for emitting from the `paint() const` member function. @@ -197,7 +196,7 @@ OverviewPage::~OverviewPage() void OverviewPage::setBalance(const interfaces::WalletBalances& balances) { - int unit = walletModel->getOptionsModel()->getDisplayUnit(); + BitcoinUnit unit = walletModel->getOptionsModel()->getDisplayUnit(); m_balances = balances; if (walletModel->wallet().isLegacy()) { if (walletModel->wallet().privateKeysDisabled()) { diff --git a/src/qt/paymentserver.cpp b/src/qt/paymentserver.cpp index c82f0683fc..be6f604932 100644 --- a/src/qt/paymentserver.cpp +++ b/src/qt/paymentserver.cpp @@ -158,9 +158,7 @@ PaymentServer::PaymentServer(QObject* parent, bool startLocalServer) : } } -PaymentServer::~PaymentServer() -{ -} +PaymentServer::~PaymentServer() = default; // // OSX-specific way of handling bitcoin: URIs diff --git a/src/qt/peertablemodel.cpp b/src/qt/peertablemodel.cpp index 563bca76e5..b7de88225e 100644 --- a/src/qt/peertablemodel.cpp +++ b/src/qt/peertablemodel.cpp @@ -28,10 +28,7 @@ PeerTableModel::PeerTableModel(interfaces::Node& node, QObject* parent) : refresh(); } -PeerTableModel::~PeerTableModel() -{ - // Intentionally left empty -} +PeerTableModel::~PeerTableModel() = default; void PeerTableModel::startAutoRefresh() { @@ -71,6 +68,8 @@ QVariant PeerTableModel::data(const QModelIndex& index, int role) const switch (column) { case NetNodeId: return (qint64)rec->nodeStats.nodeid; + case Age: + return GUIUtil::FormatPeerAge(rec->nodeStats.m_connected); case Address: return QString::fromStdString(rec->nodeStats.m_addr_name); case Direction: @@ -96,6 +95,7 @@ QVariant PeerTableModel::data(const QModelIndex& index, int role) const } else if (role == Qt::TextAlignmentRole) { switch (column) { case NetNodeId: + case Age: return QVariant(Qt::AlignRight | Qt::AlignVCenter); case Address: return {}; diff --git a/src/qt/peertablemodel.h b/src/qt/peertablemodel.h index 11064cdbfe..e2515de775 100644 --- a/src/qt/peertablemodel.h +++ b/src/qt/peertablemodel.h @@ -47,6 +47,7 @@ public: enum ColumnIndex { NetNodeId = 0, + Age, Address, Direction, ConnectionType, @@ -82,6 +83,9 @@ private: /*: Title of Peers Table column which contains a unique number used to identify a connection. */ tr("Peer"), + /*: Title of Peers Table column which indicates the duration (length of time) + since the peer connection started. */ + tr("Age"), /*: Title of Peers Table column which contains the IP/Onion/I2P address of the connected peer. */ tr("Address"), diff --git a/src/qt/peertablesortproxy.cpp b/src/qt/peertablesortproxy.cpp index 26fedb4127..d87f10c365 100644 --- a/src/qt/peertablesortproxy.cpp +++ b/src/qt/peertablesortproxy.cpp @@ -24,6 +24,8 @@ bool PeerTableSortProxy::lessThan(const QModelIndex& left_index, const QModelInd switch (static_cast<PeerTableModel::ColumnIndex>(left_index.column())) { case PeerTableModel::NetNodeId: return left_stats.nodeid < right_stats.nodeid; + case PeerTableModel::Age: + return left_stats.m_connected > right_stats.m_connected; case PeerTableModel::Address: return left_stats.m_addr_name.compare(right_stats.m_addr_name) < 0; case PeerTableModel::Direction: diff --git a/src/qt/psbtoperationsdialog.cpp b/src/qt/psbtoperationsdialog.cpp index 6880c157c0..333766ce21 100644 --- a/src/qt/psbtoperationsdialog.cpp +++ b/src/qt/psbtoperationsdialog.cpp @@ -181,7 +181,7 @@ std::string PSBTOperationsDialog::renderTransaction(const PartiallySignedTransac ExtractDestination(out.scriptPubKey, address); totalAmount += out.nValue; tx_description.append(tr(" * Sends %1 to %2") - .arg(BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, out.nValue)) + .arg(BitcoinUnits::formatWithUnit(BitcoinUnit::BTC, out.nValue)) .arg(QString::fromStdString(EncodeDestination(address)))); tx_description.append("<br>"); } @@ -193,7 +193,7 @@ std::string PSBTOperationsDialog::renderTransaction(const PartiallySignedTransac tx_description.append(tr("Unable to calculate transaction fee or total transaction amount.")); } else { tx_description.append(tr("Pays transaction fee: ")); - tx_description.append(BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, *analysis.fee)); + tx_description.append(BitcoinUnits::formatWithUnit(BitcoinUnit::BTC, *analysis.fee)); // add total amount in all subdivision units tx_description.append("<hr />"); diff --git a/src/qt/recentrequeststablemodel.cpp b/src/qt/recentrequeststablemodel.cpp index 03ca9ad7dc..061513b58f 100644 --- a/src/qt/recentrequeststablemodel.cpp +++ b/src/qt/recentrequeststablemodel.cpp @@ -34,10 +34,7 @@ RecentRequestsTableModel::RecentRequestsTableModel(WalletModel *parent) : connect(walletModel->getOptionsModel(), &OptionsModel::displayUnitChanged, this, &RecentRequestsTableModel::updateDisplayUnit); } -RecentRequestsTableModel::~RecentRequestsTableModel() -{ - /* Intentionally left empty */ -} +RecentRequestsTableModel::~RecentRequestsTableModel() = default; int RecentRequestsTableModel::rowCount(const QModelIndex &parent) const { diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp index dcc4f36aaa..7d18bfb229 100644 --- a/src/qt/rpcconsole.cpp +++ b/src/qt/rpcconsole.cpp @@ -120,7 +120,7 @@ public: connect(&timer, &QTimer::timeout, [this]{ func(); }); timer.start(millis); } - ~QtRPCTimerBase() {} + ~QtRPCTimerBase() = default; private: QTimer timer; std::function<void()> func; @@ -129,7 +129,7 @@ private: class QtRPCTimerInterface: public RPCTimerInterface { public: - ~QtRPCTimerInterface() {} + ~QtRPCTimerInterface() = default; const char *Name() override { return "Qt"; } RPCTimerBase* NewTimer(std::function<void()>& func, int64_t millis) override { @@ -457,7 +457,7 @@ void RPCExecutor::request(const QString &command, const WalletModel* wallet_mode { try // Nice formatting for standard-format error { - int code = find_value(objError, "code").get_int(); + int code = find_value(objError, "code").getInt<int>(); std::string message = find_value(objError, "message").get_str(); Q_EMIT reply(RPCConsole::CMD_ERROR, QString::fromStdString(message) + " (code " + QString::number(code) + ")"); } @@ -618,17 +618,16 @@ bool RPCConsole::eventFilter(QObject* obj, QEvent *event) case Qt::Key_Down: if(obj == ui->lineEdit) { browseHistory(1); return true; } break; case Qt::Key_PageUp: /* pass paging keys to messages widget */ case Qt::Key_PageDown: - if(obj == ui->lineEdit) - { - QApplication::postEvent(ui->messagesWidget, new QKeyEvent(*keyevt)); + if (obj == ui->lineEdit) { + QApplication::sendEvent(ui->messagesWidget, keyevt); return true; } break; case Qt::Key_Return: case Qt::Key_Enter: // forward these events to lineEdit - if(obj == autoCompleter->popup()) { - QApplication::postEvent(ui->lineEdit, new QKeyEvent(*keyevt)); + if (obj == autoCompleter->popup()) { + QApplication::sendEvent(ui->lineEdit, keyevt); autoCompleter->popup()->hide(); return true; } @@ -642,7 +641,7 @@ bool RPCConsole::eventFilter(QObject* obj, QEvent *event) ((mod & Qt::ShiftModifier) && key == Qt::Key_Insert))) { ui->lineEdit->setFocus(); - QApplication::postEvent(ui->lineEdit, new QKeyEvent(*keyevt)); + QApplication::sendEvent(ui->lineEdit, keyevt); return true; } } @@ -694,6 +693,7 @@ void RPCConsole::setClientModel(ClientModel *model, int bestblock_height, int64_ ui->peerWidget->setColumnWidth(PeerTableModel::Subversion, SUBVERSION_COLUMN_WIDTH); ui->peerWidget->setColumnWidth(PeerTableModel::Ping, PING_COLUMN_WIDTH); } + ui->peerWidget->horizontalHeader()->setSectionResizeMode(PeerTableModel::Age, QHeaderView::ResizeToContents); ui->peerWidget->horizontalHeader()->setStretchLastSection(true); ui->peerWidget->setItemDelegateForColumn(PeerTableModel::NetNodeId, new PeerIdViewDelegate(this)); @@ -726,6 +726,7 @@ void RPCConsole::setClientModel(ClientModel *model, int bestblock_height, int64_ ui->banlistWidget->setColumnWidth(BanTableModel::Address, BANSUBNET_COLUMN_WIDTH); ui->banlistWidget->setColumnWidth(BanTableModel::Bantime, BANTIME_COLUMN_WIDTH); } + ui->banlistWidget->horizontalHeader()->setSectionResizeMode(BanTableModel::Address, QHeaderView::ResizeToContents); ui->banlistWidget->horizontalHeader()->setStretchLastSection(true); // create ban table context menu @@ -870,7 +871,7 @@ void RPCConsole::clear(bool keep_prompt) } // Set default style sheet -#ifdef Q_OS_MAC +#ifdef Q_OS_MACOS QFontInfo fixedFontInfo(GUIUtil::fixedPitchFont(/*use_embedded_font=*/true)); #else QFontInfo fixedFontInfo(GUIUtil::fixedPitchFont()); @@ -1031,8 +1032,9 @@ void RPCConsole::on_lineEdit_returnPressed() ui->lineEdit->clear(); + WalletModel* wallet_model{nullptr}; #ifdef ENABLE_WALLET - WalletModel* wallet_model = ui->WalletSelector->currentData().value<WalletModel*>(); + wallet_model = ui->WalletSelector->currentData().value<WalletModel*>(); if (m_last_wallet_model != wallet_model) { if (wallet_model) { @@ -1048,7 +1050,10 @@ void RPCConsole::on_lineEdit_returnPressed() //: A console message indicating an entered command is currently being executed. message(CMD_REPLY, tr("Executing…")); m_is_executing = true; - Q_EMIT cmdRequest(cmd, m_last_wallet_model); + + QMetaObject::invokeMethod(m_executor, [this, cmd, wallet_model] { + m_executor->request(cmd, wallet_model); + }); cmd = QString::fromStdString(strFilteredCmd); @@ -1090,11 +1095,11 @@ void RPCConsole::browseHistory(int offset) void RPCConsole::startExecutor() { - RPCExecutor *executor = new RPCExecutor(m_node); - executor->moveToThread(&thread); + m_executor = new RPCExecutor(m_node); + m_executor->moveToThread(&thread); // Replies from executor object must go to this object - connect(executor, &RPCExecutor::reply, this, [this](int category, const QString& command) { + connect(m_executor, &RPCExecutor::reply, this, [this](int category, const QString& command) { // Remove "Executing…" message. ui->messagesWidget->undo(); message(category, command); @@ -1102,16 +1107,13 @@ void RPCConsole::startExecutor() m_is_executing = false; }); - // Requests from this object must go to executor - connect(this, &RPCConsole::cmdRequest, executor, &RPCExecutor::request); - // Make sure executor object is deleted in its own thread - connect(&thread, &QThread::finished, executor, &RPCExecutor::deleteLater); + connect(&thread, &QThread::finished, m_executor, &RPCExecutor::deleteLater); // Default implementation of QThread::run() simply spins up an event loop in the thread, // which is what we want. thread.start(); - QTimer::singleShot(0, executor, []() { + QTimer::singleShot(0, m_executor, []() { util::ThreadRename("qt-rpcconsole"); }); } diff --git a/src/qt/rpcconsole.h b/src/qt/rpcconsole.h index 528e2bef7d..1a54fe0cad 100644 --- a/src/qt/rpcconsole.h +++ b/src/qt/rpcconsole.h @@ -5,6 +5,10 @@ #ifndef BITCOIN_QT_RPCCONSOLE_H #define BITCOIN_QT_RPCCONSOLE_H +#if defined(HAVE_CONFIG_H) +#include <config/bitcoin-config.h> +#endif + #include <qt/guiutil.h> #include <qt/peertablemodel.h> @@ -17,6 +21,7 @@ class ClientModel; class PlatformStyle; +class RPCExecutor; class RPCTimerInterface; class WalletModel; @@ -49,8 +54,11 @@ public: } void setClientModel(ClientModel *model = nullptr, int bestblock_height = 0, int64_t bestblock_date = 0, double verification_progress = 0.0); - void addWallet(WalletModel * const walletModel); + +#ifdef ENABLE_WALLET + void addWallet(WalletModel* const walletModel); void removeWallet(WalletModel* const walletModel); +#endif // ENABLE_WALLET enum MessageClass { MC_ERROR, @@ -129,10 +137,6 @@ public Q_SLOTS: /** set which tab has the focus (is visible) */ void setTabFocus(enum TabTypes tabType); -Q_SIGNALS: - // For RPC command executor - void cmdRequest(const QString &command, const WalletModel* wallet_model); - private: struct TranslatedStrings { const QString yes{tr("Yes")}, no{tr("No")}, to{tr("To")}, from{tr("From")}, @@ -166,6 +170,7 @@ private: int consoleFontSize = 0; QCompleter *autoCompleter = nullptr; QThread thread; + RPCExecutor* m_executor{nullptr}; WalletModel* m_last_wallet_model{nullptr}; bool m_is_executing{false}; QByteArray m_peer_widget_header_state; diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index c924789796..fd8eccb86d 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -380,8 +380,7 @@ bool SendCoinsDialog::PrepareSendText(QString& question_string, QString& informa question_string.append("<hr />"); CAmount totalAmount = m_current_transaction->getTotalTransactionAmount() + txFee; QStringList alternativeUnits; - for (const BitcoinUnits::Unit u : BitcoinUnits::availableUnits()) - { + for (const BitcoinUnit u : BitcoinUnits::availableUnits()) { if(u != model->getOptionsModel()->getDisplayUnit()) alternativeUnits.append(BitcoinUnits::formatHtmlWithUnit(u, totalAmount)); } diff --git a/src/qt/signverifymessagedialog.cpp b/src/qt/signverifymessagedialog.cpp index 74bedbf020..1f4b30534b 100644 --- a/src/qt/signverifymessagedialog.cpp +++ b/src/qt/signverifymessagedialog.cpp @@ -91,7 +91,7 @@ void SignVerifyMessageDialog::on_addressBookButton_SM_clicked() { if (model && model->getAddressTableModel()) { - model->refresh(/* pk_hash_only */ true); + model->refresh(/*pk_hash_only=*/true); AddressBookPage dlg(platformStyle, AddressBookPage::ForSelection, AddressBookPage::ReceivingTab, this); dlg.setModel(model->getAddressTableModel()); if (dlg.exec()) diff --git a/src/qt/test/addressbooktests.cpp b/src/qt/test/addressbooktests.cpp index 66637a5dcf..ededde4da9 100644 --- a/src/qt/test/addressbooktests.cpp +++ b/src/qt/test/addressbooktests.cpp @@ -8,6 +8,7 @@ #include <interfaces/chain.h> #include <interfaces/node.h> +#include <qt/addressbookpage.h> #include <qt/clientmodel.h> #include <qt/editaddressdialog.h> #include <qt/optionsmodel.h> @@ -23,8 +24,10 @@ #include <chrono> #include <QApplication> -#include <QTimer> +#include <QLineEdit> #include <QMessageBox> +#include <QTableView> +#include <QTimer> using wallet::AddWallet; using wallet::CWallet; @@ -100,11 +103,13 @@ void TestAddAddressesToSendBook(interfaces::Node& node) QString s_label("already here (s)"); // Define a new address (which should add to the address book successfully). - QString new_address; + QString new_address_a; + QString new_address_b; std::tie(r_key_dest, preexisting_r_address) = build_address(); std::tie(s_key_dest, preexisting_s_address) = build_address(); - std::tie(std::ignore, new_address) = build_address(); + std::tie(std::ignore, new_address_a) = build_address(); + std::tie(std::ignore, new_address_b) = build_address(); { LOCK(wallet->cs_wallet); @@ -122,7 +127,7 @@ void TestAddAddressesToSendBook(interfaces::Node& node) // Initialize relevant QT models. std::unique_ptr<const PlatformStyle> platformStyle(PlatformStyle::instantiate("other")); - OptionsModel optionsModel; + OptionsModel optionsModel(node); ClientModel clientModel(node, &optionsModel); WalletContext& context = *node.walletLoader().context(); AddWallet(context, wallet); @@ -131,14 +136,19 @@ void TestAddAddressesToSendBook(interfaces::Node& node) EditAddressDialog editAddressDialog(EditAddressDialog::NewSendingAddress); editAddressDialog.setModel(walletModel.getAddressTableModel()); + AddressBookPage address_book{platformStyle.get(), AddressBookPage::ForEditing, AddressBookPage::SendingTab}; + address_book.setModel(walletModel.getAddressTableModel()); + auto table_view = address_book.findChild<QTableView*>("tableView"); + QCOMPARE(table_view->model()->rowCount(), 1); + EditAddressAndSubmit( &editAddressDialog, QString("uhoh"), preexisting_r_address, QString( "Address \"%1\" already exists as a receiving address with label " "\"%2\" and so cannot be added as a sending address." ).arg(preexisting_r_address).arg(r_label)); - check_addbook_size(2); + QCOMPARE(table_view->model()->rowCount(), 1); EditAddressAndSubmit( &editAddressDialog, QString("uhoh, different"), preexisting_s_address, @@ -146,22 +156,65 @@ void TestAddAddressesToSendBook(interfaces::Node& node) "The entered address \"%1\" is already in the address book with " "label \"%2\"." ).arg(preexisting_s_address).arg(s_label)); - check_addbook_size(2); + QCOMPARE(table_view->model()->rowCount(), 1); // Submit a new address which should add successfully - we expect the // warning message to be blank. EditAddressAndSubmit( - &editAddressDialog, QString("new"), new_address, QString("")); - + &editAddressDialog, QString("io - new A"), new_address_a, QString("")); check_addbook_size(3); + QCOMPARE(table_view->model()->rowCount(), 2); + + EditAddressAndSubmit( + &editAddressDialog, QString("io - new B"), new_address_b, QString("")); + check_addbook_size(4); + QCOMPARE(table_view->model()->rowCount(), 3); + + auto search_line = address_book.findChild<QLineEdit*>("searchLineEdit"); + + search_line->setText(r_label); + QCOMPARE(table_view->model()->rowCount(), 0); + + search_line->setText(s_label); + QCOMPARE(table_view->model()->rowCount(), 1); + + search_line->setText("io"); + QCOMPARE(table_view->model()->rowCount(), 2); + + // Check wilcard "?". + search_line->setText("io?new"); + QCOMPARE(table_view->model()->rowCount(), 0); + search_line->setText("io???new"); + QCOMPARE(table_view->model()->rowCount(), 2); + + // Check wilcard "*". + search_line->setText("io*new"); + QCOMPARE(table_view->model()->rowCount(), 2); + search_line->setText("*"); + QCOMPARE(table_view->model()->rowCount(), 3); + + search_line->setText(preexisting_r_address); + QCOMPARE(table_view->model()->rowCount(), 0); + + search_line->setText(preexisting_s_address); + QCOMPARE(table_view->model()->rowCount(), 1); + + search_line->setText(new_address_a); + QCOMPARE(table_view->model()->rowCount(), 1); + + search_line->setText(new_address_b); + QCOMPARE(table_view->model()->rowCount(), 1); + + search_line->setText(""); + QCOMPARE(table_view->model()->rowCount(), 3); } } // namespace void AddressBookTests::addressBookTests() { -#ifdef Q_OS_MAC +#ifdef Q_OS_MACOS if (QApplication::platformName() == "minimal") { // Disable for mac on "minimal" platform to avoid crashes inside the Qt // framework when it tries to look up unimplemented cocoa functions, diff --git a/src/qt/test/apptests.cpp b/src/qt/test/apptests.cpp index 099ac5fcae..9648ef6188 100644 --- a/src/qt/test/apptests.cpp +++ b/src/qt/test/apptests.cpp @@ -58,7 +58,7 @@ void TestRpcCommand(RPCConsole* console) //! Entry point for BitcoinApplication tests. void AppTests::appTests() { -#ifdef Q_OS_MAC +#ifdef Q_OS_MACOS if (QApplication::platformName() == "minimal") { // Disable for mac on "minimal" platform to avoid crashes inside the Qt // framework when it tries to look up unimplemented cocoa functions, @@ -119,6 +119,6 @@ AppTests::HandleCallback::~HandleCallback() assert(it != callbacks.end()); callbacks.erase(it); if (callbacks.empty()) { - m_app_tests.m_app.quit(); + m_app_tests.m_app.exit(0); } } diff --git a/src/qt/test/optiontests.cpp b/src/qt/test/optiontests.cpp index 4a943a6343..2ef9230c59 100644 --- a/src/qt/test/optiontests.cpp +++ b/src/qt/test/optiontests.cpp @@ -13,8 +13,7 @@ #include <univalue.h> -//! Entry point for BitcoinApplication tests. -void OptionTests::optionTests() +void OptionTests::integerGetArgBug() { // Test regression https://github.com/bitcoin/bitcoin/issues/24457. Ensure // that setting integer prune value doesn't cause an exception to be thrown @@ -24,7 +23,7 @@ void OptionTests::optionTests() settings.rw_settings["prune"] = 3814; }); gArgs.WriteSettingsFile(); - OptionsModel{}; + OptionsModel{m_node}; gArgs.LockSettings([&](util::Settings& settings) { settings.rw_settings.erase("prune"); }); @@ -49,7 +48,7 @@ void OptionTests::parametersInteraction() QSettings settings; settings.setValue("fListen", false); - OptionsModel{}; + OptionsModel{m_node}; const bool expected{false}; diff --git a/src/qt/test/optiontests.h b/src/qt/test/optiontests.h index 39c1612c8f..257a0b65be 100644 --- a/src/qt/test/optiontests.h +++ b/src/qt/test/optiontests.h @@ -16,7 +16,7 @@ public: explicit OptionTests(interfaces::Node& node) : m_node(node) {} private Q_SLOTS: - void optionTests(); + void integerGetArgBug(); void parametersInteraction(); private: diff --git a/src/qt/test/test_main.cpp b/src/qt/test/test_main.cpp index 07d256f05a..aeedd92834 100644 --- a/src/qt/test/test_main.cpp +++ b/src/qt/test/test_main.cpp @@ -21,8 +21,10 @@ #endif // ENABLE_WALLET #include <QApplication> +#include <QDebug> #include <QObject> #include <QTest> + #include <functional> #if defined(QT_STATICPLUGIN) @@ -69,8 +71,6 @@ int main(int argc, char* argv[]) gArgs.ForceSetArg("-upnp", "0"); gArgs.ForceSetArg("-natpmp", "0"); - bool fInvalid = false; - // Prefer the "minimal" platform for the test instead of the normal default // platform ("xcb", "windows", or "cocoa") so tests can't unintentionally // interfere with any background GUIs and don't require extra resources. @@ -86,32 +86,32 @@ int main(int argc, char* argv[]) app.setApplicationName("Bitcoin-Qt-test"); app.createNode(*init); + int num_test_failures{0}; + AppTests app_tests(app); - if (QTest::qExec(&app_tests) != 0) { - fInvalid = true; - } + num_test_failures += QTest::qExec(&app_tests); + OptionTests options_tests(app.node()); - if (QTest::qExec(&options_tests) != 0) { - fInvalid = true; - } + num_test_failures += QTest::qExec(&options_tests); + URITests test1; - if (QTest::qExec(&test1) != 0) { - fInvalid = true; - } + num_test_failures += QTest::qExec(&test1); + RPCNestedTests test3(app.node()); - if (QTest::qExec(&test3) != 0) { - fInvalid = true; - } + num_test_failures += QTest::qExec(&test3); + #ifdef ENABLE_WALLET WalletTests test5(app.node()); - if (QTest::qExec(&test5) != 0) { - fInvalid = true; - } + num_test_failures += QTest::qExec(&test5); + AddressBookTests test6(app.node()); - if (QTest::qExec(&test6) != 0) { - fInvalid = true; - } + num_test_failures += QTest::qExec(&test6); #endif - return fInvalid; + if (num_test_failures) { + qWarning("\nFailed tests: %d\n", num_test_failures); + } else { + qDebug("\nAll tests passed.\n"); + } + return num_test_failures; } diff --git a/src/qt/test/wallettests.cpp b/src/qt/test/wallettests.cpp index 6ab534764b..bc06f0f23b 100644 --- a/src/qt/test/wallettests.cpp +++ b/src/qt/test/wallettests.cpp @@ -7,24 +7,25 @@ #include <interfaces/chain.h> #include <interfaces/node.h> +#include <key_io.h> #include <qt/bitcoinamountfield.h> +#include <qt/bitcoinunits.h> #include <qt/clientmodel.h> #include <qt/optionsmodel.h> +#include <qt/overviewpage.h> #include <qt/platformstyle.h> #include <qt/qvalidatedlineedit.h> +#include <qt/receivecoinsdialog.h> +#include <qt/receiverequestdialog.h> +#include <qt/recentrequeststablemodel.h> #include <qt/sendcoinsdialog.h> #include <qt/sendcoinsentry.h> #include <qt/transactiontablemodel.h> #include <qt/transactionview.h> #include <qt/walletmodel.h> -#include <key_io.h> #include <test/util/setup_common.h> #include <validation.h> #include <wallet/wallet.h> -#include <qt/overviewpage.h> -#include <qt/receivecoinsdialog.h> -#include <qt/recentrequeststablemodel.h> -#include <qt/receiverequestdialog.h> #include <chrono> #include <memory> @@ -183,7 +184,7 @@ void TestGUI(interfaces::Node& node) std::unique_ptr<const PlatformStyle> platformStyle(PlatformStyle::instantiate("other")); SendCoinsDialog sendCoinsDialog(platformStyle.get()); TransactionView transactionView(platformStyle.get()); - OptionsModel optionsModel; + OptionsModel optionsModel(node); ClientModel clientModel(node, &optionsModel); WalletContext& context = *node.walletLoader().context(); AddWallet(context, wallet); @@ -196,7 +197,7 @@ void TestGUI(interfaces::Node& node) // Check balance in send dialog QLabel* balanceLabel = sendCoinsDialog.findChild<QLabel*>("labelBalance"); QString balanceText = balanceLabel->text(); - int unit = walletModel.getOptionsModel()->getDisplayUnit(); + BitcoinUnit unit = walletModel.getOptionsModel()->getDisplayUnit(); CAmount balance = walletModel.wallet().getBalance(); QString balanceComparison = BitcoinUnits::formatWithUnit(unit, balance, false, BitcoinUnits::SeparatorStyle::ALWAYS); QCOMPARE(balanceText, balanceComparison); @@ -222,7 +223,7 @@ void TestGUI(interfaces::Node& node) overviewPage.setWalletModel(&walletModel); QLabel* balanceLabel = overviewPage.findChild<QLabel*>("labelBalance"); QString balanceText = balanceLabel->text().trimmed(); - int unit = walletModel.getOptionsModel()->getDisplayUnit(); + BitcoinUnit unit = walletModel.getOptionsModel()->getDisplayUnit(); CAmount balance = walletModel.wallet().getBalance(); QString balanceComparison = BitcoinUnits::formatWithUnit(unit, balance, false, BitcoinUnits::SeparatorStyle::ALWAYS); QCOMPARE(balanceText, balanceComparison); @@ -314,7 +315,7 @@ void TestGUI(interfaces::Node& node) void WalletTests::walletTests() { -#ifdef Q_OS_MAC +#ifdef Q_OS_MACOS if (QApplication::platformName() == "minimal") { // Disable for mac on "minimal" platform to avoid crashes inside the Qt // framework when it tries to look up unimplemented cocoa functions, diff --git a/src/qt/transactiondesc.cpp b/src/qt/transactiondesc.cpp index be5851d627..9e92f89543 100644 --- a/src/qt/transactiondesc.cpp +++ b/src/qt/transactiondesc.cpp @@ -32,20 +32,18 @@ using wallet::ISMINE_SPENDABLE; using wallet::ISMINE_WATCH_ONLY; using wallet::isminetype; -QString TransactionDesc::FormatTxStatus(const interfaces::WalletTx& wtx, const interfaces::WalletTxStatus& status, bool inMempool, int numBlocks) +QString TransactionDesc::FormatTxStatus(const interfaces::WalletTxStatus& status, bool inMempool) { - { - int nDepth = status.depth_in_main_chain; - if (nDepth < 0) { - return tr("conflicted with a transaction with %1 confirmations").arg(-nDepth); - } else if (nDepth == 0) { - const QString abandoned{status.is_abandoned ? QLatin1String(", ") + tr("abandoned") : QString()}; - return tr("0/unconfirmed, %1").arg(inMempool ? tr("in memory pool") : tr("not in memory pool")) + abandoned; - } else if (nDepth < 6) { - return tr("%1/unconfirmed").arg(nDepth); - } else { - return tr("%1 confirmations").arg(nDepth); - } + int depth = status.depth_in_main_chain; + if (depth < 0) { + return tr("conflicted with a transaction with %1 confirmations").arg(-depth); + } else if (depth == 0) { + const QString abandoned{status.is_abandoned ? QLatin1String(", ") + tr("abandoned") : QString()}; + return tr("0/unconfirmed, %1").arg(inMempool ? tr("in memory pool") : tr("not in memory pool")) + abandoned; + } else if (depth < 6) { + return tr("%1/unconfirmed").arg(depth); + } else { + return tr("%1 confirmations").arg(depth); } } @@ -77,7 +75,7 @@ bool GetPaymentRequestMerchant(const std::string& pr, QString& merchant) return false; } -QString TransactionDesc::toHTML(interfaces::Node& node, interfaces::Wallet& wallet, TransactionRecord *rec, int unit) +QString TransactionDesc::toHTML(interfaces::Node& node, interfaces::Wallet& wallet, TransactionRecord* rec, BitcoinUnit unit) { int numBlocks; interfaces::WalletTxStatus status; @@ -95,7 +93,7 @@ QString TransactionDesc::toHTML(interfaces::Node& node, interfaces::Wallet& wall CAmount nDebit = wtx.debit; CAmount nNet = nCredit - nDebit; - strHTML += "<b>" + tr("Status") + ":</b> " + FormatTxStatus(wtx, status, inMempool, numBlocks); + strHTML += "<b>" + tr("Status") + ":</b> " + FormatTxStatus(status, inMempool); strHTML += "<br>"; strHTML += "<b>" + tr("Date") + ":</b> " + (nTime ? GUIUtil::dateTimeStr(nTime) : "") + "<br>"; diff --git a/src/qt/transactiondesc.h b/src/qt/transactiondesc.h index cf955a433c..803e41b699 100644 --- a/src/qt/transactiondesc.h +++ b/src/qt/transactiondesc.h @@ -5,6 +5,8 @@ #ifndef BITCOIN_QT_TRANSACTIONDESC_H #define BITCOIN_QT_TRANSACTIONDESC_H +#include <qt/bitcoinunits.h> + #include <QObject> #include <QString> @@ -24,12 +26,12 @@ class TransactionDesc: public QObject Q_OBJECT public: - static QString toHTML(interfaces::Node& node, interfaces::Wallet& wallet, TransactionRecord *rec, int unit); + static QString toHTML(interfaces::Node& node, interfaces::Wallet& wallet, TransactionRecord* rec, BitcoinUnit unit); private: TransactionDesc() {} - static QString FormatTxStatus(const interfaces::WalletTx& wtx, const interfaces::WalletTxStatus& status, bool inMempool, int numBlocks); + static QString FormatTxStatus(const interfaces::WalletTxStatus& status, bool inMempool); }; #endif // BITCOIN_QT_TRANSACTIONDESC_H diff --git a/src/qt/transactionrecord.h b/src/qt/transactionrecord.h index dd34656d5f..d8748d7dc9 100644 --- a/src/qt/transactionrecord.h +++ b/src/qt/transactionrecord.h @@ -20,13 +20,7 @@ struct WalletTxStatus; /** UI model for transaction status. The transaction status is the part of a transaction that will change over time. */ -class TransactionStatus -{ -public: - TransactionStatus() : countsForBalance(false), sortKey(""), - matures_in(0), status(Unconfirmed), depth(0), open_for(0) - { } - +struct TransactionStatus { enum Status { Confirmed, /**< Have 6 or more confirmations (normal tx) or fully mature (mined tx) **/ /// Normal (sent/received) transactions @@ -40,28 +34,25 @@ public: }; /// Transaction counts towards available balance - bool countsForBalance; + bool countsForBalance{false}; /// Sorting key based on status std::string sortKey; /** @name Generated (mined) transactions @{*/ - int matures_in; + int matures_in{0}; /**@}*/ /** @name Reported status @{*/ - Status status; - qint64 depth; - qint64 open_for; /**< Timestamp if status==OpenUntilDate, otherwise number - of additional blocks that need to be mined before - finalization */ + Status status{Unconfirmed}; + qint64 depth{0}; /**@}*/ /** Current block hash (to know whether cached status is still valid) */ uint256 m_cur_block_hash{}; - bool needsUpdate; + bool needsUpdate{false}; }; /** UI model for a transaction. A core transaction can be represented by multiple UI transactions if it has diff --git a/src/qt/transactiontablemodel.cpp b/src/qt/transactiontablemodel.cpp index 6b0495f5a8..4312b3cd24 100644 --- a/src/qt/transactiontablemodel.cpp +++ b/src/qt/transactiontablemodel.cpp @@ -5,6 +5,7 @@ #include <qt/transactiontablemodel.h> #include <qt/addresstablemodel.h> +#include <qt/bitcoinunits.h> #include <qt/clientmodel.h> #include <qt/guiconstants.h> #include <qt/guiutil.h> @@ -61,7 +62,7 @@ struct TxLessThan struct TransactionNotification { public: - TransactionNotification() {} + TransactionNotification() = default; TransactionNotification(uint256 _hash, ChangeType _status, bool _showTransaction): hash(_hash), status(_status), showTransaction(_showTransaction) {} @@ -232,7 +233,7 @@ public: return nullptr; } - QString describe(interfaces::Node& node, interfaces::Wallet& wallet, TransactionRecord *rec, int unit) + QString describe(interfaces::Node& node, interfaces::Wallet& wallet, TransactionRecord* rec, BitcoinUnit unit) { return TransactionDesc::toHTML(node, wallet, rec, unit); } diff --git a/src/qt/walletcontroller.cpp b/src/qt/walletcontroller.cpp index b025bb367c..d27ddf1aba 100644 --- a/src/qt/walletcontroller.cpp +++ b/src/qt/walletcontroller.cpp @@ -24,6 +24,7 @@ #include <QApplication> #include <QMessageBox> +#include <QMetaObject> #include <QMutexLocker> #include <QThread> #include <QTimer> @@ -135,7 +136,7 @@ WalletModel* WalletController::getOrCreateWallet(std::unique_ptr<interfaces::Wal // handled on the GUI event loop. wallet_model->moveToThread(thread()); // setParent(parent) must be called in the thread which created the parent object. More details in #18948. - GUIUtil::ObjectInvoke(this, [wallet_model, this] { + QMetaObject::invokeMethod(this, [wallet_model, this] { wallet_model->setParent(this); }, GUIUtil::blockingGUIThreadConnection()); diff --git a/src/qt/walletframe.cpp b/src/qt/walletframe.cpp index 91ce420a33..11bea85b21 100644 --- a/src/qt/walletframe.cpp +++ b/src/qt/walletframe.cpp @@ -55,9 +55,7 @@ WalletFrame::WalletFrame(const PlatformStyle* _platformStyle, QWidget* parent) walletStack->addWidget(no_wallet_group); } -WalletFrame::~WalletFrame() -{ -} +WalletFrame::~WalletFrame() = default; void WalletFrame::setClientModel(ClientModel *_clientModel) { @@ -194,16 +192,16 @@ void WalletFrame::gotoVerifyMessageTab(QString addr) void WalletFrame::gotoLoadPSBT(bool from_clipboard) { - std::string data; + std::vector<unsigned char> data; if (from_clipboard) { std::string raw = QApplication::clipboard()->text().toStdString(); - bool invalid; - data = DecodeBase64(raw, &invalid); - if (invalid) { + auto result = DecodeBase64(raw); + if (!result) { Q_EMIT message(tr("Error"), tr("Unable to decode PSBT from clipboard (invalid base64)"), CClientUIInterface::MSG_ERROR); return; } + data = std::move(*result); } else { QString filename = GUIUtil::getOpenFileName(this, tr("Load Transaction Data"), QString(), @@ -214,12 +212,12 @@ void WalletFrame::gotoLoadPSBT(bool from_clipboard) return; } std::ifstream in{filename.toLocal8Bit().data(), std::ios::binary}; - data = std::string(std::istreambuf_iterator<char>{in}, {}); + data.assign(std::istream_iterator<unsigned char>{in}, {}); } std::string error; PartiallySignedTransaction psbtx; - if (!DecodeRawPSBT(psbtx, data, error)) { + if (!DecodeRawPSBT(psbtx, MakeByteSpan(data), error)) { Q_EMIT message(tr("Error"), tr("Unable to decode PSBT") + "\n" + QString::fromStdString(error), CClientUIInterface::MSG_ERROR); return; } diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index 5ee32e79d5..1f6c90af4a 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -145,7 +145,7 @@ void WalletModel::updateWatchOnlyFlag(bool fHaveWatchonly) Q_EMIT notifyWatchonlyChanged(fHaveWatchonly); } -bool WalletModel::validateAddress(const QString &address) +bool WalletModel::validateAddress(const QString& address) const { return IsValidDestinationString(address.toStdString()); } @@ -284,22 +284,22 @@ WalletModel::SendCoinsReturn WalletModel::sendCoins(WalletModelTransaction &tran return SendCoinsReturn(OK); } -OptionsModel *WalletModel::getOptionsModel() +OptionsModel* WalletModel::getOptionsModel() const { return optionsModel; } -AddressTableModel *WalletModel::getAddressTableModel() +AddressTableModel* WalletModel::getAddressTableModel() const { return addressTableModel; } -TransactionTableModel *WalletModel::getTransactionTableModel() +TransactionTableModel* WalletModel::getTransactionTableModel() const { return transactionTableModel; } -RecentRequestsTableModel *WalletModel::getRecentRequestsTableModel() +RecentRequestsTableModel* WalletModel::getRecentRequestsTableModel() const { return recentRequestsTableModel; } @@ -369,7 +369,7 @@ static void NotifyAddressBookChanged(WalletModel *walletmodel, QString strPurpose = QString::fromStdString(purpose); qDebug() << "NotifyAddressBookChanged: " + strAddress + " " + strLabel + " isMine=" + QString::number(isMine) + " purpose=" + strPurpose + " status=" + QString::number(status); - bool invoked = QMetaObject::invokeMethod(walletmodel, "updateAddressBook", Qt::QueuedConnection, + bool invoked = QMetaObject::invokeMethod(walletmodel, "updateAddressBook", Q_ARG(QString, strAddress), Q_ARG(QString, strLabel), Q_ARG(bool, isMine), @@ -557,7 +557,7 @@ bool WalletModel::bumpFee(uint256 hash, uint256& new_hash) return true; } -bool WalletModel::displayAddress(std::string sAddress) +bool WalletModel::displayAddress(std::string sAddress) const { CTxDestination dest = DecodeDestination(sAddress); bool res = false; @@ -585,7 +585,7 @@ QString WalletModel::getDisplayName() const return name.isEmpty() ? "["+tr("default wallet")+"]" : name; } -bool WalletModel::isMultiwallet() +bool WalletModel::isMultiwallet() const { return m_node.walletLoader().getWallets().size() > 1; } diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h index ad1239ccdc..540fdaafe3 100644 --- a/src/qt/walletmodel.h +++ b/src/qt/walletmodel.h @@ -77,15 +77,15 @@ public: Unlocked // wallet->IsCrypted() && !wallet->IsLocked() }; - OptionsModel *getOptionsModel(); - AddressTableModel *getAddressTableModel(); - TransactionTableModel *getTransactionTableModel(); - RecentRequestsTableModel *getRecentRequestsTableModel(); + OptionsModel* getOptionsModel() const; + AddressTableModel* getAddressTableModel() const; + TransactionTableModel* getTransactionTableModel() const; + RecentRequestsTableModel* getRecentRequestsTableModel() const; EncryptionStatus getEncryptionStatus() const; // Check address for validity - bool validateAddress(const QString &address); + bool validateAddress(const QString& address) const; // Return status record for SendCoins, contains error id + information struct SendCoinsReturn @@ -137,7 +137,7 @@ public: UnlockContext requestUnlock(); bool bumpFee(uint256 hash, uint256& new_hash); - bool displayAddress(std::string sAddress); + bool displayAddress(std::string sAddress) const; static bool isWalletEnabled(); @@ -149,9 +149,7 @@ public: QString getWalletName() const; QString getDisplayName() const; - bool isMultiwallet(); - - AddressTableModel* getAddressTableModel() const { return addressTableModel; } + bool isMultiwallet() const; void refresh(bool pk_hash_only = false); diff --git a/src/qt/walletview.cpp b/src/qt/walletview.cpp index e7ec54721a..2f92c57607 100644 --- a/src/qt/walletview.cpp +++ b/src/qt/walletview.cpp @@ -23,7 +23,6 @@ #include <util/strencodings.h> #include <QAction> -#include <QActionGroup> #include <QFileDialog> #include <QHBoxLayout> #include <QProgressDialog> @@ -112,9 +111,7 @@ WalletView::WalletView(WalletModel* wallet_model, const PlatformStyle* _platform connect(walletModel, &WalletModel::showProgress, this, &WalletView::showProgress); } -WalletView::~WalletView() -{ -} +WalletView::~WalletView() = default; void WalletView::setClientModel(ClientModel *_clientModel) { diff --git a/src/qt/walletview.h b/src/qt/walletview.h index 2f9d344bc8..301084ffa9 100644 --- a/src/qt/walletview.h +++ b/src/qt/walletview.h @@ -6,6 +6,7 @@ #define BITCOIN_QT_WALLETVIEW_H #include <consensus/amount.h> +#include <qt/bitcoinunits.h> #include <QStackedWidget> @@ -115,7 +116,7 @@ Q_SIGNALS: /** Encryption status of wallet changed */ void encryptionStatusChanged(); /** Notify that a new transaction appeared */ - void incomingTransaction(const QString& date, int unit, const CAmount& amount, const QString& type, const QString& address, const QString& label, const QString& walletName); + void incomingTransaction(const QString& date, BitcoinUnit unit, const CAmount& amount, const QString& type, const QString& address, const QString& label, const QString& walletName); /** Notify that the out of sync warning icon has been pressed */ void outOfSyncWarningClicked(); }; |