From 054ffdb28276088d7fb67396b6311e67bd0f2471 Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh <63189531+kwvg@users.noreply.github.com> Date: Fri, 23 May 2025 14:52:10 +0000 Subject: [PATCH 01/16] merge bitcoin-core/gui#557: revert bitcoin-core/gui#296 --- src/qt/intro.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/qt/intro.cpp b/src/qt/intro.cpp index 5ac162d6764d..0ad53d1f1e38 100644 --- a/src/qt/intro.cpp +++ b/src/qt/intro.cpp @@ -304,12 +304,12 @@ void Intro::setStatus(int status, const QString &message, quint64 bytesAvailable void Intro::UpdateFreeSpaceLabel() { - QString freeString = tr("%1 GB of space available").arg(m_bytes_available / GB_BYTES); + QString freeString = tr("%n GB of space available", "", m_bytes_available / GB_BYTES); if (m_bytes_available < m_required_space_gb * GB_BYTES) { - freeString += " " + tr("(of %1 GB needed)").arg(m_required_space_gb); + freeString += " " + tr("(of %n GB needed)", "", m_required_space_gb); ui->freeSpace->setStyleSheet(GUIUtil::getThemedStyleQString(GUIUtil::ThemedStyle::TS_ERROR)); } else if (m_bytes_available / GB_BYTES - m_required_space_gb < 10) { - freeString += " " + tr("(%1 GB needed for full chain)").arg(m_required_space_gb); + freeString += " " + tr("(%n GB needed for full chain)", "", m_required_space_gb); ui->freeSpace->setStyleSheet(GUIUtil::getThemedStyleQString(GUIUtil::ThemedStyle::TS_WARNING)); } else { ui->freeSpace->setStyleSheet(""); From 27951a36e33ae293b6e0a25263a4dbeed24ba3a6 Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh <63189531+kwvg@users.noreply.github.com> Date: Fri, 23 May 2025 11:30:25 +0000 Subject: [PATCH 02/16] merge bitcoin-core/gui#552: Refactor `TransactionDesc::FormatTxStatus` and `TransactionStatus` --- src/qt/transactiondesc.cpp | 48 ++++++++++++++++++-------------------- src/qt/transactiondesc.h | 2 +- src/qt/transactionrecord.h | 29 +++++++---------------- 3 files changed, 33 insertions(+), 46 deletions(-) diff --git a/src/qt/transactiondesc.cpp b/src/qt/transactiondesc.cpp index 47f8918b2056..6b3d7da523c8 100644 --- a/src/qt/transactiondesc.cpp +++ b/src/qt/transactiondesc.cpp @@ -27,34 +27,32 @@ #include -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"); - - QString strTxStatus; - bool fChainLocked = status.is_chainlocked; - - if (nDepth == 0) { - const QString abandoned{status.is_abandoned ? QLatin1String(", ") + tr("abandoned") : QString()}; - strTxStatus = tr("0/unconfirmed, %1").arg((inMempool ? tr("in memory pool") : tr("not in memory pool"))) + abandoned; - } else if (!fChainLocked && nDepth < 6) { - strTxStatus = tr("%1/unconfirmed").arg(nDepth); - } else { - strTxStatus = tr("%1 confirmations").arg(nDepth); - if (fChainLocked) { - strTxStatus += QLatin1String(", ") + tr("locked via ChainLocks"); - return strTxStatus; - } - } - - if (status.is_islocked) { - strTxStatus += QLatin1String(", ") + tr("verified via InstantSend"); + int depth = status.depth_in_main_chain; + if (depth < 0) return tr("conflicted"); + + QString strTxStatus; + bool fChainLocked = status.is_chainlocked; + + if (depth == 0) { + const QString abandoned{status.is_abandoned ? QLatin1String(", ") + tr("abandoned") : QString()}; + strTxStatus = tr("0/unconfirmed, %1").arg((inMempool ? tr("in memory pool") : tr("not in memory pool"))) + abandoned; + } else if (!fChainLocked && depth < 6) { + strTxStatus = tr("%1/unconfirmed").arg(depth); + } else { + strTxStatus = tr("%1 confirmations").arg(depth); + if (fChainLocked) { + strTxStatus += QLatin1String(", ") + tr("locked via ChainLocks"); + return strTxStatus; } + } - return strTxStatus; + if (status.is_islocked) { + strTxStatus += QLatin1String(", ") + tr("verified via InstantSend"); } + + return strTxStatus; } QString TransactionDesc::toHTML(interfaces::Node& node, interfaces::Wallet& wallet, TransactionRecord *rec, int unit) @@ -75,7 +73,7 @@ QString TransactionDesc::toHTML(interfaces::Node& node, interfaces::Wallet& wall CAmount nDebit = wtx.debit; CAmount nNet = nCredit - nDebit; - strHTML += "" + tr("Status") + ": " + FormatTxStatus(wtx, status, inMempool, numBlocks); + strHTML += "" + tr("Status") + ": " + FormatTxStatus(status, inMempool); strHTML += "
"; strHTML += "" + tr("Date") + ": " + (nTime ? GUIUtil::dateTimeStr(nTime) : "") + "
"; diff --git a/src/qt/transactiondesc.h b/src/qt/transactiondesc.h index 5492dc388ecd..5b4b9a2833be 100644 --- a/src/qt/transactiondesc.h +++ b/src/qt/transactiondesc.h @@ -29,7 +29,7 @@ class TransactionDesc: public QObject 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 d4aed8c31b73..ba819d90776d 100644 --- a/src/qt/transactionrecord.h +++ b/src/qt/transactionrecord.h @@ -21,15 +21,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), lockedByInstantSend(false), lockedByChainLocks(false), sortKey(""), - matures_in(0), status(Unconfirmed), depth(0), open_for(0), - cachedChainLockHeight(-1), needsUpdate(false) - { } - +struct TransactionStatus { enum Status { Confirmed, /**< Have 6 or more confirmations (normal tx) or fully mature (mined tx) **/ /// Normal (sent/received) transactions @@ -43,35 +35,32 @@ class TransactionStatus }; /// Transaction counts towards available balance - bool countsForBalance; + bool countsForBalance{false}; /// Transaction was locked via InstantSend - bool lockedByInstantSend; + bool lockedByInstantSend{false}; /// Transaction was locked via ChainLocks - bool lockedByChainLocks; + bool lockedByChainLocks{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{}; //** Know when to update transaction for chainlocks **/ - int cachedChainLockHeight; + int cachedChainLockHeight{-1}; - bool needsUpdate; + bool needsUpdate{false}; }; /** UI model for a transaction. A core transaction can be represented by multiple UI transactions if it has From 212e54d38d082eda25cd9a0ed6df019281f47f58 Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh <63189531+kwvg@users.noreply.github.com> Date: Thu, 21 Jan 2021 22:27:28 +0200 Subject: [PATCH 03/16] merge bitcoin-core/gui#556: Make BitcoinUnits::Unit a scoped enum --- src/qt/bitcoin.cpp | 2 + src/qt/bitcoinamountfield.cpp | 33 +++--- src/qt/bitcoinamountfield.h | 3 +- src/qt/bitcoingui.cpp | 15 ++- src/qt/bitcoingui.h | 7 +- src/qt/bitcoinunits.cpp | 191 ++++++++++++++----------------- src/qt/bitcoinunits.h | 38 +++--- src/qt/coincontroldialog.cpp | 4 +- src/qt/governancelist.cpp | 2 +- src/qt/governancelist.h | 4 +- src/qt/guiutil.cpp | 5 +- src/qt/optionsmodel.cpp | 31 ++--- src/qt/optionsmodel.h | 13 ++- src/qt/overviewpage.cpp | 28 ++--- src/qt/overviewpage.h | 3 +- src/qt/psbtoperationsdialog.cpp | 4 +- src/qt/sendcoinsdialog.cpp | 3 +- src/qt/test/wallettests.cpp | 5 +- src/qt/transactiondesc.cpp | 2 +- src/qt/transactiondesc.h | 4 +- src/qt/transactiontablemodel.cpp | 3 +- src/qt/transactionview.cpp | 2 +- src/qt/walletview.h | 3 +- 23 files changed, 205 insertions(+), 200 deletions(-) diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index ff3644ddfb34..b72784a7703a 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -97,6 +97,8 @@ static void RegisterMetaTypes() qRegisterMetaType>("std::function"); qRegisterMetaType("QMessageBox::Icon"); qRegisterMetaType("interfaces::BlockAndHeaderTipInfo"); + + qRegisterMetaTypeStreamOperators("BitcoinUnit"); } static QString GetLangTerritory() diff --git a/src/qt/bitcoinamountfield.cpp b/src/qt/bitcoinamountfield.cpp index 6c080ac77e02..028b21b15b75 100644 --- a/src/qt/bitcoinamountfield.cpp +++ b/src/qt/bitcoinamountfield.cpp @@ -12,13 +12,16 @@ #include #include #include +#include + +#include /** * Parse a string into a number of base monetary units and * return validity. * @note Must return 0 if !valid. */ -static CAmount parse(const QString &text, int nUnit, bool *valid_out= nullptr) +static CAmount parse(const QString &text, BitcoinUnit nUnit, bool *valid_out= nullptr) { CAmount val = 0; bool valid = BitcoinUnits::parse(nUnit, text, &val); @@ -37,11 +40,12 @@ static CAmount parse(const QString &text, int nUnit, bool *valid_out= nullptr) class AmountValidator : public QValidator { Q_OBJECT - int currentUnit; + BitcoinUnit currentUnit; + public: explicit AmountValidator(QObject *parent) : QValidator(parent), - currentUnit(BitcoinUnits::DASH) {} + currentUnit(BitcoinUnit::DASH) {} State validate(QString &input, int &pos) const override { @@ -53,7 +57,7 @@ class AmountValidator : public QValidator return valid ? QValidator::Intermediate : QValidator::Invalid; } - void updateUnit(int nUnit) + void updateUnit(BitcoinUnit nUnit) { currentUnit = nUnit; } @@ -69,7 +73,7 @@ class AmountLineEdit: public QLineEdit public: explicit AmountLineEdit(QWidget *parent): QLineEdit(parent), - currentUnit(BitcoinUnits::DASH) + currentUnit(BitcoinUnit::DASH) { setAlignment(Qt::AlignLeft); amountValidator = new AmountValidator(this); @@ -122,7 +126,7 @@ class AmountLineEdit: public QLineEdit m_max_amount = value; } - void setDisplayUnit(int unit) + void setDisplayUnit(BitcoinUnit unit) { bool valid = false; CAmount val = value(&valid); @@ -142,14 +146,14 @@ class AmountLineEdit: public QLineEdit ensurePolished(); const QFontMetrics fm(fontMetrics()); int h = 0; - int w = GUIUtil::TextWidth(fm, BitcoinUnits::format(BitcoinUnits::DASH, BitcoinUnits::maxMoney(), false, BitcoinUnits::SeparatorStyle::ALWAYS)); + int w = GUIUtil::TextWidth(fm, BitcoinUnits::format(BitcoinUnit::DASH, BitcoinUnits::maxMoney(), false, BitcoinUnits::SeparatorStyle::ALWAYS)); w += 2; // cursor blinking space w += GUIUtil::dashThemeActive() ? 24 : 0; // counteract padding from css return QSize(w, h); } private: - int currentUnit{BitcoinUnits::DASH}; + BitcoinUnit currentUnit{BitcoinUnit::DASH}; bool m_allow_empty{true}; CAmount m_min_amount{CAmount(0)}; CAmount m_max_amount{BitcoinUnits::maxMoney()}; @@ -287,14 +291,13 @@ void BitcoinAmountField::unitChanged(int idx) amount->setToolTip(units->data(idx, Qt::ToolTipRole).toString()); // Determine new unit ID - int newUnit = units->data(idx, BitcoinUnits::UnitRole).toInt(); - - amount->setPlaceholderText(tr("Amount in %1").arg(units->data(idx,Qt::DisplayRole).toString())); - - amount->setDisplayUnit(newUnit); + QVariant new_unit = units->data(idx, BitcoinUnits::UnitRole); + assert(new_unit.isValid()); + amount->setPlaceholderText(tr("Amount in %1").arg(units->data(idx, Qt::DisplayRole).toString())); + amount->setDisplayUnit(new_unit.value()); } -void BitcoinAmountField::setDisplayUnit(int newUnit) +void BitcoinAmountField::setDisplayUnit(BitcoinUnit new_unit) { - unitChanged(newUnit); + unitChanged(QVariant::fromValue(new_unit).toInt()); } diff --git a/src/qt/bitcoinamountfield.h b/src/qt/bitcoinamountfield.h index 3466cee97fcc..42254f280732 100644 --- a/src/qt/bitcoinamountfield.h +++ b/src/qt/bitcoinamountfield.h @@ -6,6 +6,7 @@ #define BITCOIN_QT_BITCOINAMOUNTFIELD_H #include +#include #include #include @@ -51,7 +52,7 @@ class BitcoinAmountField: public QWidget 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 51575a9cadfc..9f7fc8c31270 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -1801,7 +1801,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) { IncomingTransactionMessage itx = { date, unit, amount, type, address, label, walletName @@ -1855,7 +1855,7 @@ void BitcoinGUI::showIncomingTransactions() } // Use display unit of last entry - int unit = txs.back().unit; + BitcoinUnit unit = txs.back().unit; QString msg; if (sentCount > 0) { @@ -2152,11 +2152,10 @@ UnitDisplayStatusBarControl::UnitDisplayStatusBarControl() : { createContextMenu(); setToolTip(tr("Unit to show amounts in. Click to select another unit.")); - QList units = BitcoinUnits::availableUnits(); + QList units = BitcoinUnits::availableUnits(); int max_width = 0; const QFontMetrics fm(GUIUtil::getFontNormal()); - for (const BitcoinUnits::Unit unit : units) - { + for (const BitcoinUnit unit : units) { max_width = qMax(max_width, GUIUtil::TextWidth(fm, BitcoinUnits::name(unit))); } setMinimumSize(max_width, 0); @@ -2173,8 +2172,8 @@ void UnitDisplayStatusBarControl::mousePressEvent(QMouseEvent *event) void UnitDisplayStatusBarControl::createContextMenu() { menu = new QMenu(this); - for (const BitcoinUnits::Unit u : BitcoinUnits::availableUnits()) { - menu->addAction(BitcoinUnits::name(u))->setData(QVariant(u)); + for (const BitcoinUnit u : BitcoinUnits::availableUnits()) { + menu->addAction(BitcoinUnits::name(u))->setData(QVariant::fromValue(u)); } connect(menu, &QMenu::triggered, this, &UnitDisplayStatusBarControl::onMenuSelection); } @@ -2195,7 +2194,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::name(newUnits)); } diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h index 0a165d37a75b..6b04432669b8 100644 --- a/src/qt/bitcoingui.h +++ b/src/qt/bitcoingui.h @@ -9,6 +9,7 @@ #include #endif +#include #include #include @@ -212,7 +213,7 @@ class BitcoinGUI : public QMainWindow struct IncomingTransactionMessage { QString date; - int unit; + BitcoinUnit unit; CAmount amount; QString type; QString address; @@ -309,7 +310,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); void showIncomingTransactions(); #endif // ENABLE_WALLET @@ -415,7 +416,7 @@ class UnitDisplayStatusBarControl : public QLabel 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 60c955a5e223..2d5d6d265f62 100644 --- a/src/qt/bitcoinunits.cpp +++ b/src/qt/bitcoinunits.cpp @@ -21,112 +21,66 @@ BitcoinUnits::BitcoinUnits(QObject *parent): { } -QList BitcoinUnits::availableUnits() +QList BitcoinUnits::availableUnits() { - QList unitlist; - unitlist.append(DASH); - unitlist.append(mDASH); - unitlist.append(uDASH); - unitlist.append(duffs); + QList unitlist; + unitlist.append(Unit::DASH); + unitlist.append(Unit::mDASH); + unitlist.append(Unit::uDASH); + unitlist.append(Unit::duffs); return unitlist; } -bool BitcoinUnits::valid(int unit) +QString BitcoinUnits::name(Unit unit) { - switch(unit) - { - case DASH: - case mDASH: - case uDASH: - case duffs: - return true; - default: - return false; - } + const bool is_mainnet{Params().NetworkIDString() == CBaseChainParams::MAIN}; + switch (unit) { + case Unit::DASH: return is_mainnet ? QString("DASH") : QString("tDASH"); + case Unit::mDASH: return is_mainnet ? QString("mDASH") : QString("mtDASH"); + case Unit::uDASH: return is_mainnet ? QString::fromUtf8("μDASH") : QString::fromUtf8("μtDASH"); + case Unit::duffs: return is_mainnet ? QString("duffs") : QString("tduffs"); + } // no default case, so the compiler can warn about missing cases + assert(false); } -QString BitcoinUnits::name(int unit) +QString BitcoinUnits::description(Unit unit) { - if(Params().NetworkIDString() == CBaseChainParams::MAIN) - { - switch(unit) - { - case DASH: return QString("DASH"); - case mDASH: return QString("mDASH"); - case uDASH: return QString::fromUtf8("μDASH"); - case duffs: return QString("duffs"); - default: return QString("???"); - } - } - else - { - switch(unit) - { - case DASH: return QString("tDASH"); - case mDASH: return QString("mtDASH"); - case uDASH: return QString::fromUtf8("μtDASH"); - case duffs: return QString("tduffs"); - default: return QString("???"); - } - } + const QString maybe_prefix{Params().NetworkIDString() == CBaseChainParams::MAIN ? "" : "Test"}; + switch(unit) { + case Unit::DASH: return QString("%1Dash"); + case Unit::mDASH: return QString("Milli-%1Dash (1 / 1" THIN_SP_UTF8 "000)").arg(maybe_prefix); + case Unit::uDASH: return QString("Micro-%1Dash (1 / 1" THIN_SP_UTF8 "000" THIN_SP_UTF8 "000)").arg(maybe_prefix); + case Unit::duffs: return QString("Ten Nano-%1Dash (1 / 100" THIN_SP_UTF8 "000" THIN_SP_UTF8 "000)").arg(maybe_prefix); + } // no default case, so the compiler can warn about missing cases + assert(false); } -QString BitcoinUnits::description(int unit) +qint64 BitcoinUnits::factor(Unit unit) { - if(Params().NetworkIDString() == CBaseChainParams::MAIN) - { - switch(unit) - { - case DASH: return QString("Dash"); - case mDASH: return QString("Milli-Dash (1 / 1" THIN_SP_UTF8 "000)"); - case uDASH: return QString("Micro-Dash (1 / 1" THIN_SP_UTF8 "000" THIN_SP_UTF8 "000)"); - case duffs: return QString("Ten Nano-Dash (1 / 100" THIN_SP_UTF8 "000" THIN_SP_UTF8 "000)"); - default: return QString("???"); - } - } - else - { - switch(unit) - { - case DASH: return QString("TestDashs"); - case mDASH: return QString("Milli-TestDash (1 / 1" THIN_SP_UTF8 "000)"); - case uDASH: return QString("Micro-TestDash (1 / 1" THIN_SP_UTF8 "000" THIN_SP_UTF8 "000)"); - case duffs: return QString("Ten Nano-TestDash (1 / 100" THIN_SP_UTF8 "000" THIN_SP_UTF8 "000)"); - default: return QString("???"); - } - } + switch (unit) { + case Unit::DASH: return 100'000'000; + case Unit::mDASH: return 100'000; + case Unit::uDASH: return 100; + case Unit::duffs: return 1; + } // no default case, so the compiler can warn about missing cases + assert(false); } -qint64 BitcoinUnits::factor(int unit) +int BitcoinUnits::decimals(Unit unit) { - switch(unit) - { - case DASH: return 100000000; - case mDASH: return 100000; - case uDASH: return 100; - case duffs: return 1; - default: return 100000000; - } + switch (unit) { + case Unit::DASH: return 8; + case Unit::mDASH: return 5; + case Unit::uDASH: return 2; + case Unit::duffs: return 0; + } // no default case, so the compiler can warn about missing cases + assert(false); } -int BitcoinUnits::decimals(int unit) -{ - switch(unit) - { - case DASH: return 8; - case mDASH: return 5; - case uDASH: return 2; - case duffs: return 0; - default: return 0; - } -} - -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); @@ -168,19 +122,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(" ") + name(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("%1").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; @@ -192,7 +146,7 @@ QString BitcoinUnits::formatWithPrivacy(int unit, const CAmount& amount, Separat return value + QString(" ") + name(unit); } -QString BitcoinUnits::floorWithUnit(int unit, const CAmount& amount, bool plussign, SeparatorStyle separators) +QString BitcoinUnits::floorWithUnit(Unit unit, const CAmount& amount, bool plussign, SeparatorStyle separators) { QSettings settings; int digits = settings.value("digits").toInt(); @@ -203,7 +157,7 @@ QString BitcoinUnits::floorWithUnit(int unit, const CAmount& amount, bool plussi return result + QString(" ") + name(unit); } -QString BitcoinUnits::floorHtmlWithPrivacy(int unit, const CAmount& amount, SeparatorStyle separators, bool privacy) +QString BitcoinUnits::floorHtmlWithPrivacy(Unit unit, const CAmount& amount, SeparatorStyle separators, bool privacy) { assert(amount >= 0); QString str = privacy @@ -214,10 +168,11 @@ QString BitcoinUnits::floorHtmlWithPrivacy(int unit, const CAmount& amount, Sepa return QString("%1").arg(str); } -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 @@ -253,14 +208,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::name(unit) + ")"; - } - return amountTitle; + return QObject::tr("Amount") + " (" + name(unit) + ")"; } int BitcoinUnits::rowCount(const QModelIndex &parent) const @@ -287,7 +237,7 @@ QVariant BitcoinUnits::data(const int &row, int role) const case Qt::ToolTipRole: return QVariant(description(unit)); case UnitRole: - return QVariant(static_cast(unit)); + return QVariant::fromValue(unit); } } return QVariant(); @@ -297,3 +247,40 @@ CAmount BitcoinUnits::maxMoney() { return MAX_MONEY; } + +namespace { +qint8 ToQint8(BitcoinUnit unit) +{ + switch (unit) { + case BitcoinUnit::DASH: return 0; + case BitcoinUnit::mDASH: return 1; + case BitcoinUnit::uDASH: return 2; + case BitcoinUnit::duffs: 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::DASH; + case 1: return BitcoinUnit::mDASH; + case 2: return BitcoinUnit::uDASH; + case 3: return BitcoinUnit::duffs; + } + 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 1bc4c9977890..7e2d202004b7 100644 --- a/src/qt/bitcoinunits.h +++ b/src/qt/bitcoinunits.h @@ -9,6 +9,7 @@ #include #include +#include #include // U+2009 THIN SPACE = UTF-8 E2 80 89 @@ -39,13 +40,13 @@ class BitcoinUnits: public QAbstractListModel /** Dash units. @note Source: https://en.bitcoin.it/wiki/Units . Please add only sensible ones */ - enum Unit - { + enum class Unit { DASH, mDASH, uDASH, duffs }; + Q_ENUM(Unit) enum class SeparatorStyle { @@ -60,32 +61,30 @@ class BitcoinUnits: public QAbstractListModel //! Get list of units, for drop-down box static QList availableUnits(); - //! Is unit ID valid? - static bool valid(int unit); //! Short name - static QString name(int unit); + static QString name(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 simpleFormat(int unit, const CAmount& amount, bool plussign=false, SeparatorStyle separators=SeparatorStyle::STANDARD); + static QString format(Unit unit, const CAmount& amount, bool plussign = false, SeparatorStyle separators = SeparatorStyle::STANDARD, bool justify = false); + static QString simpleFormat(Unit unit, const CAmount& amount, bool plussign=false, SeparatorStyle separators=SeparatorStyle::STANDARD); //! 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); //! Format as string (with unit) but floor value up to "digits" settings - static QString floorWithUnit(int unit, const CAmount& amount, bool plussign=false, SeparatorStyle separators=SeparatorStyle::STANDARD); - static QString floorHtmlWithPrivacy(int unit, const CAmount& amount, SeparatorStyle separators, bool privacy); + static QString floorWithUnit(Unit unit, const CAmount& amount, bool plussign=false, SeparatorStyle separators=SeparatorStyle::STANDARD); + static QString floorHtmlWithPrivacy(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 @@ -111,8 +110,11 @@ class BitcoinUnits: public QAbstractListModel static CAmount maxMoney(); private: - QList unitlist; + QList 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/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp index 385340719fc7..13c4c6bd67b0 100644 --- a/src/qt/coincontroldialog.cpp +++ b/src/qt/coincontroldialog.cpp @@ -562,7 +562,7 @@ void CoinControlDialog::updateLabels(CCoinControl& m_coin_control, WalletModel * } // actually update labels - int nDisplayUnit = BitcoinUnits::DASH; + BitcoinUnit nDisplayUnit = BitcoinUnit::DASH; if (model->getOptionsModel()) nDisplayUnit = model->getOptionsModel()->getDisplayUnit(); @@ -670,7 +670,7 @@ void CoinControlDialog::updateView() QFlags flgCheckbox = Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsUserCheckable; QFlags flgTristate = Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsUserCheckable | Qt::ItemIsTristate; - 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/governancelist.cpp b/src/qt/governancelist.cpp index 8631799347e4..4f0b6e274825 100644 --- a/src/qt/governancelist.cpp +++ b/src/qt/governancelist.cpp @@ -286,7 +286,7 @@ const Proposal* ProposalModel::getProposalAt(const QModelIndex& index) const return m_data[index.row()]; } -void ProposalModel::setDisplayUnit(int display_unit) { this->m_display_unit = display_unit; } +void ProposalModel::setDisplayUnit(BitcoinUnit display_unit) { this->m_display_unit = display_unit; } // // Governance Tab main widget. diff --git a/src/qt/governancelist.h b/src/qt/governancelist.h index 4e76322ddcae..c0a02d24a2b0 100644 --- a/src/qt/governancelist.h +++ b/src/qt/governancelist.h @@ -94,7 +94,7 @@ class ProposalModel : public QAbstractTableModel private: QList m_data; int nAbsVoteReq = 0; - int m_display_unit{BitcoinUnits::DASH}; + BitcoinUnit m_display_unit{BitcoinUnit::DASH}; public: explicit ProposalModel(QObject* parent = nullptr) : @@ -123,7 +123,7 @@ class ProposalModel : public QAbstractTableModel const Proposal* getProposalAt(const QModelIndex& index) const; - void setDisplayUnit(int display_unit); + void setDisplayUnit(BitcoinUnit display_unit); }; #endif // BITCOIN_QT_GOVERNANCELIST_H diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp index cb2ea62ab05f..a462726a5f33 100644 --- a/src/qt/guiutil.cpp +++ b/src/qt/guiutil.cpp @@ -395,8 +395,7 @@ bool parseBitcoinURI(const QUrl &uri, SendCoinsRecipient *out) { if(!i->second.isEmpty()) { - if(!BitcoinUnits::parse(BitcoinUnits::DASH, i->second, &rv.amount)) - { + if (!BitcoinUnits::parse(BitcoinUnit::DASH, i->second, &rv.amount)) { return false; } } @@ -432,7 +431,7 @@ QString formatBitcoinURI(const SendCoinsRecipient &info) if (info.amount) { - ret += QString("?amount=%1").arg(BitcoinUnits::format(BitcoinUnits::DASH, info.amount, false, BitcoinUnits::SeparatorStyle::NEVER)); + ret += QString("?amount=%1").arg(BitcoinUnits::format(BitcoinUnit::DASH, info.amount, false, BitcoinUnits::SeparatorStyle::NEVER)); paramCount++; } diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp index e59caaa8498f..a4d61ad7a6ff 100644 --- a/src/qt/optionsmodel.cpp +++ b/src/qt/optionsmodel.cpp @@ -29,6 +29,7 @@ #include #include #include +#include const char *DEFAULT_GUI_PROXY_HOST = "127.0.0.1"; @@ -76,9 +77,16 @@ void OptionsModel::Init(bool resetSettings) fMinimizeOnClose = settings.value("fMinimizeOnClose").toBool(); // Display - if (!settings.contains("nDisplayUnit")) - settings.setValue("nDisplayUnit", BitcoinUnits::DASH); - nDisplayUnit = settings.value("nDisplayUnit").toInt(); + if (!settings.contains("DisplayDashUnit")) { + settings.setValue("DisplayDashUnit", QVariant::fromValue(BitcoinUnit::DASH)); + } + QVariant unit = settings.value("DisplayDashUnit"); + if (unit.canConvert()) { + m_display_bitcoin_unit = unit.value(); + } else { + m_display_bitcoin_unit = BitcoinUnit::DASH; + settings.setValue("DisplayDashUnit", QVariant::fromValue(m_display_bitcoin_unit)); + } if (!settings.contains("strThirdPartyTxUrls")) settings.setValue("strThirdPartyTxUrls", ""); @@ -511,7 +519,7 @@ QVariant OptionsModel::data(const QModelIndex & index, int role) const return settings.value("fCoinJoinMultiSession"); #endif case DisplayUnit: - return nDisplayUnit; + return QVariant::fromValue(m_display_bitcoin_unit); case ThirdPartyTxUrls: return strThirdPartyTxUrls; #ifdef ENABLE_WALLET @@ -844,16 +852,13 @@ bool OptionsModel::setData(const QModelIndex & index, const QVariant & value, in 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() == m_display_bitcoin_unit) return; + m_display_bitcoin_unit = new_unit.value(); + QSettings settings; + settings.setValue("DisplayDashUnit", QVariant::fromValue(m_display_bitcoin_unit)); + Q_EMIT displayUnitChanged(m_display_bitcoin_unit); } void OptionsModel::emitCoinJoinEnabledChanged() diff --git a/src/qt/optionsmodel.h b/src/qt/optionsmodel.h index f892056d4dfc..95f6bc987023 100644 --- a/src/qt/optionsmodel.h +++ b/src/qt/optionsmodel.h @@ -5,6 +5,7 @@ #ifndef BITCOIN_QT_OPTIONSMODEL_H #define BITCOIN_QT_OPTIONSMODEL_H +#include #include #include @@ -56,7 +57,7 @@ class OptionsModel : public QAbstractListModel ProxyUseTor, // bool ProxyIPTor, // QString ProxyPortTor, // int - DisplayUnit, // BitcoinUnits::Unit + DisplayUnit, // BitcoinUnit ThirdPartyTxUrls, // QString Digits, // QString Theme, // QString @@ -96,14 +97,14 @@ class OptionsModel : public QAbstractListModel 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); + /** 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 getCoinControlFeatures() const { return fCoinControlFeatures; } bool getSubFeeFromAmount() const { return m_sub_fee_from_amount; } @@ -131,7 +132,7 @@ class OptionsModel : public QAbstractListModel bool fMinimizeToTray; bool fMinimizeOnClose; QString language; - int nDisplayUnit; + BitcoinUnit m_display_bitcoin_unit; QString strThirdPartyTxUrls; bool fCoinControlFeatures; bool m_sub_fee_from_amount; @@ -146,7 +147,7 @@ class OptionsModel : public QAbstractListModel // Check settings version and upgrade default values if required void checkAndMigrate(); Q_SIGNALS: - void displayUnitChanged(int unit); + void displayUnitChanged(BitcoinUnit unit); void coinJoinEnabledChanged(); void coinJoinRoundsChanged(); void coinJoinAmountChanged(); diff --git a/src/qt/overviewpage.cpp b/src/qt/overviewpage.cpp index 60cbdf74afc6..dc08a67c3e2e 100644 --- a/src/qt/overviewpage.cpp +++ b/src/qt/overviewpage.cpp @@ -42,8 +42,8 @@ class TxViewDelegate : public QAbstractItemDelegate { Q_OBJECT public: - explicit TxViewDelegate(QObject* parent = nullptr) : - QAbstractItemDelegate(), unit(BitcoinUnits::DASH) + explicit TxViewDelegate(QObject* parent = nullptr) + : QAbstractItemDelegate(parent), unit(BitcoinUnit::DASH) { connect(this, &TxViewDelegate::width_changed, this, &TxViewDelegate::sizeHintChanged); } @@ -126,7 +126,7 @@ class TxViewDelegate : public QAbstractItemDelegate return {ITEM_HEIGHT + 8 + minimum_text_width, ITEM_HEIGHT}; } - int unit; + BitcoinUnit unit; Q_SIGNALS: //! An intermediate signal for emitting from the `paint() const` member function. @@ -226,7 +226,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()) { @@ -347,13 +347,13 @@ void OverviewPage::updateDisplayUnit() { if(walletModel && walletModel->getOptionsModel()) { - nDisplayUnit = walletModel->getOptionsModel()->getDisplayUnit(); + m_display_bitcoin_unit = walletModel->getOptionsModel()->getDisplayUnit(); if (m_balances.balance != -1) { setBalance(m_balances); } // Update txdelegate->unit with the current unit - txdelegate->unit = nDisplayUnit; + txdelegate->unit = m_display_bitcoin_unit; ui->listTransactions->update(); } @@ -377,7 +377,7 @@ void OverviewPage::updateCoinJoinProgress() if (!walletModel || !clientModel || clientModel->node().shutdownRequested() || !clientModel->masternodeSync().isBlockchainSynced()) return; QString strAmountAndRounds; - QString strCoinJoinAmount = BitcoinUnits::formatHtmlWithUnit(nDisplayUnit, clientModel->coinJoinOptions().getAmount() * COIN, false, BitcoinUnits::SeparatorStyle::ALWAYS); + QString strCoinJoinAmount = BitcoinUnits::formatHtmlWithUnit(m_display_bitcoin_unit, clientModel->coinJoinOptions().getAmount() * COIN, false, BitcoinUnits::SeparatorStyle::ALWAYS); if(m_balances.balance == 0) { @@ -385,7 +385,7 @@ void OverviewPage::updateCoinJoinProgress() ui->coinJoinProgress->setToolTip(tr("No inputs detected")); // when balance is zero just show info from settings - strCoinJoinAmount = strCoinJoinAmount.remove(strCoinJoinAmount.indexOf("."), BitcoinUnits::decimals(nDisplayUnit) + 1); + strCoinJoinAmount = strCoinJoinAmount.remove(strCoinJoinAmount.indexOf("."), BitcoinUnits::decimals(m_display_bitcoin_unit) + 1); strAmountAndRounds = strCoinJoinAmount + " / " + tr("%n Rounds", "", clientModel->coinJoinOptions().getRounds()); ui->labelAmountRounds->setToolTip(tr("No inputs detected")); @@ -403,23 +403,23 @@ void OverviewPage::updateCoinJoinProgress() if(nMaxToAnonymize == 0) return; if (m_privacy) { - strAmountAndRounds = "#### " + BitcoinUnits::name(nDisplayUnit) + " / " + tr("%n Rounds", "", 0); + strAmountAndRounds = "#### " + BitcoinUnits::name(m_display_bitcoin_unit) + " / " + tr("%n Rounds", "", 0); ui->labelAmountRounds->setToolTip(""); } else if (nMaxToAnonymize >= clientModel->coinJoinOptions().getAmount() * COIN) { ui->labelAmountRounds->setToolTip(tr("Found enough compatible inputs to mix %1") .arg(strCoinJoinAmount)); - strCoinJoinAmount = strCoinJoinAmount.remove(strCoinJoinAmount.indexOf("."), BitcoinUnits::decimals(nDisplayUnit) + 1); + strCoinJoinAmount = strCoinJoinAmount.remove(strCoinJoinAmount.indexOf("."), BitcoinUnits::decimals(m_display_bitcoin_unit) + 1); strAmountAndRounds = strCoinJoinAmount + " / " + tr("%n Rounds", "", clientModel->coinJoinOptions().getRounds()); } else { - QString strMaxToAnonymize = BitcoinUnits::formatHtmlWithUnit(nDisplayUnit, nMaxToAnonymize, false, BitcoinUnits::SeparatorStyle::ALWAYS); + QString strMaxToAnonymize = BitcoinUnits::formatHtmlWithUnit(m_display_bitcoin_unit, nMaxToAnonymize, false, BitcoinUnits::SeparatorStyle::ALWAYS); ui->labelAmountRounds->setToolTip(tr("Not enough compatible inputs to mix %2,
" "will mix %3 instead") .arg(GUIUtil::getThemedStyleQString(GUIUtil::ThemedStyle::TS_ERROR)) .arg(strCoinJoinAmount) .arg(strMaxToAnonymize)); - strMaxToAnonymize = strMaxToAnonymize.remove(strMaxToAnonymize.indexOf("."), BitcoinUnits::decimals(nDisplayUnit) + 1); + strMaxToAnonymize = strMaxToAnonymize.remove(strMaxToAnonymize.indexOf("."), BitcoinUnits::decimals(m_display_bitcoin_unit) + 1); strAmountAndRounds = "" + - QString(BitcoinUnits::factor(nDisplayUnit) == 1 ? "" : "~") + strMaxToAnonymize + + QString(BitcoinUnits::factor(m_display_bitcoin_unit) == 1 ? "" : "~") + strMaxToAnonymize + " / " + tr("%n Rounds", "", clientModel->coinJoinOptions().getRounds()) + ""; } ui->labelAmountRounds->setText(strAmountAndRounds); @@ -681,7 +681,7 @@ void OverviewPage::toggleCoinJoin(){ auto& options = walletModel->node().coinJoinOptions(); const CAmount nMinAmount = options.getSmallestDenomination() + options.getMaxCollateralAmount(); if(m_balances.balance < nMinAmount) { - QString strMinAmount(BitcoinUnits::formatWithUnit(nDisplayUnit, nMinAmount)); + QString strMinAmount(BitcoinUnits::formatWithUnit(m_display_bitcoin_unit, nMinAmount)); QMessageBox::warning(this, strCoinJoinName, tr("%1 requires at least %2 to use.").arg(strCoinJoinName).arg(strMinAmount), QMessageBox::Ok, QMessageBox::Ok); diff --git a/src/qt/overviewpage.h b/src/qt/overviewpage.h index 4b033861870f..5f1789f27d7a 100644 --- a/src/qt/overviewpage.h +++ b/src/qt/overviewpage.h @@ -6,6 +6,7 @@ #define BITCOIN_QT_OVERVIEWPAGE_H #include +#include #include #include @@ -52,7 +53,7 @@ public Q_SLOTS: WalletModel *walletModel; interfaces::WalletBalances m_balances; bool m_privacy{false}; - int nDisplayUnit; + BitcoinUnit m_display_bitcoin_unit; bool fShowAdvancedCJUI; int cachedNumISLocks; diff --git a/src/qt/psbtoperationsdialog.cpp b/src/qt/psbtoperationsdialog.cpp index 99c8c7303065..d8b23ee57f88 100644 --- a/src/qt/psbtoperationsdialog.cpp +++ b/src/qt/psbtoperationsdialog.cpp @@ -178,7 +178,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::DASH, out.nValue)) + .arg(BitcoinUnits::formatWithUnit(BitcoinUnit::DASH, out.nValue)) .arg(QString::fromStdString(EncodeDestination(address)))); // Check if the address is one of ours if (m_wallet_model != nullptr && m_wallet_model->wallet().txoutIsMine(out)) tx_description.append(" (" + tr("own address") + ")"); @@ -192,7 +192,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::DASH, *analysis.fee)); + tx_description.append(BitcoinUnits::formatWithUnit(BitcoinUnit::DASH, *analysis.fee)); // add total amount in all subdivision units tx_description.append("
"); diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index 581b4221687f..b895c7d33d15 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -445,8 +445,7 @@ bool SendCoinsDialog::send(const QList& recipients, QString& question_string.append("
"); 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/test/wallettests.cpp b/src/qt/test/wallettests.cpp index ae1f5fa71d45..6f73429d9434 100644 --- a/src/qt/test/wallettests.cpp +++ b/src/qt/test/wallettests.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -152,7 +153,7 @@ void TestGUI(interfaces::Node& node) // Check balance in send dialog QLabel* balanceLabel = sendCoinsDialog.findChild("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); @@ -175,7 +176,7 @@ void TestGUI(interfaces::Node& node) overviewPage.setWalletModel(&walletModel); QLabel* balanceLabel = overviewPage.findChild("labelBalance"); QString balanceText = balanceLabel->text().trimmed(); - int unit = walletModel.getOptionsModel()->getDisplayUnit(); + BitcoinUnit unit = walletModel.getOptionsModel()->getDisplayUnit(); CAmount balance = walletModel.wallet().getBalance(); QString balanceComparison = BitcoinUnits::floorHtmlWithPrivacy(unit, balance, BitcoinUnits::SeparatorStyle::ALWAYS, false); QCOMPARE(balanceText, balanceComparison); diff --git a/src/qt/transactiondesc.cpp b/src/qt/transactiondesc.cpp index 6b3d7da523c8..1dc172af1a97 100644 --- a/src/qt/transactiondesc.cpp +++ b/src/qt/transactiondesc.cpp @@ -55,7 +55,7 @@ QString TransactionDesc::FormatTxStatus(const interfaces::WalletTxStatus& status return strTxStatus; } -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; diff --git a/src/qt/transactiondesc.h b/src/qt/transactiondesc.h index 5b4b9a2833be..cff708ef69a7 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 + #include #include @@ -24,7 +26,7 @@ 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() {} diff --git a/src/qt/transactiontablemodel.cpp b/src/qt/transactiontablemodel.cpp index 366377222a6c..f08d87208192 100644 --- a/src/qt/transactiontablemodel.cpp +++ b/src/qt/transactiontablemodel.cpp @@ -4,6 +4,7 @@ #include +#include #include #include #include @@ -248,7 +249,7 @@ class TransactionTablePriv 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/transactionview.cpp b/src/qt/transactionview.cpp index ea4c2b31df7c..0692f1a32caf 100644 --- a/src/qt/transactionview.cpp +++ b/src/qt/transactionview.cpp @@ -562,7 +562,7 @@ void TransactionView::showAddressQRCode() void TransactionView::computeSum() { qint64 amount = 0; - int nDisplayUnit = model->getOptionsModel()->getDisplayUnit(); + BitcoinUnit nDisplayUnit = model->getOptionsModel()->getDisplayUnit(); if(!transactionView->selectionModel()) return; QModelIndexList selection = transactionView->selectionModel()->selectedRows(); diff --git a/src/qt/walletview.h b/src/qt/walletview.h index 0f70a704ecf0..4ff82cbf4b8c 100644 --- a/src/qt/walletview.h +++ b/src/qt/walletview.h @@ -6,6 +6,7 @@ #define BITCOIN_QT_WALLETVIEW_H #include +#include #include #include @@ -130,7 +131,7 @@ public Q_SLOTS: /** 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(); }; From a334b686e90fac68a3ef408bd10abb80e2a0b0b9 Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh <63189531+kwvg@users.noreply.github.com> Date: Sat, 23 Apr 2022 14:13:05 +0200 Subject: [PATCH 04/16] merge bitcoin-core/gui#590: Declare `WalletModel` member functions with `const` --- src/qt/walletmodel.cpp | 12 ++++++------ src/qt/walletmodel.h | 16 +++++++--------- 2 files changed, 13 insertions(+), 15 deletions(-) diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index b50306611fb5..f299efba3754 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -185,7 +185,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()); } @@ -332,22 +332,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; } @@ -592,7 +592,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 e47dedc3f975..033386c3ebde 100644 --- a/src/qt/walletmodel.h +++ b/src/qt/walletmodel.h @@ -77,15 +77,15 @@ class WalletModel : public QObject 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 @@ -139,7 +139,7 @@ class WalletModel : public QObject void CopyFrom(UnlockContext&& rhs); }; - UnlockContext requestUnlock(bool fForMixingOnly=false); + UnlockContext requestUnlock(bool fForMixingOnly = false); static bool isWalletEnabled(); @@ -156,9 +156,7 @@ class WalletModel : public QObject QString getWalletName() const; QString getDisplayName() const; - bool isMultiwallet(); - - AddressTableModel* getAddressTableModel() const { return addressTableModel; } + bool isMultiwallet() const; uint256 getLastBlockProcessed() const; From bb5b44e6439baef36df0656b69bdd4fb2b1eb99f Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh <63189531+kwvg@users.noreply.github.com> Date: Tue, 27 May 2025 09:26:59 +0000 Subject: [PATCH 05/16] merge bitcoin-core/gui#581: Revamp `ClientModel` code to handle core signals --- src/qt/clientmodel.cpp | 165 ++++++++++++++--------------------------- src/qt/clientmodel.h | 15 ++-- 2 files changed, 61 insertions(+), 119 deletions(-) diff --git a/src/qt/clientmodel.cpp b/src/qt/clientmodel.cpp index bb4a34d4a6b8..36f7b8c54db7 100644 --- a/src/qt/clientmodel.cpp +++ b/src/qt/clientmodel.cpp @@ -25,9 +25,9 @@ #include #include -#include #include +#include #include #include @@ -183,21 +183,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()) @@ -265,129 +250,87 @@ 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(QString, QString::fromStdString(tip.block_hash.ToString())), - 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), QString::fromStdString(tip.block_hash.ToString()), verification_progress, header, sync_state); nLastUpdateNotification = now; } -static void NotifyChainLock(ClientModel *clientmodel, const std::string& bestChainLockHash, int bestChainLockHeight) -{ - // emits signal "chainlockChanged" - bool invoked = QMetaObject::invokeMethod(clientmodel, "chainLockChanged", Qt::QueuedConnection, - Q_ARG(QString, QString::fromStdString(bestChainLockHash)), - Q_ARG(int, bestChainLockHeight)); - assert(invoked); -} - -static void NotifyMasternodeListChanged(ClientModel *clientmodel, const CDeterministicMNList& newList, const CBlockIndex* pindex) -{ - clientmodel->setMasternodeList(newList, pindex); -} - -static void NotifyAdditionalDataSyncProgressChanged(ClientModel *clientmodel, double nSyncProgress) -{ - bool invoked = QMetaObject::invokeMethod(clientmodel, "additionalDataSyncProgressChanged", Qt::QueuedConnection, - Q_ARG(double, nSyncProgress)); - assert(invoked); -} - 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_chainlock = m_node.handleNotifyChainLock(std::bind(NotifyChainLock, this, std::placeholders::_1, std::placeholders::_2)); - m_handler_notify_header_tip = m_node.handleNotifyHeaderTip(std::bind(BlockTipChanged, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, true)); - m_handler_notify_masternodelist_changed = m_node.handleNotifyMasternodeListChanged(std::bind(NotifyMasternodeListChanged, this, std::placeholders::_1, std::placeholders::_2)); - m_handler_notify_additional_data_sync_progess_changed = m_node.handleNotifyAdditionalDataSyncProgressChanged(std::bind(NotifyAdditionalDataSyncProgressChanged, this, std::placeholders::_1)); + 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); + }); + m_handler_notify_additional_data_sync_progess_changed = m_node.handleNotifyAdditionalDataSyncProgressChanged( + [this](double nSyncProgress) { + Q_EMIT additionalDataSyncProgressChanged(nSyncProgress); + }); + m_handler_notify_chainlock = m_node.handleNotifyChainLock( + [this](const std::string& best_hash, int best_height) { + Q_EMIT chainLockChanged(QString::fromStdString(best_hash), best_height); + }); + m_handler_notify_masternodelist_changed = m_node.handleNotifyMasternodeListChanged( + [this](const CDeterministicMNList& newList, const CBlockIndex* pindex) { + setMasternodeList(newList, pindex); + }); } 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(); m_handler_notify_alert_changed->disconnect(); m_handler_banned_list_changed->disconnect(); m_handler_notify_block_tip->disconnect(); - m_handler_notify_chainlock->disconnect(); m_handler_notify_header_tip->disconnect(); - m_handler_notify_masternodelist_changed->disconnect(); m_handler_notify_additional_data_sync_progess_changed->disconnect(); + m_handler_notify_chainlock->disconnect(); + m_handler_notify_masternodelist_changed->disconnect(); } bool ClientModel::getProxyInfo(std::string& ip_port) const diff --git a/src/qt/clientmodel.h b/src/qt/clientmodel.h index 97782762f498..98806a068493 100644 --- a/src/qt/clientmodel.h +++ b/src/qt/clientmodel.h @@ -23,6 +23,10 @@ class PeerTableModel; class PeerTableSortProxy; enum class SynchronizationState; +namespace interfaces { +struct BlockTip; +} + QT_BEGIN_NAMESPACE class QTimer; QT_END_NAMESPACE @@ -105,10 +109,10 @@ class ClientModel : public QObject std::unique_ptr m_handler_notify_alert_changed; std::unique_ptr m_handler_banned_list_changed; std::unique_ptr m_handler_notify_block_tip; - std::unique_ptr m_handler_notify_chainlock; std::unique_ptr m_handler_notify_header_tip; - std::unique_ptr m_handler_notify_masternodelist_changed; std::unique_ptr m_handler_notify_additional_data_sync_progess_changed; + std::unique_ptr m_handler_notify_chainlock; + std::unique_ptr m_handler_notify_masternodelist_changed; OptionsModel *optionsModel; PeerTableModel *peerTableModel; PeerTableSortProxy* m_peer_table_sort_proxy{nullptr}; @@ -124,6 +128,7 @@ class ClientModel : public QObject CDeterministicMNListPtr mnListCached; const CBlockIndex* mnListTip{nullptr}; + void TipChanged(SynchronizationState sync_state, interfaces::BlockTip tip, double verification_progress, bool header); void subscribeToCoreSignals(); void unsubscribeFromCoreSignals(); @@ -143,12 +148,6 @@ class ClientModel : public QObject // 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 From 320f0e6b43362651141f7d6d5cbf32b86205c6a1 Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh <63189531+kwvg@users.noreply.github.com> Date: Sun, 29 May 2022 17:40:30 +0200 Subject: [PATCH 06/16] merge bitcoin-core/gui#608: Make `WalletModel::sendCoins()` return `void` --- src/qt/sendcoinsdialog.cpp | 11 ++--------- src/qt/walletmodel.cpp | 4 +--- src/qt/walletmodel.h | 2 +- 3 files changed, 4 insertions(+), 13 deletions(-) diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index b895c7d33d15..205d2571df7f 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -538,15 +538,8 @@ void SendCoinsDialog::sendButtonClicked([[maybe_unused]] bool checked) } } else { // now send the prepared transaction - WalletModel::SendCoinsReturn sendStatus = model->sendCoins(*m_current_transaction, m_coin_control->IsUsingCoinJoin()); - // process sendStatus and on error generate message shown to user - processSendCoinsReturn(sendStatus); - - if (sendStatus.status == WalletModel::OK) { - Q_EMIT coinsSent(m_current_transaction->getWtx()->GetHash()); - } else { - send_failure = true; - } + model->sendCoins(*m_current_transaction, m_coin_control->IsUsingCoinJoin()); + Q_EMIT coinsSent(m_current_transaction->getWtx()->GetHash()); } if (!send_failure) { accept(); diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index f299efba3754..98657ccbf3cf 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -277,7 +277,7 @@ WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransact return SendCoinsReturn(OK); } -WalletModel::SendCoinsReturn WalletModel::sendCoins(WalletModelTransaction &transaction, bool fIsCoinJoin) +void WalletModel::sendCoins(WalletModelTransaction& transaction, bool fIsCoinJoin) { QByteArray transaction_array; /* store serialized transaction */ @@ -328,8 +328,6 @@ WalletModel::SendCoinsReturn WalletModel::sendCoins(WalletModelTransaction &tran } checkBalanceChanged(m_wallet->getBalances()); // update balance immediately, otherwise there could be a short noticeable delay until pollBalanceChanged hits - - return SendCoinsReturn(OK); } OptionsModel* WalletModel::getOptionsModel() const diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h index 033386c3ebde..2d48ca2014f4 100644 --- a/src/qt/walletmodel.h +++ b/src/qt/walletmodel.h @@ -103,7 +103,7 @@ class WalletModel : public QObject SendCoinsReturn prepareTransaction(WalletModelTransaction &transaction, const CCoinControl& coinControl); // Send coins to a list of recipients - SendCoinsReturn sendCoins(WalletModelTransaction &transaction, bool fIsCoinJoin); + void sendCoins(WalletModelTransaction& transaction, bool fIsCoinJoin); // Wallet encryption bool setWalletEncrypted(const SecureString& passphrase); From 13990d84c4cb84add2d72ba2b52a768640fffb17 Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh <63189531+kwvg@users.noreply.github.com> Date: Fri, 16 Sep 2022 16:17:27 +0200 Subject: [PATCH 07/16] merge bitcoin-core/gui#673: Use fallback value for Version and User Agent during peer connection --- src/qt/rpcconsole.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp index 9c16b64c9bfe..7a3ab164ecb2 100644 --- a/src/qt/rpcconsole.cpp +++ b/src/qt/rpcconsole.cpp @@ -1304,8 +1304,12 @@ void RPCConsole::updateDetailWidget() ui->peerPingTime->setText(GUIUtil::formatPingTime(stats->nodeStats.m_last_ping_time)); ui->peerMinPing->setText(GUIUtil::formatPingTime(stats->nodeStats.m_min_ping_time)); ui->timeoffset->setText(GUIUtil::formatTimeOffset(stats->nodeStats.nTimeOffset)); - ui->peerVersion->setText(QString::number(stats->nodeStats.nVersion)); - ui->peerSubversion->setText(QString::fromStdString(stats->nodeStats.cleanSubVer)); + if (stats->nodeStats.nVersion) { + ui->peerVersion->setText(QString::number(stats->nodeStats.nVersion)); + } + if (!stats->nodeStats.cleanSubVer.empty()) { + ui->peerSubversion->setText(QString::fromStdString(stats->nodeStats.cleanSubVer)); + } ui->peerConnectionType->setText(GUIUtil::ConnectionTypeToQString(stats->nodeStats.m_conn_type, /* prepend_direction */ true)); ui->peerTransportType->setText(QString::fromStdString(TransportTypeAsString(stats->nodeStats.m_transport_type))); if (stats->nodeStats.m_transport_type == TransportProtocolType::V2) { From 5c894e109a280d583c5642029fd4cdefe049134e Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh <63189531+kwvg@users.noreply.github.com> Date: Fri, 23 May 2025 14:15:58 +0000 Subject: [PATCH 08/16] merge bitcoin-core/gui#676: Update peers window "Transaction Relay" label and tooltip --- src/qt/forms/debugwindow.ui | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/qt/forms/debugwindow.ui b/src/qt/forms/debugwindow.ui index ba685d77f825..49ea00b8990e 100644 --- a/src/qt/forms/debugwindow.ui +++ b/src/qt/forms/debugwindow.ui @@ -1178,10 +1178,10 @@ - Whether the peer requested us to relay transactions. + Whether we relay transactions to this peer (not available while the peer connection is being set up). - Wants Tx Relay + Transaction Relay From 6cc422d903829cc0bad364eb8c9550cb40234d32 Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh <63189531+kwvg@users.noreply.github.com> Date: Fri, 23 May 2025 14:17:04 +0000 Subject: [PATCH 09/16] merge bitcoin-core/gui#681: Fix Transaction Relay tooltip text in Peers details window --- src/qt/forms/debugwindow.ui | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qt/forms/debugwindow.ui b/src/qt/forms/debugwindow.ui index 49ea00b8990e..d42647588a85 100644 --- a/src/qt/forms/debugwindow.ui +++ b/src/qt/forms/debugwindow.ui @@ -1178,7 +1178,7 @@ - Whether we relay transactions to this peer (not available while the peer connection is being set up). + Whether we relay transactions to this peer. Transaction Relay From 8bdcd12ed3c6c978aecfc527977442ad57dad947 Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh <63189531+kwvg@users.noreply.github.com> Date: Fri, 23 May 2025 14:19:51 +0000 Subject: [PATCH 10/16] merge bitcoin-core/gui#690: Catch invalid networks combination crash --- src/qt/bitcoin.cpp | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index b72784a7703a..c932a45e391a 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -566,29 +566,30 @@ int GuiMain(int argc, char* argv[]) // Gracefully exit if the user cancels if (!Intro::showIfNeeded(did_show_intro, prune_MiB)) return EXIT_SUCCESS; - /// 6. Determine availability of data directory and parse dash.conf - /// - Do not call gArgs.GetDataDirNet() before this step finishes + /// 6a. Determine availability of data directory if (!CheckDataDirOption()) { InitError(strprintf(Untranslated("Specified data directory \"%s\" does not exist.\n"), gArgs.GetArg("-datadir", ""))); QMessageBox::critical(nullptr, PACKAGE_NAME, QObject::tr("Error: Specified data directory \"%1\" does not exist.").arg(QString::fromStdString(gArgs.GetArg("-datadir", "")))); return EXIT_FAILURE; } - if (!gArgs.ReadConfigFiles(error, true)) { - InitError(strprintf(Untranslated("Error reading configuration file: %s\n"), error)); - QMessageBox::critical(nullptr, PACKAGE_NAME, - QObject::tr("Error: Cannot parse configuration file: %1.").arg(QString::fromStdString(error))); - return EXIT_FAILURE; - } + try { + /// 6b. Parse dash.conf + /// - Do not call gArgs.GetDataDirNet() before this step finishes + if (!gArgs.ReadConfigFiles(error, true)) { + InitError(strprintf(Untranslated("Error reading configuration file: %s\n"), error)); + QMessageBox::critical(nullptr, PACKAGE_NAME, + QObject::tr("Error: Cannot parse configuration file: %1.").arg(QString::fromStdString(error))); + return EXIT_FAILURE; + } - /// 7. Determine network (and switch to network specific options) - // - Do not call Params() before this step - // - Do this after parsing the configuration file, as the network can be switched there - // - QSettings() will use the new application name after this, resulting in network-specific settings - // - Needs to be done before createOptionsModel + /// 7. Determine network (and switch to network specific options) + // - Do not call Params() before this step + // - Do this after parsing the configuration file, as the network can be switched there + // - QSettings() will use the new application name after this, resulting in network-specific settings + // - Needs to be done before createOptionsModel - // Check for -chain, -testnet or -regtest parameter (Params() calls are only valid after this clause) - try { + // Check for -chain, -testnet or -regtest parameter (Params() calls are only valid after this clause) SelectParams(gArgs.GetChainName()); } catch(std::exception &e) { InitError(Untranslated(strprintf("%s\n", e.what()))); From fd651ee8d207b4baaabe1498d4b0f8270f1557f5 Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh <63189531+kwvg@users.noreply.github.com> Date: Tue, 17 Jan 2023 16:46:33 +0100 Subject: [PATCH 11/16] merge bitcoin-core/gui#697: Remove reindex special case from the progress bar label --- src/interfaces/node.h | 7 ++----- src/node/interfaces.cpp | 3 +-- src/qt/bitcoingui.cpp | 5 +---- src/qt/clientmodel.cpp | 11 +++-------- src/qt/clientmodel.h | 7 +++---- 5 files changed, 10 insertions(+), 23 deletions(-) diff --git a/src/interfaces/node.h b/src/interfaces/node.h index a3d3a8624d72..088423986a02 100644 --- a/src/interfaces/node.h +++ b/src/interfaces/node.h @@ -235,11 +235,8 @@ class Node //! Is masternode. virtual bool isMasternode() = 0; - //! Get reindex. - virtual bool getReindex() = 0; - - //! Get importing. - virtual bool getImporting() = 0; + //! Is loading blocks. + virtual bool isLoadingBlocks() = 0; //! Set network active. virtual void setNetworkActive(bool active) = 0; diff --git a/src/node/interfaces.cpp b/src/node/interfaces.cpp index 3f1a7e9517e3..64b3b929d6cd 100644 --- a/src/node/interfaces.cpp +++ b/src/node/interfaces.cpp @@ -468,8 +468,7 @@ class NodeImpl : public Node { return m_context->mn_activeman != nullptr; } - bool getReindex() override { return ::fReindex; } - bool getImporting() override { return ::fImporting; } + bool isLoadingBlocks() override { return ::fReindex || ::fImporting; } void setNetworkActive(bool active) override { if (m_context->connman) { diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 9f7fc8c31270..1ea4b0c9a987 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -1529,7 +1529,7 @@ void BitcoinGUI::setNumBlocks(int count, const QDateTime& blockDate, const QStri statusBar()->clearMessage(); // Acquire current block source - enum BlockSource blockSource = clientModel->getBlockSource(); + BlockSource blockSource{clientModel->getBlockSource()}; switch (blockSource) { case BlockSource::NETWORK: if (header) { @@ -1546,9 +1546,6 @@ void BitcoinGUI::setNumBlocks(int count, const QDateTime& blockDate, const QStri progressBarLabel->setText(tr("Processing blocks on disk…")); } break; - case BlockSource::REINDEX: - progressBarLabel->setText(tr("Reindexing blocks on disk…")); - break; case BlockSource::NONE: if (header) { return; diff --git a/src/qt/clientmodel.cpp b/src/qt/clientmodel.cpp index 36f7b8c54db7..f03028f85444 100644 --- a/src/qt/clientmodel.cpp +++ b/src/qt/clientmodel.cpp @@ -183,15 +183,10 @@ uint256 ClientModel::getBestBlockHash() return m_cached_tip_blocks; } -enum BlockSource ClientModel::getBlockSource() const +BlockSource ClientModel::getBlockSource() const { - if (m_node.getReindex()) - return BlockSource::REINDEX; - else if (m_node.getImporting()) - return BlockSource::DISK; - else if (getNumConnections() > 0) - return BlockSource::NETWORK; - + if (m_node.isLoadingBlocks()) return BlockSource::DISK; + if (getNumConnections() > 0) return BlockSource::NETWORK; return BlockSource::NONE; } diff --git a/src/qt/clientmodel.h b/src/qt/clientmodel.h index 98806a068493..4129d976a475 100644 --- a/src/qt/clientmodel.h +++ b/src/qt/clientmodel.h @@ -33,9 +33,8 @@ QT_END_NAMESPACE enum class BlockSource { NONE, - REINDEX, DISK, - NETWORK + NETWORK, }; enum NumConnections { @@ -79,8 +78,8 @@ class ClientModel : public QObject void getAllGovernanceObjects(std::vector &obj); - //! Returns enum BlockSource of the current importing/syncing state - enum BlockSource getBlockSource() const; + //! Returns the block source of the current importing/syncing state + BlockSource getBlockSource() const; //! Return warnings to be displayed in status bar QString getStatusBarWarnings() const; From 9385d44bc62e20bfab322a1bb9d0a040c3740f20 Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh <63189531+kwvg@users.noreply.github.com> Date: Mon, 6 Mar 2023 12:49:39 +0100 Subject: [PATCH 12/16] merge bitcoin-core/gui#717: Use the stored CSubNet entry when unbanning --- src/qt/bantablemodel.cpp | 8 +++++++- src/qt/bantablemodel.h | 4 +++- src/qt/rpcconsole.cpp | 17 +++++++---------- 3 files changed, 17 insertions(+), 12 deletions(-) diff --git a/src/qt/bantablemodel.cpp b/src/qt/bantablemodel.cpp index e004fba3089d..ba0e7b8480b2 100644 --- a/src/qt/bantablemodel.cpp +++ b/src/qt/bantablemodel.cpp @@ -43,7 +43,7 @@ class BanTablePriv /** Order (ascending or descending) to sort nodes by */ Qt::SortOrder sortOrder; - /** Pull a full list of banned nodes from CNode into our cache */ + /** Pull a full list of banned nodes from interfaces::Node into our cache */ void refreshBanlist(interfaces::Node& node) { banmap_t banMap; @@ -181,3 +181,9 @@ bool BanTableModel::shouldShow() { return priv->size() > 0; } + +bool BanTableModel::unban(const QModelIndex& index) +{ + CCombinedBan* ban{static_cast(index.internalPointer())}; + return ban != nullptr && m_node.unban(ban->subnet); +} diff --git a/src/qt/bantablemodel.h b/src/qt/bantablemodel.h index 0a3090517227..adb97ad1c0ea 100644 --- a/src/qt/bantablemodel.h +++ b/src/qt/bantablemodel.h @@ -37,7 +37,7 @@ class BannedNodeLessThan }; /** - Qt model providing information about connected peers, similar to the + Qt model providing information about banned peers, similar to the "getpeerinfo" RPC call. Used by the rpc console UI. */ class BanTableModel : public QAbstractTableModel @@ -68,6 +68,8 @@ class BanTableModel : public QAbstractTableModel bool shouldShow(); + bool unban(const QModelIndex& index); + public Q_SLOTS: void refresh(); diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp index 7a3ab164ecb2..5d7d92a6e6ac 100644 --- a/src/qt/rpcconsole.cpp +++ b/src/qt/rpcconsole.cpp @@ -14,7 +14,6 @@ #include #include -#include #include #include #include @@ -1492,15 +1491,13 @@ void RPCConsole::unbanSelectedNode() // Get selected ban addresses QList nodes = GUIUtil::getEntryData(ui->banlistWidget, BanTableModel::Address); - for(int i = 0; i < nodes.count(); i++) - { - // Get currently selected ban address - QString strNode = nodes.at(i).data().toString(); - CSubNet possibleSubnet{LookupSubNet(strNode.toStdString())}; - if (possibleSubnet.IsValid() && m_node.unban(possibleSubnet)) - { - clientModel->getBanTableModel()->refresh(); - } + BanTableModel* ban_table_model{clientModel->getBanTableModel()}; + bool unbanned{false}; + for (const auto& node_index : nodes) { + unbanned |= ban_table_model->unban(node_index); + } + if (unbanned) { + ban_table_model->refresh(); } } From 107a180ffc327c003d16a8c905c8abe95586e956 Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh <63189531+kwvg@users.noreply.github.com> Date: Wed, 13 Sep 2023 15:01:38 -0300 Subject: [PATCH 13/16] merge bitcoin-core/gui#757: Add wallet name to address book page title --- src/qt/addressbookpage.cpp | 26 +++++++++++++++----------- src/qt/addressbookpage.h | 1 + src/qt/addresstablemodel.cpp | 2 ++ src/qt/addresstablemodel.h | 2 ++ 4 files changed, 20 insertions(+), 11 deletions(-) diff --git a/src/qt/addressbookpage.cpp b/src/qt/addressbookpage.cpp index ce50842a6d12..441531bb9207 100644 --- a/src/qt/addressbookpage.cpp +++ b/src/qt/addressbookpage.cpp @@ -71,9 +71,7 @@ AddressBookPage::AddressBookPage(Mode _mode, Tabs _tab, QWidget* parent) : ui->showAddressQRCode->setEnabled(false); #endif - switch(mode) - { - case ForSelection: + if (mode == ForSelection) { switch(tab) { case SendingTab: setWindowTitle(tr("Choose the address to send coins to")); break; @@ -84,14 +82,6 @@ AddressBookPage::AddressBookPage(Mode _mode, Tabs _tab, QWidget* parent) : ui->tableView->setFocus(); ui->closeButton->setText(tr("C&hoose")); ui->exportButton->hide(); - break; - case ForEditing: - switch(tab) - { - case SendingTab: setWindowTitle(tr("Sending addresses")); break; - case ReceivingTab: setWindowTitle(tr("Receiving addresses")); break; - } - break; } switch(tab) { @@ -162,6 +152,7 @@ void AddressBookPage::setModel(AddressTableModel *_model) connect(_model, &AddressTableModel::rowsInserted, this, &AddressBookPage::selectNewAddress); selectionChanged(); + this->updateWindowsTitleWithWalletName(); } void AddressBookPage::on_copyAddress_clicked() @@ -347,3 +338,16 @@ void AddressBookPage::selectNewAddress(const QModelIndex &parent, int begin, int newAddressToSelect.clear(); } } + +void AddressBookPage::updateWindowsTitleWithWalletName() +{ + const QString walletName = this->model->GetWalletDisplayName(); + + if (mode == ForEditing) { + switch(tab) + { + case SendingTab: setWindowTitle(tr("Sending addresses - %1").arg(walletName)); break; + case ReceivingTab: setWindowTitle(tr("Receiving addresses - %1").arg(walletName)); break; + } + } +} diff --git a/src/qt/addressbookpage.h b/src/qt/addressbookpage.h index 2d858b2c957a..2446443e5d3f 100644 --- a/src/qt/addressbookpage.h +++ b/src/qt/addressbookpage.h @@ -55,6 +55,7 @@ public Q_SLOTS: AddressBookSortFilterProxyModel *proxyModel; QMenu *contextMenu; QString newAddressToSelect; + void updateWindowsTitleWithWalletName(); private Q_SLOTS: /** Delete currently selected address entry */ diff --git a/src/qt/addresstablemodel.cpp b/src/qt/addresstablemodel.cpp index 528649667f54..4a09af9fff5b 100644 --- a/src/qt/addresstablemodel.cpp +++ b/src/qt/addresstablemodel.cpp @@ -448,3 +448,5 @@ void AddressTableModel::emitDataChanged(int idx) { Q_EMIT dataChanged(index(idx, 0, QModelIndex()), index(idx, columns.length()-1, QModelIndex())); } + +QString AddressTableModel::GetWalletDisplayName() const { return walletModel->getDisplayName(); }; diff --git a/src/qt/addresstablemodel.h b/src/qt/addresstablemodel.h index 050e46664cbb..54eda1fed79a 100644 --- a/src/qt/addresstablemodel.h +++ b/src/qt/addresstablemodel.h @@ -80,6 +80,8 @@ class AddressTableModel : public QAbstractTableModel EditStatus getEditStatus() const { return editStatus; } + QString GetWalletDisplayName() const; + private: WalletModel* const walletModel; AddressTablePriv *priv = nullptr; From ae0ca104a7c8df33086dc4656e291b8ed6599562 Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh <63189531+kwvg@users.noreply.github.com> Date: Fri, 23 May 2025 09:47:20 +0000 Subject: [PATCH 14/16] merge bitcoin-core/gui#751: macOS, do not process actions during shutdown --- src/qt/bitcoingui.cpp | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 1ea4b0c9a987..c5853b1ca386 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -263,7 +263,6 @@ BitcoinGUI::~BitcoinGUI() trayIcon->hide(); #ifdef Q_OS_MAC delete m_app_nap_inhibitor; - delete appMenuBar; MacDockIconHandler::cleanup(); #endif @@ -592,13 +591,7 @@ void BitcoinGUI::createActions() void BitcoinGUI::createMenuBar() { -#ifdef Q_OS_MAC - // Create a decoupled menu bar on Mac which stays even if the window is closed - appMenuBar = new QMenuBar(); -#else - // Get the main window's menu bar on other platforms appMenuBar = menuBar(); -#endif // Configure the menus QMenu *file = appMenuBar->addMenu(tr("&File")); @@ -843,6 +836,7 @@ void BitcoinGUI::setClientModel(ClientModel *_clientModel, interfaces::BlockAndH MacDockIconHandler *dockIconHandler = MacDockIconHandler::instance(); connect(dockIconHandler, &MacDockIconHandler::dockIconClicked, [this] { + if (m_node.shutdownRequested()) return; // nothing to show, node is shutting down. showNormalIfMinimized(); activateWindow(); }); @@ -1124,6 +1118,8 @@ void BitcoinGUI::createIconMenu(QMenu *pmenu) // See https://bugreports.qt.io/browse/QTBUG-91697 pmenu, &QMenu::aboutToShow, [this, show_hide_action, send_action, cj_send_action, receive_action, sign_action, verify_action, options_action, node_window_action, quit_action, repair_action, backups_action, info_action, graph_action, peer_action, conf_action] { + if (m_node.shutdownRequested()) return; // nothing to do, node is shutting down. + if (show_hide_action) show_hide_action->setText( (!isHidden() && !isMinimized() && !GUIUtil::isObscured(this)) ? tr("&Hide") : From 56fc672a52d8d0e4ef6b950d3e53dd8ece9eaee4 Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh <63189531+kwvg@users.noreply.github.com> Date: Fri, 23 May 2025 17:35:27 +0000 Subject: [PATCH 15/16] merge bitcoin-core/gui#773: Check for private keys disabled before attempting unlock --- src/qt/walletmodel.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index 98657ccbf3cf..4684965c1844 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -522,6 +522,14 @@ void WalletModel::unsubscribeFromCoreSignals() // WalletModel::UnlockContext implementation WalletModel::UnlockContext WalletModel::requestUnlock(bool fForMixingOnly) { + // Bugs in earlier versions may have resulted in wallets with private keys disabled to become "encrypted" + // (encryption keys are present, but not actually doing anything). + // To avoid issues with such wallets, check if the wallet has private keys disabled, and if so, return a context + // that indicates the wallet is not encrypted. + if (m_wallet->privateKeysDisabled()) { + return UnlockContext(this, /*valid=*/true, /*was_locked=*/false, /*was_mixing=*/false); + } + EncryptionStatus encStatusOld = getEncryptionStatus(); // Wallet was completely locked From a41f4e711298088a3ccb262f85f5a237c2f876f8 Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh <63189531+kwvg@users.noreply.github.com> Date: Wed, 28 Feb 2024 09:47:20 -0300 Subject: [PATCH 16/16] merge bitcoin-core/gui#801: Fix nullptr clientModel access during shutdown --- src/qt/bitcoin.cpp | 5 ++++ src/qt/bitcoingui.cpp | 5 +--- src/qt/clientmodel.cpp | 58 ++++++++++++++++++++---------------------- src/qt/clientmodel.h | 13 +++------- src/qt/rpcconsole.cpp | 1 + 5 files changed, 37 insertions(+), 45 deletions(-) diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index c932a45e391a..0ece5876c83e 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -348,6 +348,11 @@ void BitcoinApplication::requestShutdown() // Request node shutdown, which can interrupt long operations, like // rescanning a wallet. node().startShutdown(); + // Prior to unsetting the client model, stop listening backend signals + if (clientModel) { + clientModel->stop(); + } + // Unsetting the client model can cause the current thread to wait for node // to complete an operation, like wait for a RPC execution to complete. window->setClientModel(nullptr); diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index c5853b1ca386..08d0c6a16ca5 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -1305,10 +1305,7 @@ void BitcoinGUI::gotoLoadPSBT(bool from_clipboard) void BitcoinGUI::updateNetworkState() { - if (clientModel == nullptr) { - return; - } - + if (!clientModel) return; static int nCountPrev{0}; static bool fNetworkActivePrev{false}; int count = clientModel->getNumConnections(); diff --git a/src/qt/clientmodel.cpp b/src/qt/clientmodel.cpp index f03028f85444..7b20ce368b97 100644 --- a/src/qt/clientmodel.cpp +++ b/src/qt/clientmodel.cpp @@ -72,7 +72,7 @@ ClientModel::ClientModel(interfaces::Node& node, OptionsModel *_optionsModel, QO subscribeToCoreSignals(); } -ClientModel::~ClientModel() +void ClientModel::stop() { unsubscribeFromCoreSignals(); @@ -80,6 +80,11 @@ ClientModel::~ClientModel() m_thread->wait(); } +ClientModel::~ClientModel() +{ + stop(); +} + int ClientModel::getNumConnections(unsigned int flags) const { ConnectionDirection connections = ConnectionDirection::None; @@ -270,62 +275,53 @@ void ClientModel::TipChanged(SynchronizationState sync_state, interfaces::BlockT void ClientModel::subscribeToCoreSignals() { - m_handler_show_progress = m_node.handleShowProgress( + m_event_handlers.emplace_back(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( + })); + m_event_handlers.emplace_back(m_node.handleNotifyNumConnectionsChanged( [this](int new_num_connections) { Q_EMIT numConnectionsChanged(new_num_connections); - }); - m_handler_notify_network_active_changed = m_node.handleNotifyNetworkActiveChanged( + })); + m_event_handlers.emplace_back(m_node.handleNotifyNetworkActiveChanged( [this](bool network_active) { Q_EMIT networkActiveChanged(network_active); - }); - m_handler_notify_alert_changed = m_node.handleNotifyAlertChanged( + })); + m_event_handlers.emplace_back(m_node.handleNotifyAlertChanged( [this]() { qDebug() << "ClientModel: NotifyAlertChanged"; Q_EMIT alertsChanged(getStatusBarWarnings()); - }); - m_handler_banned_list_changed = m_node.handleBannedListChanged( + })); + m_event_handlers.emplace_back(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( + })); + m_event_handlers.emplace_back(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( + })); + m_event_handlers.emplace_back(m_node.handleNotifyHeaderTip( [this](SynchronizationState sync_state, interfaces::BlockTip tip, double verification_progress) { TipChanged(sync_state, tip, verification_progress, /*header=*/true); - }); - m_handler_notify_additional_data_sync_progess_changed = m_node.handleNotifyAdditionalDataSyncProgressChanged( + })); + m_event_handlers.emplace_back(m_node.handleNotifyAdditionalDataSyncProgressChanged( [this](double nSyncProgress) { Q_EMIT additionalDataSyncProgressChanged(nSyncProgress); - }); - m_handler_notify_chainlock = m_node.handleNotifyChainLock( + })); + m_event_handlers.emplace_back(m_node.handleNotifyChainLock( [this](const std::string& best_hash, int best_height) { Q_EMIT chainLockChanged(QString::fromStdString(best_hash), best_height); - }); - m_handler_notify_masternodelist_changed = m_node.handleNotifyMasternodeListChanged( + })); + m_event_handlers.emplace_back(m_node.handleNotifyMasternodeListChanged( [this](const CDeterministicMNList& newList, const CBlockIndex* pindex) { setMasternodeList(newList, pindex); - }); + })); } void ClientModel::unsubscribeFromCoreSignals() { - m_handler_show_progress->disconnect(); - m_handler_notify_num_connections_changed->disconnect(); - m_handler_notify_network_active_changed->disconnect(); - m_handler_notify_alert_changed->disconnect(); - m_handler_banned_list_changed->disconnect(); - m_handler_notify_block_tip->disconnect(); - m_handler_notify_header_tip->disconnect(); - m_handler_notify_additional_data_sync_progess_changed->disconnect(); - m_handler_notify_chainlock->disconnect(); - m_handler_notify_masternodelist_changed->disconnect(); + m_event_handlers.clear(); } bool ClientModel::getProxyInfo(std::string& ip_port) const diff --git a/src/qt/clientmodel.h b/src/qt/clientmodel.h index 4129d976a475..613f8be7d428 100644 --- a/src/qt/clientmodel.h +++ b/src/qt/clientmodel.h @@ -57,6 +57,8 @@ class ClientModel : public QObject explicit ClientModel(interfaces::Node& node, OptionsModel *optionsModel, QObject *parent = nullptr); ~ClientModel(); + void stop(); + interfaces::Node& node() const { return m_node; } interfaces::Masternode::Sync& masternodeSync() const { return m_node.masternodeSync(); } interfaces::CoinJoin::Options& coinJoinOptions() const { return m_node.coinJoinOptions(); } @@ -102,16 +104,7 @@ class ClientModel : public QObject private: interfaces::Node& m_node; - std::unique_ptr m_handler_show_progress; - std::unique_ptr m_handler_notify_num_connections_changed; - std::unique_ptr m_handler_notify_network_active_changed; - std::unique_ptr m_handler_notify_alert_changed; - std::unique_ptr m_handler_banned_list_changed; - std::unique_ptr m_handler_notify_block_tip; - std::unique_ptr m_handler_notify_header_tip; - std::unique_ptr m_handler_notify_additional_data_sync_progess_changed; - std::unique_ptr m_handler_notify_chainlock; - std::unique_ptr m_handler_notify_masternodelist_changed; + std::vector> m_event_handlers; OptionsModel *optionsModel; PeerTableModel *peerTableModel; PeerTableSortProxy* m_peer_table_sort_proxy{nullptr}; diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp index 5d7d92a6e6ac..1546dad74e00 100644 --- a/src/qt/rpcconsole.cpp +++ b/src/qt/rpcconsole.cpp @@ -1033,6 +1033,7 @@ void RPCConsole::message(int category, const QString &message, bool html) void RPCConsole::updateNetworkState() { + if (!clientModel) return; QString connections = QString::number(clientModel->getNumConnections()) + " ("; connections += tr("In:") + " " + QString::number(clientModel->getNumConnections(CONNECTIONS_IN)) + " / "; connections += tr("Out:") + " " + QString::number(clientModel->getNumConnections(CONNECTIONS_OUT)) + ")";