Merge #6285: backport: bitcoin-core/gui#29, gui#123, #164, gui#256, gui#309, gui#313, gui#329, gui#331, gui#333, gui#346, gui#393

6431f71b3a Merge bitcoin-core/gui#393: Fix regression in "Encrypt Wallet" menu item (Hennadii Stepanov)
fc900a8aea Merge bitcoin-core/gui#333: refactor: Signal-slot connections cleanup (Hennadii Stepanov)
9ca2aad0b3 Merge bitcoin-core/gui#164: Handle peer addition/removal in a right way (Hennadii Stepanov)
7d9ce32562 Merge bitcoin-core/gui#29: refactor: Optimize signal-slot connections logic (Hennadii Stepanov)
3be79a9ed9 Merge bitcoin-core/gui#256: Save/restore column sizes of the tables in the Peers tab (Hennadii Stepanov)
f4fccd31cb Merge bitcoin-core/gui#329: Make console buttons look clickable (Hennadii Stepanov)
5a0d524506 Merge bitcoin-core/gui#123: rpc: Do not accept command while executing another one (Hennadii Stepanov)
19310646e0 Merge bitcoin-core/gui#331: Make RPC console welcome message translation-friendly (Hennadii Stepanov)
69a1305978 Merge bitcoin-core/gui#309: Add access to the Peers tab from the network icon (Hennadii Stepanov)
c858325d40 Merge bitcoin-core/gui#346: English translations update (Hennadii Stepanov)
412445afb5 Merge bitcoin-core/gui#313: qt: Optimize string concatenation by default (W. J. van der Laan)

Pull request description:

  ## Issue being fixed or feature implemented
  Gui related backports from bitcoin v22

  ## What was done?
  See commits

  ## How Has This Been Tested?
  Run unit/functional tests

  See also:

  <img alt="right menu" src="https://user-images.githubusercontent.com/32963518/116794314-d64b9b80-aad4-11eb-89ca-7f75c7442ba8.gif"/>

  ## Breaking Changes
  N/A

  ## Checklist:
  - [x] I have performed a self-review of my own code
  - [ ] I have commented my code, particularly in hard-to-understand areas
  - [ ] I have added or updated relevant unit/integration/functional/e2e tests
  - [ ] I have made corresponding changes to the documentation
  - [x] I have assigned this pull request to a milestone

ACKs for top commit:
  UdjinM6:
    light ACK 6431f71b3a
  PastaPastaPasta:
    utACK 6431f71b3a

Tree-SHA512: bb14de71c9375b10da695db6c521c26686815b8b5ca2748bfe3bd2eafa9d332acd60acd85a1f2eed3aa831d16e5741ecc7570130ce9cf5bff011c065b55d62b2
This commit is contained in:
pasta 2024-09-26 20:19:27 -05:00
commit 750475ffaa
No known key found for this signature in database
GPG Key ID: E2F3D7916E722D38
24 changed files with 274 additions and 223 deletions

View File

@ -364,7 +364,7 @@ RES_ANIMATION = $(wildcard $(srcdir)/qt/res/animation/spinner-*.png)
BITCOIN_RC = qt/res/dash-qt-res.rc
BITCOIN_QT_INCLUDES = -DQT_NO_KEYWORDS
BITCOIN_QT_INCLUDES = -DQT_NO_KEYWORDS -DQT_USE_QSTRINGBUILDER
qt_libbitcoinqt_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(BITCOIN_QT_INCLUDES) \
$(QT_INCLUDES) $(QT_DBUS_INCLUDES) $(QR_CFLAGS)

View File

@ -54,7 +54,6 @@
#include <QMessageBox>
#include <QProcess>
#include <QSettings>
#include <QStringBuilder>
#include <QThread>
#include <QTimer>
#include <QTranslator>
@ -492,8 +491,8 @@ void BitcoinApplication::handleRunawayException(const QString &message)
{
QMessageBox::critical(
nullptr, tr("Runaway exception"),
tr("A fatal error occurred. %1 can no longer continue safely and will quit.").arg(PACKAGE_NAME) %
QLatin1String("<br><br>") % GUIUtil::MakeHtmlLink(message, PACKAGE_BUGREPORT));
tr("A fatal error occurred. %1 can no longer continue safely and will quit.").arg(PACKAGE_NAME) +
QLatin1String("<br><br>") + GUIUtil::MakeHtmlLink(message, PACKAGE_BUGREPORT));
::exit(EXIT_FAILURE);
}
@ -503,8 +502,8 @@ void BitcoinApplication::handleNonFatalException(const QString& message)
QMessageBox::warning(
nullptr, tr("Internal error"),
tr("An internal error occurred. %1 will attempt to continue safely. This is "
"an unexpected bug which can be reported as described below.").arg(PACKAGE_NAME) %
QLatin1String("<br><br>") % GUIUtil::MakeHtmlLink(message, PACKAGE_BUGREPORT));
"an unexpected bug which can be reported as described below.").arg(PACKAGE_NAME) +
QLatin1String("<br><br>") + GUIUtil::MakeHtmlLink(message, PACKAGE_BUGREPORT));
}
WId BitcoinApplication::getMainWinId() const

View File

@ -47,6 +47,7 @@
#include <QApplication>
#include <QButtonGroup>
#include <QComboBox>
#include <QCursor>
#include <QDateTime>
#include <QDragEnterEvent>
#include <QKeySequence>
@ -114,6 +115,11 @@ BitcoinGUI::BitcoinGUI(interfaces::Node& node, const NetworkStyle* networkStyle,
connect(walletFrame, &WalletFrame::message, [this](const QString& title, const QString& message, unsigned int style) {
this->message(title, message, style);
});
connect(walletFrame, &WalletFrame::createWalletButtonClicked, [this] {
auto activity = new CreateWalletActivity(getWalletController(), this);
connect(activity, &CreateWalletActivity::finished, activity, &QObject::deleteLater);
activity->create();
});
} else
#endif // ENABLE_WALLET
{
@ -211,8 +217,6 @@ BitcoinGUI::BitcoinGUI(interfaces::Node& node, const NetworkStyle* networkStyle,
// Subscribe to notifications from core
subscribeToCoreSignals();
// Jump to peers tab by clicking on connections icon
connect(labelConnectionsIcon, &GUIUtil::ClickableLabel::clicked, this, &BitcoinGUI::showPeers);
connect(labelProxyIcon, &GUIUtil::ClickableLabel::clicked, [this] {
openOptionsDialogWithTab(OptionsDialog::TAB_NETWORK);
});
@ -220,11 +224,6 @@ BitcoinGUI::BitcoinGUI(interfaces::Node& node, const NetworkStyle* networkStyle,
modalOverlay = new ModalOverlay(enableWallet, this->centralWidget());
connect(labelBlocksIcon, &GUIUtil::ClickableLabel::clicked, this, &BitcoinGUI::showModalOverlay);
connect(progressBar, &GUIUtil::ClickableProgressBar::clicked, this, &BitcoinGUI::showModalOverlay);
#ifdef ENABLE_WALLET
if(enableWallet) {
connect(walletFrame, &WalletFrame::requestedSyncWarningInfo, this, &BitcoinGUI::showModalOverlay);
}
#endif
#ifdef Q_OS_MAC
m_app_nap_inhibitor = new CAppNapInhibitor;
@ -812,8 +811,11 @@ void BitcoinGUI::setClientModel(ClientModel *_clientModel, interfaces::BlockAndH
}
// Keep up to date with client
updateNetworkState();
setNetworkActive(m_node.getNetworkActive());
setNumConnections(_clientModel->getNumConnections());
connect(labelConnectionsIcon, &GUIUtil::ClickableLabel::clicked, [this] {
GUIUtil::PopupMenu(m_network_context_menu, QCursor::pos());
});
connect(_clientModel, &ClientModel::numConnectionsChanged, this, &BitcoinGUI::setNumConnections);
connect(_clientModel, &ClientModel::networkActiveChanged, this, &BitcoinGUI::setNetworkActive);
@ -911,13 +913,28 @@ WalletController* BitcoinGUI::getWalletController()
void BitcoinGUI::addWallet(WalletModel* walletModel)
{
if (!walletFrame) return;
if (!walletFrame->addWallet(walletModel)) return;
WalletView* wallet_view = new WalletView(walletFrame);
if (!walletFrame->addWallet(walletModel, wallet_view)) return;
rpcConsole->addWallet(walletModel);
if (m_wallet_selector->count() == 0) {
setWalletActionsEnabled(true);
} else if (m_wallet_selector->count() == 1) {
m_wallet_selector_action->setVisible(true);
}
connect(wallet_view, &WalletView::outOfSyncWarningClicked, this, &BitcoinGUI::showModalOverlay);
connect(wallet_view, &WalletView::transactionClicked, this, &BitcoinGUI::gotoHistoryPage);
connect(wallet_view, &WalletView::coinsSent, this, &BitcoinGUI::gotoHistoryPage);
connect(wallet_view, &WalletView::message, [this](const QString& title, const QString& message, unsigned int style) {
this->message(title, message, style);
});
connect(wallet_view, &WalletView::encryptionStatusChanged, this, &BitcoinGUI::updateWalletStatus);
connect(wallet_view, &WalletView::incomingTransaction, this, &BitcoinGUI::incomingTransaction);
connect(wallet_view, &WalletView::hdEnabledStatusChanged, this, &BitcoinGUI::updateWalletStatus);
connect(this, &BitcoinGUI::setPrivacy, wallet_view, &WalletView::setPrivacy);
wallet_view->setPrivacy(isPrivacyModeActivated());
const QString display_name = walletModel->getDisplayName();
m_wallet_selector->addItem(display_name, QVariant::fromValue(walletModel));
}
@ -1270,14 +1287,21 @@ void BitcoinGUI::updateNetworkState()
nCountPrev = count;
fNetworkActivePrev = fNetworkActive;
QString tooltip;
if (fNetworkActive) {
labelConnectionsIcon->setToolTip(tr("%n active connection(s) to Dash network", "", count));
//: A substring of the tooltip.
tooltip = tr("%n active connection(s) to Dash network", "", count);
} else {
labelConnectionsIcon->setToolTip(tr("Network activity disabled"));
tooltip = tr("Network activity disabled");
icon = "connect_4";
color = GUIUtil::ThemedColor::RED;
}
// Don't word-wrap this (fixed-width) tooltip
tooltip = QLatin1String("<nobr>") + tooltip + QLatin1String("<br>") +
//: A substring of the tooltip. "More actions" are available via the context menu.
tr("Click for more actions.") + QLatin1String("</nobr>");
if (fNetworkActive && count == 0) {
startConnectingAnimation();
}
@ -1285,6 +1309,7 @@ void BitcoinGUI::updateNetworkState()
stopConnectingAnimation();
labelConnectionsIcon->setPixmap(GUIUtil::getIcon(icon, color).pixmap(STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE));
}
labelConnectionsIcon->setToolTip(tooltip);
}
void BitcoinGUI::setNumConnections(int count)
@ -1292,9 +1317,24 @@ void BitcoinGUI::setNumConnections(int count)
updateNetworkState();
}
void BitcoinGUI::setNetworkActive(bool networkActive)
void BitcoinGUI::setNetworkActive(bool network_active)
{
updateNetworkState();
m_network_context_menu->clear();
m_network_context_menu->addAction(
//: A context menu item. The "Peers tab" is an element of the "Node window".
tr("Show Peers tab"),
[this] {
rpcConsole->setTabFocus(RPCConsole::TabTypes::PEERS);
showDebugWindow();
});
m_network_context_menu->addAction(
network_active ?
//: A context menu item.
tr("Disable network activity") :
//: A context menu item. The network activity was disabled previously.
tr("Enable network activity"),
[this, new_state = !network_active] { m_node.setNetworkActive(new_state); });
}
void BitcoinGUI::updateHeadersSyncProgressLabel()

View File

@ -16,6 +16,7 @@
#include <QLabel>
#include <QMainWindow>
#include <QMap>
#include <QMenu>
#include <QPoint>
#include <QPushButton>
#include <QSystemTrayIcon>
@ -51,7 +52,6 @@ class QAction;
class QButtonGroup;
class QComboBox;
class QDateTime;
class QMenu;
class QProgressBar;
class QProgressDialog;
class QToolButton;
@ -190,6 +190,8 @@ private:
ModalOverlay* modalOverlay = nullptr;
QButtonGroup* tabGroup = nullptr;
QMenu* m_network_context_menu = new QMenu(this);
#ifdef Q_OS_MAC
CAppNapInhibitor* m_app_nap_inhibitor = nullptr;
#endif
@ -264,7 +266,7 @@ public Q_SLOTS:
/** Set number of connections shown in the UI */
void setNumConnections(int count);
/** Set network state shown in the UI */
void setNetworkActive(bool networkActive);
void setNetworkActive(bool network_active);
/** Get restart command-line parameters and request restart */
void handleRestart(QStringList args);
/** Set number of blocks and last block date shown in the UI */

View File

@ -69,7 +69,6 @@
#include <QShortcut>
#include <QSize>
#include <QString>
#include <QStringBuilder>
#include <QTextDocument> // for Qt::mightBeRichText
#include <QThread>
#include <QTimer>
@ -1931,7 +1930,7 @@ QString MakeHtmlLink(const QString& source, const QString& link)
{
return QString(source).replace(
link,
QLatin1String("<a href=\"") % link % QLatin1String("\">") % link % QLatin1String("</a>"));
QLatin1String("<a href=\"") + link + QLatin1String("\">") + link + QLatin1String("</a>"));
}
void PrintSlotException(

View File

@ -26,6 +26,7 @@
#endif
#include <QDebug>
#include <QLatin1Char>
#include <QSettings>
#include <QStringList>
@ -376,7 +377,7 @@ static ProxySetting GetProxySetting(QSettings &settings, const QString &name)
static void SetProxySetting(QSettings &settings, const QString &name, const ProxySetting &ip_port)
{
settings.setValue(name, ip_port.ip + ":" + ip_port.port);
settings.setValue(name, QString{ip_port.ip + QLatin1Char(':') + ip_port.port});
}
static const QString GetDefaultProxyAddress()

View File

@ -199,11 +199,6 @@ void OverviewPage::handleTransactionClicked(const QModelIndex &index)
Q_EMIT transactionClicked(filter->mapToSource(index));
}
void OverviewPage::handleOutOfSyncWarningClicks()
{
Q_EMIT outOfSyncWarningClicked();
}
void OverviewPage::setPrivacy(bool privacy)
{
m_privacy = privacy;
@ -469,7 +464,7 @@ void OverviewPage::updateCoinJoinProgress()
ui->coinJoinProgress->setValue(progress);
QString strToolPip = ("<b>" + tr("Overall progress") + ": %1%</b><br/>" +
QString strToolPip = QString("<b>" + tr("Overall progress") + ": %1%</b><br/>" +
tr("Denominated") + ": %2%<br/>" +
tr("Partially mixed") + ": %3%<br/>" +
tr("Mixed") + ": %4%<br/>" +

View File

@ -70,7 +70,6 @@ private Q_SLOTS:
void handleTransactionClicked(const QModelIndex &index);
void updateAlerts(const QString &warnings);
void updateWatchOnlyLabels(bool showWatchOnly);
void handleOutOfSyncWarningClicks();
};
#endif // BITCOIN_QT_OVERVIEWPAGE_H

View File

@ -14,53 +14,11 @@
#include <QList>
#include <QTimer>
// private implementation
class PeerTablePriv
{
public:
/** Local cache of peer information */
QList<CNodeCombinedStats> cachedNodeStats;
/** Pull a full list of peers from vNodes into our cache */
void refreshPeers(interfaces::Node& node)
{
cachedNodeStats.clear();
interfaces::Node::NodesStats nodes_stats;
node.getNodesStats(nodes_stats);
cachedNodeStats.reserve(nodes_stats.size());
for (const auto& node_stats : nodes_stats)
{
CNodeCombinedStats stats;
stats.nodeStats = std::get<0>(node_stats);
stats.fNodeStateStatsAvailable = std::get<1>(node_stats);
stats.nodeStateStats = std::get<2>(node_stats);
cachedNodeStats.append(stats);
}
}
int size() const
{
return cachedNodeStats.size();
}
CNodeCombinedStats *index(int idx)
{
if (idx >= 0 && idx < cachedNodeStats.size())
return &cachedNodeStats[idx];
return nullptr;
}
};
PeerTableModel::PeerTableModel(interfaces::Node& node, QObject* parent) :
QAbstractTableModel(parent),
m_node(node),
timer(nullptr)
{
priv.reset(new PeerTablePriv());
// set up timer for auto refresh
timer = new QTimer(this);
connect(timer, &QTimer::timeout, this, &PeerTableModel::refresh);
@ -85,15 +43,15 @@ void PeerTableModel::stopAutoRefresh()
timer->stop();
}
int PeerTableModel::rowCount(const QModelIndex &parent) const
int PeerTableModel::rowCount(const QModelIndex& parent) const
{
if (parent.isValid()) {
return 0;
}
return priv->size();
return m_peers_data.size();
}
int PeerTableModel::columnCount(const QModelIndex &parent) const
int PeerTableModel::columnCount(const QModelIndex& parent) const
{
if (parent.isValid()) {
return 0;
@ -101,7 +59,7 @@ int PeerTableModel::columnCount(const QModelIndex &parent) const
return columns.length();
}
QVariant PeerTableModel::data(const QModelIndex &index, int role) const
QVariant PeerTableModel::data(const QModelIndex& index, int role) const
{
if(!index.isValid())
return QVariant();
@ -115,7 +73,7 @@ QVariant PeerTableModel::data(const QModelIndex &index, int role) const
return (qint64)rec->nodeStats.nodeid;
case Address:
// prepend to peer address down-arrow symbol for inbound connection and up-arrow for outbound connection
return QString(rec->nodeStats.fInbound ? "" : "") + QString::fromStdString(rec->nodeStats.m_addr_name);
return QString::fromStdString((rec->nodeStats.fInbound ? "" : "") + rec->nodeStats.m_addr_name);
case ConnectionType:
return GUIUtil::ConnectionTypeToQString(rec->nodeStats.m_conn_type, /* prepend_direction */ false);
case Network:
@ -174,19 +132,52 @@ Qt::ItemFlags PeerTableModel::flags(const QModelIndex &index) const
return retval;
}
QModelIndex PeerTableModel::index(int row, int column, const QModelIndex &parent) const
QModelIndex PeerTableModel::index(int row, int column, const QModelIndex& parent) const
{
Q_UNUSED(parent);
CNodeCombinedStats *data = priv->index(row);
if (data)
return createIndex(row, column, data);
if (0 <= row && row < rowCount() && 0 <= column && column < columnCount()) {
return createIndex(row, column, const_cast<CNodeCombinedStats*>(&m_peers_data[row]));
}
return QModelIndex();
}
void PeerTableModel::refresh()
{
Q_EMIT layoutAboutToBeChanged();
priv->refreshPeers(m_node);
Q_EMIT layoutChanged();
interfaces::Node::NodesStats nodes_stats;
m_node.getNodesStats(nodes_stats);
decltype(m_peers_data) new_peers_data;
new_peers_data.reserve(nodes_stats.size());
for (const auto& node_stats : nodes_stats) {
const CNodeCombinedStats stats{std::get<0>(node_stats), std::get<2>(node_stats), std::get<1>(node_stats)};
new_peers_data.append(stats);
}
// Handle peer addition or removal as suggested in Qt Docs. See:
// - https://doc.qt.io/qt-5/model-view-programming.html#inserting-and-removing-rows
// - https://doc.qt.io/qt-5/model-view-programming.html#resizable-models
// We take advantage of the fact that the std::vector returned
// by interfaces::Node::getNodesStats is sorted by nodeid.
for (int i = 0; i < m_peers_data.size();) {
if (i < new_peers_data.size() && m_peers_data.at(i).nodeStats.nodeid == new_peers_data.at(i).nodeStats.nodeid) {
++i;
continue;
}
// A peer has been removed from the table.
beginRemoveRows(QModelIndex(), i, i);
m_peers_data.erase(m_peers_data.begin() + i);
endRemoveRows();
}
if (m_peers_data.size() < new_peers_data.size()) {
// Some peers have been added to the end of the table.
beginInsertRows(QModelIndex(), m_peers_data.size(), new_peers_data.size() - 1);
m_peers_data.swap(new_peers_data);
endInsertRows();
} else {
m_peers_data.swap(new_peers_data);
}
Q_EMIT changed();
}

View File

@ -8,10 +8,11 @@
#include <net_processing.h> // For CNodeStateStats
#include <net.h>
#include <memory>
#include <QAbstractTableModel>
#include <QList>
#include <QModelIndex>
#include <QStringList>
#include <QVariant>
class PeerTablePriv;
@ -61,18 +62,23 @@ public:
/** @name Methods overridden from QAbstractTableModel
@{*/
int rowCount(const QModelIndex &parent) const override;
int columnCount(const QModelIndex &parent) const override;
QVariant data(const QModelIndex &index, int role) const override;
QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
QModelIndex index(int row, int column, const QModelIndex &parent) const override;
int rowCount(const QModelIndex& parent = QModelIndex()) const override;
int columnCount(const QModelIndex& parent = QModelIndex()) const override;
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const override;
Qt::ItemFlags flags(const QModelIndex &index) const override;
/*@}*/
public Q_SLOTS:
void refresh();
Q_SIGNALS:
void changed();
private:
//! Internal peer data structure.
QList<CNodeCombinedStats> m_peers_data{};
interfaces::Node& m_node;
const QStringList columns{
/*: Title of Peers Table column which contains a
@ -99,7 +105,6 @@ private:
/*: Title of Peers Table column which contains the peer's
User Agent string. */
tr("User Agent")};
std::unique_ptr<PeerTablePriv> priv;
QTimer *timer;
};

View File

@ -17,6 +17,9 @@
#include <utility>
#include <QLatin1Char>
#include <QLatin1String>
RecentRequestsTableModel::RecentRequestsTableModel(WalletModel *parent) :
QAbstractTableModel(parent), walletModel(parent)
{
@ -126,7 +129,11 @@ void RecentRequestsTableModel::updateAmountColumnTitle()
/** Gets title for amount column including current display unit if optionsModel reference available. */
QString RecentRequestsTableModel::getAmountTitle()
{
return (this->walletModel->getOptionsModel() != nullptr) ? tr("Requested") + " ("+BitcoinUnits::name(this->walletModel->getOptionsModel()->getDisplayUnit()) + ")" : "";
if (!walletModel->getOptionsModel()) return {};
return tr("Requested") +
QLatin1String(" (") +
BitcoinUnits::name(this->walletModel->getOptionsModel()->getDisplayUnit()) +
QLatin1Char(')');
}
QModelIndex RecentRequestsTableModel::index(int row, int column, const QModelIndex &parent) const

View File

@ -37,6 +37,7 @@
#include <wallet/walletutil.h>
#endif
#include <QAbstractButton>
#include <QButtonGroup>
#include <QDir>
#include <QFont>
@ -504,6 +505,9 @@ RPCConsole::RPCConsole(interfaces::Node& node, QWidget* parent, Qt::WindowFlags
ui->splitter->restoreState(settings.value("RPCConsoleWidgetPeersTabSplitterSizes").toByteArray());
}
m_peer_widget_header_state = settings.value("PeersTabPeerHeaderState").toByteArray();
m_banlist_widget_header_state = settings.value("PeersTabBanlistHeaderState").toByteArray();
constexpr QChar nonbreaking_hyphen(8209);
const std::vector<QString> CONNECTION_TYPE_DOC{
//: Explanatory text for an inbound peer connection.
@ -547,9 +551,9 @@ RPCConsole::RPCConsole(interfaces::Node& node, QWidget* parent, Qt::WindowFlags
ui->lineEdit->setMaxLength(16 * 1024 * 1024);
ui->messagesWidget->installEventFilter(this);
connect(ui->clearButton, &QPushButton::clicked, [this] { clear(); });
connect(ui->fontBiggerButton, &QPushButton::clicked, this, &RPCConsole::fontBigger);
connect(ui->fontSmallerButton, &QPushButton::clicked, this, &RPCConsole::fontSmaller);
connect(ui->clearButton, &QAbstractButton::clicked, [this] { clear(); });
connect(ui->fontBiggerButton, &QAbstractButton::clicked, this, &RPCConsole::fontBigger);
connect(ui->fontSmallerButton, &QAbstractButton::clicked, this, &RPCConsole::fontSmaller);
connect(ui->btnClearTrafficGraph, &QPushButton::clicked, ui->trafficGraph, &TrafficGraphWidget::clear);
// disable the wallet selector by default
@ -609,6 +613,9 @@ RPCConsole::~RPCConsole()
settings.setValue("RPCConsoleWidgetPeersTabSplitterSizes", ui->splitter->saveState());
}
settings.setValue("PeersTabPeerHeaderState", m_peer_widget_header_state);
settings.setValue("PeersTabBanlistHeaderState", m_banlist_widget_header_state);
m_node.rpcUnsetTimerInterface(rpcTimerInterface);
delete rpcTimerInterface;
delete pageButtons;
@ -700,9 +707,12 @@ void RPCConsole::setClientModel(ClientModel *model, int bestblock_height, int64_
ui->peerWidget->setSelectionBehavior(QAbstractItemView::SelectRows);
ui->peerWidget->setSelectionMode(QAbstractItemView::ExtendedSelection);
ui->peerWidget->setContextMenuPolicy(Qt::CustomContextMenu);
if (!ui->peerWidget->horizontalHeader()->restoreState(m_peer_widget_header_state)) {
ui->peerWidget->setColumnWidth(PeerTableModel::Address, ADDRESS_COLUMN_WIDTH);
ui->peerWidget->setColumnWidth(PeerTableModel::Subversion, SUBVERSION_COLUMN_WIDTH);
ui->peerWidget->setColumnWidth(PeerTableModel::Ping, PING_COLUMN_WIDTH);
}
ui->peerWidget->horizontalHeader()->setStretchLastSection(true);
ui->peerWidget->setItemDelegateForColumn(PeerTableModel::NetNodeId, new PeerIdViewDelegate(this));
@ -717,7 +727,7 @@ void RPCConsole::setClientModel(ClientModel *model, int bestblock_height, int64_
// peer table signal handling - update peer details when selecting new node
connect(ui->peerWidget->selectionModel(), &QItemSelectionModel::selectionChanged, this, &RPCConsole::updateDetailWidget);
connect(model->getPeerTableModel(), &PeerTableModel::layoutChanged, this, &RPCConsole::updateDetailWidget);
connect(model->getPeerTableModel(), &PeerTableModel::changed, this, &RPCConsole::updateDetailWidget);
// set up ban table
ui->banlistWidget->setModel(model->getBanTableModel());
@ -725,8 +735,11 @@ void RPCConsole::setClientModel(ClientModel *model, int bestblock_height, int64_
ui->banlistWidget->setSelectionBehavior(QAbstractItemView::SelectRows);
ui->banlistWidget->setSelectionMode(QAbstractItemView::SingleSelection);
ui->banlistWidget->setContextMenuPolicy(Qt::CustomContextMenu);
if (!ui->banlistWidget->horizontalHeader()->restoreState(m_banlist_widget_header_state)) {
ui->banlistWidget->setColumnWidth(BanTableModel::Address, BANSUBNET_COLUMN_WIDTH);
ui->banlistWidget->setColumnWidth(BanTableModel::Bantime, BANTIME_COLUMN_WIDTH);
}
ui->banlistWidget->horizontalHeader()->setStretchLastSection(true);
// create ban table context menu
@ -936,23 +949,29 @@ void RPCConsole::clear(bool keep_prompt)
).arg(consoleFontSize)
);
message(CMD_REPLY,
tr("Welcome to the %1 RPC console.").arg(PACKAGE_NAME) +
"<br>" +
tr("Use up and down arrows to navigate history, and %1 to clear screen.")
.arg("<b>" + ui->clearButton->shortcut().toString(QKeySequence::NativeText) + "</b>") +
"<br>" +
tr("Use %1 and %2 to increase or decrease the font size.")
.arg("<b>" + ui->fontBiggerButton->shortcut().toString(QKeySequence::NativeText) + "</b>")
.arg("<b>" + ui->fontSmallerButton->shortcut().toString(QKeySequence::NativeText) + "</b>") +
"<br>" +
tr("Type %1 for an overview of available commands.").arg("<b>help</b>") +
"<br>" +
tr("For more information on using this console type %1.").arg("<b>help-console</b>") +
"<br><span class=\"secwarning\"><br>" +
tr("WARNING: Scammers have been active, telling users to type commands here, stealing their wallet contents. Do not use this console without fully understanding the ramifications of a command.") +
"</span>",
true);
static const QString welcome_message =
/*: RPC console welcome message.
Placeholders %7 and %8 are style tags for the warning content, and
they are not space separated from the rest of the text intentionally. */
tr("Welcome to the %1 RPC console.\n"
"Use up and down arrows to navigate history, and %2 to clear screen.\n"
"Use %3 and %4 to increase or decrease the font size.\n"
"Type %5 for an overview of available commands.\n"
"For more information on using this console, type %6.\n"
"\n"
"%7WARNING: Scammers have been active, telling users to type"
" commands here, stealing their wallet contents. Do not use this console"
" without fully understanding the ramifications of a command.%8")
.arg(PACKAGE_NAME,
"<b>" + ui->clearButton->shortcut().toString(QKeySequence::NativeText) + "</b>",
"<b>" + ui->fontBiggerButton->shortcut().toString(QKeySequence::NativeText) + "</b>",
"<b>" + ui->fontSmallerButton->shortcut().toString(QKeySequence::NativeText) + "</b>",
"<b>help</b>",
"<b>help-console</b>",
"<span class=\"secwarning\">",
"<span>");
message(CMD_REPLY, welcome_message, true);
}
void RPCConsole::keyPressEvent(QKeyEvent *event)
@ -1074,10 +1093,12 @@ void RPCConsole::showPage(int index)
void RPCConsole::on_lineEdit_returnPressed()
{
QString cmd = ui->lineEdit->text();
QString cmd = ui->lineEdit->text().trimmed();
if (cmd.isEmpty()) {
return;
}
if(!cmd.isEmpty())
{
std::string strFilteredCmd;
try {
std::string dummy;
@ -1090,9 +1111,18 @@ void RPCConsole::on_lineEdit_returnPressed()
return;
}
ui->lineEdit->clear();
// A special case allows to request shutdown even a long-running command is executed.
if (cmd == QLatin1String("stop")) {
std::string dummy;
RPCExecuteCommandLine(m_node, dummy, cmd.toStdString());
return;
}
cmdBeforeBrowsing = QString();
if (m_is_executing) {
return;
}
ui->lineEdit->clear();
#ifdef ENABLE_WALLET
WalletModel* wallet_model = ui->WalletSelector->currentData().value<WalletModel*>();
@ -1105,9 +1135,12 @@ void RPCConsole::on_lineEdit_returnPressed()
}
m_last_wallet_model = wallet_model;
}
#endif
#endif // ENABLE_WALLET
message(CMD_REQUEST, QString::fromStdString(strFilteredCmd));
//: A console message indicating an entered command is currently being executed.
message(CMD_REPLY, tr("Executing…"));
m_is_executing = true;
Q_EMIT cmdRequest(cmd, m_last_wallet_model);
cmd = QString::fromStdString(strFilteredCmd);
@ -1117,14 +1150,14 @@ void RPCConsole::on_lineEdit_returnPressed()
// Append command to history
history.append(cmd);
// Enforce maximum history size
while(history.size() > CONSOLE_HISTORY)
while (history.size() > CONSOLE_HISTORY) {
history.removeFirst();
}
// Set pointer to end of history
historyPtr = history.size();
// Scroll console view to end
scrollToEnd();
}
}
void RPCConsole::browseHistory(int offset)
@ -1154,7 +1187,13 @@ void RPCConsole::startExecutor()
executor->moveToThread(&thread);
// Replies from executor object must go to this object
connect(executor, &RPCExecutor::reply, this, qOverload<int, const QString&>(&RPCConsole::message));
connect(executor, &RPCExecutor::reply, this, [this](int category, const QString& command) {
// Remove "Executing…" message.
ui->messagesWidget->undo();
message(category, command);
scrollToEnd();
m_is_executing = false;
});
// Requests from this object must go to executor
connect(this, &RPCConsole::cmdRequest, executor, &RPCExecutor::request);
@ -1330,6 +1369,11 @@ void RPCConsole::showEvent(QShowEvent *event)
void RPCConsole::hideEvent(QHideEvent *event)
{
// It is too late to call QHeaderView::saveState() in ~RPCConsole(), as all of
// the columns of QTableView child widgets will have zero width at that moment.
m_peer_widget_header_state = ui->peerWidget->horizontalHeader()->saveState();
m_banlist_widget_header_state = ui->banlistWidget->horizontalHeader()->saveState();
QWidget::hideEvent(event);
if (!clientModel || !clientModel->getPeerTableModel())

View File

@ -12,9 +12,10 @@
#include <net.h>
#include <uint256.h>
#include <QWidget>
#include <QByteArray>
#include <QCompleter>
#include <QThread>
#include <QWidget>
class ClientModel;
class RPCTimerInterface;
@ -189,6 +190,9 @@ private:
QCompleter *autoCompleter = nullptr;
QThread thread;
WalletModel* m_last_wallet_model{nullptr};
bool m_is_executing{false};
QByteArray m_peer_widget_header_state;
QByteArray m_banlist_widget_header_state;
/** Update UI with latest network info from model. */
void updateNetworkState();

View File

@ -41,7 +41,7 @@ void TestRpcCommand(RPCConsole* console)
QTest::keyClicks(lineEdit, "getblockchaininfo");
QTest::keyClick(lineEdit, Qt::Key_Return);
QVERIFY(mw_spy.wait(1000));
QCOMPARE(mw_spy.count(), 2);
QCOMPARE(mw_spy.count(), 4);
QString output = messagesWidget->toPlainText();
UniValue value;
value.read(output.right(output.size() - output.indexOf("{")).toStdString());

View File

@ -201,7 +201,7 @@ void TestGUI(interfaces::Node& node)
QCOMPARE(uri.count("amount=0.00000001"), 2);
QCOMPARE(receiveRequestDialog->QObject::findChild<QLabel*>("amount_tag")->text(), QString("Amount:"));
QCOMPARE(receiveRequestDialog->QObject::findChild<QLabel*>("amount_content")->text(), QString("0.00000001 ") + BitcoinUnits::name(unit));
QCOMPARE(receiveRequestDialog->QObject::findChild<QLabel*>("amount_content")->text(), QString::fromStdString("0.00000001 ") + BitcoinUnits::name(unit));
QCOMPARE(uri.count("label=TEST_LABEL_1"), 2);
QCOMPARE(receiveRequestDialog->QObject::findChild<QLabel*>("label_tag")->text(), QString("Label:"));

View File

@ -26,6 +26,8 @@
#include <stdint.h>
#include <string>
#include <QLatin1String>
QString TransactionDesc::FormatTxStatus(const interfaces::WalletTx& wtx, const interfaces::WalletTxStatus& status, bool inMempool, int numBlocks)
{
if (!status.is_final)
@ -44,19 +46,20 @@ QString TransactionDesc::FormatTxStatus(const interfaces::WalletTx& wtx, const i
bool fChainLocked = status.is_chainlocked;
if (nDepth == 0) {
strTxStatus = tr("0/unconfirmed, %1").arg((inMempool ? tr("in memory pool") : tr("not in memory pool"))) + (status.is_abandoned ? ", "+tr("abandoned") : "");
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 += ", " + tr("locked via ChainLocks");
strTxStatus += QLatin1String(", ") + tr("locked via ChainLocks");
return strTxStatus;
}
}
if (status.is_islocked) {
strTxStatus += ", " + tr("verified via InstantSend");
strTxStatus += QLatin1String(", ") + tr("verified via InstantSend");
}
return strTxStatus;

View File

@ -23,6 +23,8 @@
#include <QDateTime>
#include <QDebug>
#include <QIcon>
#include <QLatin1Char>
#include <QLatin1String>
#include <QList>
#include <QMessageBox>
@ -466,9 +468,9 @@ QVariant TransactionTableModel::txAddressDecoration(const TransactionRecord *wtx
QString TransactionTableModel::formatTxToAddress(const TransactionRecord *wtx, bool tooltip) const
{
QString watchAddress;
if (tooltip) {
if (tooltip && wtx->involvesWatchAddress) {
// Mark transactions involving watch-only addresses by adding " (watch-only)"
watchAddress = wtx->involvesWatchAddress ? QString(" (") + tr("watch-only") + QString(")") : "";
watchAddress = QLatin1String(" (") + tr("watch-only") + QLatin1Char(')');
}
switch(wtx->type)

View File

@ -7,14 +7,11 @@
#include <fs.h>
#include <node/ui_interface.h>
#include <psbt.h>
#include <qt/bitcoingui.h>
#include <qt/createwalletdialog.h>
#include <qt/governancelist.h>
#include <qt/guiutil.h>
#include <qt/masternodelist.h>
#include <qt/overviewpage.h>
#include <qt/psbtoperationsdialog.h>
#include <qt/walletcontroller.h>
#include <qt/walletmodel.h>
#include <qt/walletview.h>
#include <util/system.h>
@ -30,9 +27,8 @@
#include <QPushButton>
#include <QVBoxLayout>
WalletFrame::WalletFrame(BitcoinGUI* _gui)
: QFrame(_gui),
gui(_gui),
WalletFrame::WalletFrame(QWidget* parent)
: QFrame(parent),
m_size_hint(OverviewPage{nullptr}.sizeHint())
{
// Leave HBox hook for adding a list view later
@ -53,11 +49,7 @@ WalletFrame::WalletFrame(BitcoinGUI* _gui)
// A button for create wallet dialog
QPushButton* create_wallet_button = new QPushButton(tr("Create a new wallet"), walletStack);
connect(create_wallet_button, &QPushButton::clicked, [this] {
auto activity = new CreateWalletActivity(gui->getWalletController(), this);
connect(activity, &CreateWalletActivity::finished, activity, &QObject::deleteLater);
activity->create();
});
connect(create_wallet_button, &QPushButton::clicked, this, &WalletFrame::createWalletButtonClicked);
no_wallet_layout->addWidget(create_wallet_button, 0, Qt::AlignHCenter | Qt::AlignTop);
no_wallet_group->setLayout(no_wallet_layout);
@ -86,17 +78,15 @@ void WalletFrame::setClientModel(ClientModel *_clientModel)
}
}
bool WalletFrame::addWallet(WalletModel *walletModel)
bool WalletFrame::addWallet(WalletModel* walletModel, WalletView* walletView)
{
if (!gui || !clientModel || !walletModel) return false;
if (!clientModel || !walletModel) return false;
if (mapWalletViews.count(walletModel) > 0) return false;
WalletView* walletView = new WalletView(this);
walletView->setClientModel(clientModel);
walletView->setWalletModel(walletModel);
walletView->showOutOfSyncWarning(bOutOfSync);
walletView->setPrivacy(gui->isPrivacyModeActivated());
WalletView* current_wallet_view = currentWalletView();
if (current_wallet_view) {
@ -108,17 +98,6 @@ bool WalletFrame::addWallet(WalletModel *walletModel)
walletStack->addWidget(walletView);
mapWalletViews[walletModel] = walletView;
connect(walletView, &WalletView::outOfSyncWarningClicked, this, &WalletFrame::outOfSyncWarningClicked);
connect(walletView, &WalletView::transactionClicked, gui, &BitcoinGUI::gotoHistoryPage);
connect(walletView, &WalletView::coinsSent, gui, &BitcoinGUI::gotoHistoryPage);
connect(walletView, &WalletView::message, [this](const QString& title, const QString& message, unsigned int style) {
gui->message(title, message, style);
});
connect(walletView, &WalletView::encryptionStatusChanged, gui, &BitcoinGUI::updateWalletStatus);
connect(walletView, &WalletView::incomingTransaction, gui, &BitcoinGUI::incomingTransaction);
connect(walletView, &WalletView::hdEnabledStatusChanged, gui, &BitcoinGUI::updateWalletStatus);
connect(gui, &BitcoinGUI::setPrivacy, walletView, &WalletView::setPrivacy);
return true;
}
@ -364,8 +343,3 @@ WalletModel* WalletFrame::currentWalletModel() const
WalletView* wallet_view = currentWalletView();
return wallet_view ? wallet_view->getWalletModel() : nullptr;
}
void WalletFrame::outOfSyncWarningClicked()
{
Q_EMIT requestedSyncWarningInfo();
}

View File

@ -9,7 +9,6 @@
#include <QGroupBox>
#include <QMap>
class BitcoinGUI;
class ClientModel;
class SendCoinsRecipient;
class WalletModel;
@ -33,12 +32,12 @@ class WalletFrame : public QFrame
Q_OBJECT
public:
explicit WalletFrame(BitcoinGUI* _gui = nullptr);
explicit WalletFrame(QWidget* parent);
~WalletFrame();
void setClientModel(ClientModel *clientModel);
bool addWallet(WalletModel *walletModel);
bool addWallet(WalletModel* walletModel, WalletView* walletView);
void setCurrentWallet(WalletModel* wallet_model);
void removeWallet(WalletModel* wallet_model);
void removeAllWallets();
@ -51,12 +50,11 @@ public:
Q_SIGNALS:
void message(const QString& title, const QString& message, unsigned int style);
/** Notify that the user has requested more information about the out-of-sync warning */
void requestedSyncWarningInfo();
void createWalletButtonClicked();
private:
QStackedWidget *walletStack;
BitcoinGUI *gui;
ClientModel *clientModel;
QMap<WalletModel*, WalletView*> mapWalletViews;
QGroupBox* no_wallet_group;
@ -110,8 +108,6 @@ public Q_SLOTS:
void usedSendingAddresses();
/** Show used receiving addresses */
void usedReceivingAddresses();
/** Pass on signal over requested out-of-sync-warning information */
void outOfSyncWarningClicked();
};
#endif // BITCOIN_QT_WALLETFRAME_H

View File

@ -100,7 +100,7 @@ WalletView::WalletView(QWidget* parent) :
connect(overviewPage, &OverviewPage::transactionClicked, this, &WalletView::transactionClicked);
// Clicking on a transaction on the overview pre-selects the transaction on the transaction history page
connect(overviewPage, &OverviewPage::transactionClicked, transactionView, qOverload<const QModelIndex&>(&TransactionView::focusTransaction));
connect(overviewPage, &OverviewPage::outOfSyncWarningClicked, this, &WalletView::requestedSyncWarningInfo);
connect(overviewPage, &OverviewPage::outOfSyncWarningClicked, this, &WalletView::outOfSyncWarningClicked);
connect(sendCoinsPage, &SendCoinsDialog::coinsSent, this, &WalletView::coinsSent);
connect(coinJoinCoinsPage, &SendCoinsDialog::coinsSent, this, &WalletView::coinsSent);
@ -408,11 +408,6 @@ void WalletView::showProgress(const QString &title, int nProgress)
}
}
void WalletView::requestedSyncWarningInfo()
{
Q_EMIT outOfSyncWarningClicked();
}
/** Update wallet with the sum of the selected transactions */
void WalletView::trxAmount(QString amount)
{

View File

@ -122,10 +122,6 @@ public Q_SLOTS:
/** Show progress dialog e.g. for rescan */
void showProgress(const QString &title, int nProgress);
/** User has requested more information about the out of sync state */
void requestedSyncWarningInfo();
/** Update selected DASH amount from transactionview */
void trxAmount(QString amount);
Q_SIGNALS:

View File

@ -115,7 +115,7 @@ bool LoadWallets(interfaces::Chain& chain, interfaces::CoinJoin::Loader& coinjoi
if (!database && status == DatabaseStatus::FAILED_NOT_FOUND) {
continue;
}
chain.initMessage(_("Loading wallet...").translated);
chain.initMessage(_("Loading wallet").translated);
std::shared_ptr<CWallet> pwallet = database ? CWallet::Create(&chain, &coinjoin_loader, name, std::move(database), options.create_flags, error_string, warnings) : nullptr;
if (!warnings.empty()) chain.initWarning(Join(warnings, Untranslated("\n")));
if (!pwallet) {

View File

@ -238,7 +238,7 @@ std::shared_ptr<CWallet> LoadWalletInternal(interfaces::Chain& chain, interfaces
return nullptr;
}
chain.initMessage(_("Loading wallet...").translated);
chain.initMessage(_("Loading wallet").translated);
std::shared_ptr<CWallet> wallet = CWallet::Create(&chain, &coinjoin_loader, name, std::move(database), options.create_flags, error, warnings);
if (!wallet) {
error = Untranslated("Wallet loading failed.") + Untranslated(" ") + error;
@ -304,7 +304,7 @@ std::shared_ptr<CWallet> CreateWallet(interfaces::Chain& chain, interfaces::Coin
}
// Make the wallet
chain.initMessage(_("Loading wallet...").translated);
chain.initMessage(_("Loading wallet").translated);
std::shared_ptr<CWallet> wallet = CWallet::Create(&chain, &coinjoin_loader, name, std::move(database), wallet_creation_flags, error, warnings);
if (!wallet) {
error = Untranslated("Wallet creation failed.") + Untranslated(" ") + error;

View File

@ -16,7 +16,6 @@ EXPECTED_CIRCULAR_DEPENDENCIES=(
"index/coinstatsindex -> node/coinstats -> index/coinstatsindex"
"policy/fees -> txmempool -> policy/fees"
"qt/addresstablemodel -> qt/walletmodel -> qt/addresstablemodel"
"qt/bitcoingui -> qt/walletframe -> qt/bitcoingui"
"qt/recentrequeststablemodel -> qt/walletmodel -> qt/recentrequeststablemodel"
"qt/transactiontablemodel -> qt/walletmodel -> qt/transactiontablemodel"
"txmempool -> validation -> txmempool"