diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index f3c0cf10ee..b579b8aa8d 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -408,6 +408,11 @@ void BitcoinGUI::createActions() resetblockchainAction = new QAction(tr("&Reset blockchain data"), this); resetblockchainAction->setToolTip(tr("Remove blockchain data and start chain from zero")); + m_mask_values_action = new QAction(tr("&Mask values"), this); + m_mask_values_action->setShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_M)); + m_mask_values_action->setStatusTip(tr("Mask the values in the Overview screen")); + m_mask_values_action->setCheckable(true); + connect(quitAction, &QAction::triggered, this, &BitcoinGUI::tryQuit); connect(aboutAction, &QAction::triggered, this, &BitcoinGUI::aboutClicked); connect(optionsAction, &QAction::triggered, this, &BitcoinGUI::optionsClicked); @@ -554,6 +559,8 @@ void BitcoinGUI::createMenuBar() settings->addSeparator(); settings->addAction(optionsAction); settings->addAction(openConfigAction); + settings->addSeparator(); + settings->addAction(m_mask_values_action); QMenu *community = appMenuBar->addMenu(tr("&Community")); community->addAction(bxAction); @@ -758,6 +765,13 @@ void BitcoinGUI::setClientModel(ClientModel *clientModel) // Report errors from network/worker thread connect(clientModel, &ClientModel::error, this, &BitcoinGUI::error); + // Ensure the checkbox for mask values action matches the retrieved state from the optionsModel. + m_mask_values_action->setChecked(isPrivacyModeActivated()); + + // Connect the action to the setPrivacy function. This has to be done after the setting of the + // checkbox state instead of in the createActions above. + connect(m_mask_values_action, &QAction::toggled, this, &BitcoinGUI::setPrivacy); + rpcConsole->setClientModel(clientModel); addressBookPage->setOptionsModel(clientModel->getOptionsModel()); receiveCoinsPage->setOptionsModel(clientModel->getOptionsModel()); @@ -839,6 +853,13 @@ void BitcoinGUI::createTrayIcon() notificator = new Notificator(qApp->applicationName(), trayIcon, this); } +bool BitcoinGUI::isPrivacyModeActivated() const +{ + if (!clientModel || !clientModel->getOptionsModel()) return false; + + return clientModel->getOptionsModel()->getMaskValues(); +} + void BitcoinGUI::createTrayIconMenu() { #ifndef Q_OS_MAC @@ -1278,6 +1299,20 @@ void BitcoinGUI::resetblockchainClicked() } } +void BitcoinGUI::setPrivacy() +{ + if (!clientModel || !clientModel->getOptionsModel()) return; + + bool privacy_mode(!clientModel->getOptionsModel()->getMaskValues()); + + clientModel->getOptionsModel()->setMaskValues(privacy_mode); + + // Need to call updateMinerStatus from here to feed back in the Coin Weight to the overview screen. + // Not ideal, but the normal trigger to update the Staking fields on the overview screen normally come from + // the core, not the GUI. Here the privacy state change is coming from the GUI. + clientModel->updateMinerStatus(g_miner_status.StakingActive(), g_miner_status.GetSearchReport().CoinWeight()); +} + bool BitcoinGUI::tryQuit() { if(clientModel && diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h index 1a6dd29244..491778c61a 100644 --- a/src/qt/bitcoingui.h +++ b/src/qt/bitcoingui.h @@ -72,6 +72,12 @@ class BitcoinGUI : public QMainWindow */ void setVotingModel(VotingModel *votingModel); + /** + * @brief Queries the state of privacy mode (mask values on overview screen). + * @return boolean of the mask values state + */ + bool isPrivacyModeActivated() const; + protected: void changeEvent(QEvent *e); void closeEvent(QCloseEvent *event); @@ -140,6 +146,7 @@ class BitcoinGUI : public QMainWindow QAction *openRPCConsoleAction; QAction *snapshotAction; QAction *resetblockchainAction; + QAction *m_mask_values_action; QSystemTrayIcon *trayIcon; QMenu *trayIconMenu; @@ -244,6 +251,7 @@ private slots: void peersClicked(); void snapshotClicked(); void resetblockchainClicked(); + void setPrivacy(); bool tryQuit(); #ifndef Q_OS_MAC diff --git a/src/qt/overviewpage.cpp b/src/qt/overviewpage.cpp index 31cb2b2666..45abac7402 100644 --- a/src/qt/overviewpage.cpp +++ b/src/qt/overviewpage.cpp @@ -196,8 +196,16 @@ int OverviewPage::getNumTransactionsForView() // Compute the maximum number of transactions the transaction list widget // can hold without overflowing. const size_t itemHeight = txdelegate->height() + ui->listTransactions->spacing(); - const size_t contentsHeight = ui->listTransactions->height(); - const int numItems = contentsHeight / itemHeight; + + // We have to use the frame here because when the listTransactions is hidden, the recentTransactionsNoResult + // takes up the space and would cause the calculation to be off. + const size_t contentsHeight = std::max(ui->recentTransactionsFrame->height() - ui->recentTransLabel->height(), 0); + + LogPrint(BCLog::LogFlags::QT, "INFO: %s: contentsHeight = %u, itemHeight = %u", + __func__, contentsHeight, itemHeight); + + // take one off so that there is not a "half-visible one" there, ensure not below 0. + const int numItems = std::max((int) (contentsHeight / itemHeight) - 1, 0); return numItems; } @@ -262,12 +270,13 @@ void OverviewPage::setBalance(qint64 balance, qint64 stake, qint64 unconfirmedBa currentStake = stake; currentUnconfirmedBalance = unconfirmedBalance; currentImmatureBalance = immatureBalance; - ui->headerBalanceLabel->setText(BitcoinUnits::formatOverviewRounded(balance)); - ui->balanceLabel->setText(BitcoinUnits::formatWithUnit(unit, balance)); - ui->stakeLabel->setText(BitcoinUnits::formatWithUnit(unit, stake)); - ui->unconfirmedLabel->setText(BitcoinUnits::formatWithUnit(unit, unconfirmedBalance)); - ui->immatureLabel->setText(BitcoinUnits::formatWithUnit(unit, immatureBalance)); - ui->totalLabel->setText(BitcoinUnits::formatWithUnit(unit, balance + stake + unconfirmedBalance + immatureBalance)); + ui->headerBalanceLabel->setText(BitcoinUnits::formatOverviewRounded(balance, m_privacy)); + ui->balanceLabel->setText(BitcoinUnits::formatWithPrivacy(unit, balance, m_privacy)); + ui->stakeLabel->setText(BitcoinUnits::formatWithPrivacy(unit, stake, m_privacy)); + ui->unconfirmedLabel->setText(BitcoinUnits::formatWithPrivacy(unit, unconfirmedBalance, m_privacy)); + ui->immatureLabel->setText(BitcoinUnits::formatWithPrivacy(unit, immatureBalance, m_privacy)); + ui->totalLabel->setText(BitcoinUnits::formatWithPrivacy(unit, balance + stake + unconfirmedBalance + immatureBalance, + m_privacy)); // only show immature (newly mined) balance if it's non-zero, so as not to complicate things // for the non-mining users @@ -289,7 +298,15 @@ void OverviewPage::setDifficulty(double difficulty, double net_weight) void OverviewPage::setCoinWeight(double coin_weight) { - ui->coinWeightLabel->setText(QString::number(coin_weight, 'f', 2)); + QString text; + + if (m_privacy) { + text = QString("#.##"); + } else { + text = QString::number(coin_weight, 'f', 2); + } + + ui->coinWeightLabel->setText(text); } void OverviewPage::setCurrentPollTitle(const QString& title) @@ -297,6 +314,22 @@ void OverviewPage::setCurrentPollTitle(const QString& title) ui->currentPollsTitleLabel->setText(title); } +void OverviewPage::setPrivacy(bool privacy) +{ + m_privacy = privacy; + if (currentBalance != -1) { + setBalance(currentBalance, currentStake, currentUnconfirmedBalance, currentImmatureBalance); + } + + ui->listTransactions->setVisible(!m_privacy); + if (researcherModel) researcherModel->setMaskAccrual(m_privacy); + + LogPrint(BCLog::LogFlags::QT, "INFO: %s: m_privacy = %u", __func__, m_privacy); + + updateTransactions(); + updatePendingAccrual(); +} + void OverviewPage::setResearcherModel(ResearcherModel *researcherModel) { this->researcherModel = researcherModel; @@ -324,7 +357,13 @@ void OverviewPage::setWalletModel(WalletModel *model) filter->setDynamicSortFilter(true); filter->setSortRole(Qt::EditRole); filter->setShowInactive(false); - filter->setLimit(getNumTransactionsForView()); + + int num_transactions_for_view = getNumTransactionsForView(); + filter->setLimit(num_transactions_for_view); + + LogPrint(BCLog::LogFlags::QT, "INFO: %s: num_transactions_for_view = %i, getLimit() = %i", + __func__, num_transactions_for_view, filter->getLimit()); + filter->sort(TransactionTableModel::Status, Qt::DescendingOrder); ui->listTransactions->setModel(filter.get()); ui->listTransactions->setModelColumn(TransactionTableModel::ToAddress); @@ -337,6 +376,12 @@ void OverviewPage::setWalletModel(WalletModel *model) connect(model->getOptionsModel(), &OptionsModel::LimitTxnDisplayChanged, this, &OverviewPage::updateTransactions); connect(model, &WalletModel::transactionUpdated, this, &OverviewPage::updateTransactions); + + // Set the privacy state for the overview screen from the optionsModel for init. + setPrivacy(model->getOptionsModel()->getMaskValues()); + + // Connect the privacy mode setting to the options dialog. + connect(walletModel->getOptionsModel(), &OptionsModel::MaskValuesChanged, this, & OverviewPage::setPrivacy); } // update the display unit, to not use the default ("BTC") diff --git a/src/qt/overviewpage.h b/src/qt/overviewpage.h index c69fe894fd..172281bf8d 100644 --- a/src/qt/overviewpage.h +++ b/src/qt/overviewpage.h @@ -35,6 +35,7 @@ public slots: void setDifficulty(double difficulty, double net_weight); void setCoinWeight(double coin_weight); void setCurrentPollTitle(const QString& title); + void setPrivacy(bool privacy); signals: void transactionClicked(const QModelIndex &index); @@ -55,6 +56,7 @@ public slots: qint64 currentUnconfirmedBalance; qint64 currentImmatureBalance; int scaledDecorationSize; + bool m_privacy = false; TxViewDelegate *txdelegate; std::unique_ptr filter; diff --git a/src/qt/researcher/researchermodel.cpp b/src/qt/researcher/researchermodel.cpp index 1fc24eb513..a70609de35 100644 --- a/src/qt/researcher/researchermodel.cpp +++ b/src/qt/researcher/researchermodel.cpp @@ -202,6 +202,11 @@ void ResearcherModel::setTheme(const QString& theme_name) emit beaconChanged(); } +void ResearcherModel::setMaskAccrual(bool privacy) +{ + m_mask_accrual = privacy; +} + bool ResearcherModel::configuredForInvestorMode() const { return m_configured_for_investor_mode; @@ -303,11 +308,15 @@ QString ResearcherModel::formatMagnitude() const QString ResearcherModel::formatAccrual(const int display_unit) const { + QString text; + if (outOfSync()) { - return "..."; + text = "..."; + } else { + text = BitcoinUnits::formatWithPrivacy(display_unit, m_researcher->Accrual(), m_mask_accrual); } - return BitcoinUnits::formatWithUnit(display_unit, m_researcher->Accrual()); + return text; } QString ResearcherModel::formatStatus() const diff --git a/src/qt/researcher/researchermodel.h b/src/qt/researcher/researchermodel.h index d3a229af33..0260044831 100644 --- a/src/qt/researcher/researchermodel.h +++ b/src/qt/researcher/researchermodel.h @@ -81,6 +81,7 @@ class ResearcherModel : public QObject void showWizard(WalletModel* wallet_model); void setTheme(const QString& theme_name); + void setMaskAccrual(bool privacy); bool configuredForInvestorMode() const; bool outOfSync() const; @@ -120,6 +121,7 @@ class ResearcherModel : public QObject bool m_configured_for_investor_mode; bool m_wizard_open; bool m_out_of_sync; + bool m_mask_accrual; QString m_theme_suffix; void subscribeToCoreSignals();