Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F13115845
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
116 KB
Subscribers
None
View Options
diff --git a/doc/release-notes.md b/doc/release-notes.md
index d7eb555725..76ee5aad18 100644
--- a/doc/release-notes.md
+++ b/doc/release-notes.md
@@ -1,7 +1,9 @@
Bitcoin ABC version 0.20.5 is now available from:
<https://download.bitcoinabc.org/0.20.5/>
This release includes the following features and fixes:
- Wallets loaded dynamically through the RPC interface may now be displayed in
the bitcoin-qt GUI.
+ - The default wallet will now be labeled `[default wallet]` in the bitcoin-qt
+ GUI if no name is provided by the `-wallet` option on start up.
diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp
index 08998de71e..cefe06e518 100644
--- a/src/qt/bitcoingui.cpp
+++ b/src/qt/bitcoingui.cpp
@@ -1,1340 +1,1347 @@
// Copyright (c) 2011-2016 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#if defined(HAVE_CONFIG_H)
#include <config/bitcoin-config.h>
#endif
#include <qt/bitcoingui.h>
#include <chain.h>
#include <chainparams.h>
#include <config.h>
#include <init.h>
#include <interfaces/handler.h>
#include <interfaces/node.h>
#include <qt/bitcoinunits.h>
#include <qt/clientmodel.h>
#include <qt/guiconstants.h>
#include <qt/guiutil.h>
#ifdef Q_OS_MAC
#include <qt/macdockiconhandler.h>
#endif
#include <qt/modaloverlay.h>
#include <qt/networkstyle.h>
#include <qt/notificator.h>
#include <qt/openuridialog.h>
#include <qt/optionsdialog.h>
#include <qt/optionsmodel.h>
#include <qt/platformstyle.h>
#include <qt/rpcconsole.h>
#include <qt/utilitydialog.h>
#ifdef ENABLE_WALLET
#include <qt/walletframe.h>
#include <qt/walletmodel.h>
#include <qt/walletview.h>
#endif // ENABLE_WALLET
#include <ui_interface.h>
#include <util/system.h>
#include <QAction>
#include <QApplication>
#include <QComboBox>
#include <QDateTime>
#include <QDesktopWidget>
#include <QDragEnterEvent>
#include <QListWidget>
#include <QMenuBar>
#include <QMessageBox>
#include <QMimeData>
#include <QProgressDialog>
#include <QSettings>
#include <QShortcut>
#include <QStackedWidget>
#include <QStatusBar>
#include <QStyle>
#include <QTimer>
#include <QToolBar>
#include <QUrlQuery>
#include <QVBoxLayout>
const std::string BitcoinGUI::DEFAULT_UIPLATFORM =
#if defined(Q_OS_MAC)
"macosx"
#elif defined(Q_OS_WIN)
"windows"
#else
"other"
#endif
;
BitcoinGUI::BitcoinGUI(interfaces::Node &node, const Config *configIn,
const PlatformStyle *_platformStyle,
const NetworkStyle *networkStyle, QWidget *parent)
: QMainWindow(parent), enableWallet(false), m_node(node),
platformStyle(_platformStyle), config(configIn) {
QSettings settings;
if (!restoreGeometry(settings.value("MainWindowGeometry").toByteArray())) {
// Restore failed (perhaps missing setting), center the window
move(QApplication::desktop()->availableGeometry().center() -
frameGeometry().center());
}
QString windowTitle = tr(PACKAGE_NAME) + " - ";
#ifdef ENABLE_WALLET
enableWallet = WalletModel::isWalletEnabled();
#endif // ENABLE_WALLET
if (enableWallet) {
windowTitle += tr("Wallet");
} else {
windowTitle += tr("Node");
}
windowTitle += " " + networkStyle->getTitleAddText();
#ifndef Q_OS_MAC
QApplication::setWindowIcon(networkStyle->getTrayAndWindowIcon());
setWindowIcon(networkStyle->getTrayAndWindowIcon());
#else
MacDockIconHandler::instance()->setIcon(networkStyle->getAppIcon());
#endif
setWindowTitle(windowTitle);
rpcConsole = new RPCConsole(node, _platformStyle, 0);
helpMessageDialog = new HelpMessageDialog(node, this, false);
#ifdef ENABLE_WALLET
if (enableWallet) {
/** Create wallet frame and make it the central widget */
walletFrame = new WalletFrame(_platformStyle, this);
setCentralWidget(walletFrame);
} else
#endif // ENABLE_WALLET
{
/**
* When compiled without wallet or -disablewallet is provided, the
* central widget is the rpc console.
*/
setCentralWidget(rpcConsole);
}
// Accept D&D of URIs
setAcceptDrops(true);
// Create actions for the toolbar, menu bar and tray/dock icon
// Needs walletFrame to be initialized
createActions();
// Create application menu bar
createMenuBar();
// Create the toolbars
createToolBars();
// Create system tray icon and notification
createTrayIcon(networkStyle);
// Create status bar
statusBar();
// Disable size grip because it looks ugly and nobody needs it
statusBar()->setSizeGripEnabled(false);
// Status bar notification icons
QFrame *frameBlocks = new QFrame();
frameBlocks->setContentsMargins(0, 0, 0, 0);
frameBlocks->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred);
QHBoxLayout *frameBlocksLayout = new QHBoxLayout(frameBlocks);
frameBlocksLayout->setContentsMargins(3, 0, 3, 0);
frameBlocksLayout->setSpacing(3);
unitDisplayControl = new UnitDisplayStatusBarControl(platformStyle);
labelWalletEncryptionIcon = new QLabel();
labelWalletHDStatusIcon = new QLabel();
connectionsControl = new GUIUtil::ClickableLabel();
labelBlocksIcon = new GUIUtil::ClickableLabel();
if (enableWallet) {
frameBlocksLayout->addStretch();
frameBlocksLayout->addWidget(unitDisplayControl);
frameBlocksLayout->addStretch();
frameBlocksLayout->addWidget(labelWalletEncryptionIcon);
frameBlocksLayout->addWidget(labelWalletHDStatusIcon);
}
frameBlocksLayout->addStretch();
frameBlocksLayout->addWidget(connectionsControl);
frameBlocksLayout->addStretch();
frameBlocksLayout->addWidget(labelBlocksIcon);
frameBlocksLayout->addStretch();
// Progress bar and label for blocks download
progressBarLabel = new QLabel();
progressBarLabel->setVisible(false);
progressBar = new GUIUtil::ProgressBar();
progressBar->setAlignment(Qt::AlignCenter);
progressBar->setVisible(false);
// Override style sheet for progress bar for styles that have a segmented
// progress bar, as they make the text unreadable (workaround for issue
// #1071)
// See https://doc.qt.io/qt-5/gallery.html
QString curStyle = QApplication::style()->metaObject()->className();
if (curStyle == "QWindowsStyle" || curStyle == "QWindowsXPStyle") {
progressBar->setStyleSheet(
"QProgressBar { background-color: #e8e8e8; border: 1px solid grey; "
"border-radius: 7px; padding: 1px; text-align: center; } "
"QProgressBar::chunk { background: QLinearGradient(x1: 0, y1: 0, "
"x2: 1, y2: 0, stop: 0 #FF8000, stop: 1 orange); border-radius: "
"7px; margin: 0px; }");
}
statusBar()->addWidget(progressBarLabel);
statusBar()->addWidget(progressBar);
statusBar()->addPermanentWidget(frameBlocks);
// Install event filter to be able to catch status tip events
// (QEvent::StatusTip)
this->installEventFilter(this);
// Initially wallet actions should be disabled
setWalletActionsEnabled(false);
// Subscribe to notifications from core
subscribeToCoreSignals();
connect(connectionsControl, SIGNAL(clicked(QPoint)), this,
SLOT(toggleNetworkActive()));
modalOverlay = new ModalOverlay(this->centralWidget());
#ifdef ENABLE_WALLET
if (enableWallet) {
connect(walletFrame, SIGNAL(requestedSyncWarningInfo()), this,
SLOT(showModalOverlay()));
connect(labelBlocksIcon, SIGNAL(clicked(QPoint)), this,
SLOT(showModalOverlay()));
connect(progressBar, SIGNAL(clicked(QPoint)), this,
SLOT(showModalOverlay()));
}
#endif
}
BitcoinGUI::~BitcoinGUI() {
// Unsubscribe from notifications from core
unsubscribeFromCoreSignals();
QSettings settings;
settings.setValue("MainWindowGeometry", saveGeometry());
// Hide tray icon, as deleting will let it linger until quit (on Ubuntu)
if (trayIcon) {
trayIcon->hide();
}
#ifdef Q_OS_MAC
delete appMenuBar;
MacDockIconHandler::cleanup();
#endif
delete rpcConsole;
}
void BitcoinGUI::createActions() {
QActionGroup *tabGroup = new QActionGroup(this);
overviewAction =
new QAction(platformStyle->SingleColorIcon(":/icons/overview"),
tr("&Overview"), this);
overviewAction->setStatusTip(tr("Show general overview of wallet"));
overviewAction->setToolTip(overviewAction->statusTip());
overviewAction->setCheckable(true);
overviewAction->setShortcut(QKeySequence(Qt::ALT + Qt::Key_1));
tabGroup->addAction(overviewAction);
sendCoinsAction = new QAction(
platformStyle->SingleColorIcon(":/icons/send"), tr("&Send"), this);
sendCoinsAction->setStatusTip(tr("Send coins to a Bitcoin address"));
sendCoinsAction->setToolTip(sendCoinsAction->statusTip());
sendCoinsAction->setCheckable(true);
sendCoinsAction->setShortcut(QKeySequence(Qt::ALT + Qt::Key_2));
tabGroup->addAction(sendCoinsAction);
sendCoinsMenuAction =
new QAction(platformStyle->TextColorIcon(":/icons/send"),
sendCoinsAction->text(), this);
sendCoinsMenuAction->setStatusTip(sendCoinsAction->statusTip());
sendCoinsMenuAction->setToolTip(sendCoinsMenuAction->statusTip());
receiveCoinsAction = new QAction(
platformStyle->SingleColorIcon(":/icons/receiving_addresses"),
tr("&Receive"), this);
receiveCoinsAction->setStatusTip(
tr("Request payments (generates QR codes and %1: URIs)")
.arg(QString::fromStdString(
config->GetChainParams().CashAddrPrefix())));
receiveCoinsAction->setToolTip(receiveCoinsAction->statusTip());
receiveCoinsAction->setCheckable(true);
receiveCoinsAction->setShortcut(QKeySequence(Qt::ALT + Qt::Key_3));
tabGroup->addAction(receiveCoinsAction);
receiveCoinsMenuAction =
new QAction(platformStyle->TextColorIcon(":/icons/receiving_addresses"),
receiveCoinsAction->text(), this);
receiveCoinsMenuAction->setStatusTip(receiveCoinsAction->statusTip());
receiveCoinsMenuAction->setToolTip(receiveCoinsMenuAction->statusTip());
historyAction =
new QAction(platformStyle->SingleColorIcon(":/icons/history"),
tr("&Transactions"), this);
historyAction->setStatusTip(tr("Browse transaction history"));
historyAction->setToolTip(historyAction->statusTip());
historyAction->setCheckable(true);
historyAction->setShortcut(QKeySequence(Qt::ALT + Qt::Key_4));
tabGroup->addAction(historyAction);
#ifdef ENABLE_WALLET
// These showNormalIfMinimized are needed because Send Coins and Receive
// Coins can be triggered from the tray menu, and need to show the GUI to be
// useful.
connect(overviewAction, SIGNAL(triggered()), this,
SLOT(showNormalIfMinimized()));
connect(overviewAction, SIGNAL(triggered()), this,
SLOT(gotoOverviewPage()));
connect(sendCoinsAction, SIGNAL(triggered()), this,
SLOT(showNormalIfMinimized()));
connect(sendCoinsAction, SIGNAL(triggered()), this,
SLOT(gotoSendCoinsPage()));
connect(sendCoinsMenuAction, SIGNAL(triggered()), this,
SLOT(showNormalIfMinimized()));
connect(sendCoinsMenuAction, SIGNAL(triggered()), this,
SLOT(gotoSendCoinsPage()));
connect(receiveCoinsAction, SIGNAL(triggered()), this,
SLOT(showNormalIfMinimized()));
connect(receiveCoinsAction, SIGNAL(triggered()), this,
SLOT(gotoReceiveCoinsPage()));
connect(receiveCoinsMenuAction, SIGNAL(triggered()), this,
SLOT(showNormalIfMinimized()));
connect(receiveCoinsMenuAction, SIGNAL(triggered()), this,
SLOT(gotoReceiveCoinsPage()));
connect(historyAction, SIGNAL(triggered()), this,
SLOT(showNormalIfMinimized()));
connect(historyAction, SIGNAL(triggered()), this, SLOT(gotoHistoryPage()));
#endif // ENABLE_WALLET
quitAction = new QAction(platformStyle->TextColorIcon(":/icons/quit"),
tr("E&xit"), this);
quitAction->setStatusTip(tr("Quit application"));
quitAction->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_Q));
quitAction->setMenuRole(QAction::QuitRole);
aboutAction = new QAction(platformStyle->TextColorIcon(":/icons/about"),
tr("&About %1").arg(tr(PACKAGE_NAME)), this);
aboutAction->setStatusTip(
tr("Show information about %1").arg(tr(PACKAGE_NAME)));
aboutAction->setMenuRole(QAction::AboutRole);
aboutAction->setEnabled(false);
aboutQtAction =
new QAction(platformStyle->TextColorIcon(":/icons/about_qt"),
tr("About &Qt"), this);
aboutQtAction->setStatusTip(tr("Show information about Qt"));
aboutQtAction->setMenuRole(QAction::AboutQtRole);
optionsAction = new QAction(platformStyle->TextColorIcon(":/icons/options"),
tr("&Options..."), this);
optionsAction->setStatusTip(
tr("Modify configuration options for %1").arg(tr(PACKAGE_NAME)));
optionsAction->setMenuRole(QAction::PreferencesRole);
optionsAction->setEnabled(false);
toggleHideAction =
new QAction(platformStyle->TextColorIcon(":/icons/about"),
tr("&Show / Hide"), this);
toggleHideAction->setStatusTip(tr("Show or hide the main Window"));
encryptWalletAction =
new QAction(platformStyle->TextColorIcon(":/icons/lock_closed"),
tr("&Encrypt Wallet..."), this);
encryptWalletAction->setStatusTip(
tr("Encrypt the private keys that belong to your wallet"));
encryptWalletAction->setCheckable(true);
backupWalletAction =
new QAction(platformStyle->TextColorIcon(":/icons/filesave"),
tr("&Backup Wallet..."), this);
backupWalletAction->setStatusTip(tr("Backup wallet to another location"));
changePassphraseAction =
new QAction(platformStyle->TextColorIcon(":/icons/key"),
tr("&Change Passphrase..."), this);
changePassphraseAction->setStatusTip(
tr("Change the passphrase used for wallet encryption"));
signMessageAction =
new QAction(platformStyle->TextColorIcon(":/icons/edit"),
tr("Sign &message..."), this);
signMessageAction->setStatusTip(
tr("Sign messages with your Bitcoin addresses to prove you own them"));
verifyMessageAction =
new QAction(platformStyle->TextColorIcon(":/icons/verify"),
tr("&Verify message..."), this);
verifyMessageAction->setStatusTip(
tr("Verify messages to ensure they were signed with specified Bitcoin "
"addresses"));
openRPCConsoleAction =
new QAction(platformStyle->TextColorIcon(":/icons/debugwindow"),
tr("&Debug window"), this);
openRPCConsoleAction->setStatusTip(
tr("Open debugging and diagnostic console"));
// initially disable the debug window menu item
openRPCConsoleAction->setEnabled(false);
usedSendingAddressesAction =
new QAction(platformStyle->TextColorIcon(":/icons/address-book"),
tr("&Sending addresses..."), this);
usedSendingAddressesAction->setStatusTip(
tr("Show the list of used sending addresses and labels"));
usedReceivingAddressesAction =
new QAction(platformStyle->TextColorIcon(":/icons/address-book"),
tr("&Receiving addresses..."), this);
usedReceivingAddressesAction->setStatusTip(
tr("Show the list of used receiving addresses and labels"));
openAction = new QAction(platformStyle->TextColorIcon(":/icons/open"),
tr("Open &URI..."), this);
openAction->setStatusTip(
tr("Open a %1: URI or payment request")
.arg(QString::fromStdString(
config->GetChainParams().CashAddrPrefix())));
showHelpMessageAction =
new QAction(platformStyle->TextColorIcon(":/icons/info"),
tr("&Command-line options"), this);
showHelpMessageAction->setMenuRole(QAction::NoRole);
showHelpMessageAction->setStatusTip(
tr("Show the %1 help message to get a list with possible Bitcoin "
"command-line options")
.arg(tr(PACKAGE_NAME)));
connect(quitAction, SIGNAL(triggered()), qApp, SLOT(quit()));
connect(aboutAction, SIGNAL(triggered()), this, SLOT(aboutClicked()));
connect(aboutQtAction, SIGNAL(triggered()), qApp, SLOT(aboutQt()));
connect(optionsAction, SIGNAL(triggered()), this, SLOT(optionsClicked()));
connect(toggleHideAction, SIGNAL(triggered()), this, SLOT(toggleHidden()));
connect(showHelpMessageAction, SIGNAL(triggered()), this,
SLOT(showHelpMessageClicked()));
connect(openRPCConsoleAction, SIGNAL(triggered()), this,
SLOT(showDebugWindow()));
// prevents an open debug window from becoming stuck/unusable on client
// shutdown
connect(quitAction, SIGNAL(triggered()), rpcConsole, SLOT(hide()));
#ifdef ENABLE_WALLET
if (walletFrame) {
connect(encryptWalletAction, SIGNAL(triggered(bool)), walletFrame,
SLOT(encryptWallet(bool)));
connect(backupWalletAction, SIGNAL(triggered()), walletFrame,
SLOT(backupWallet()));
connect(changePassphraseAction, SIGNAL(triggered()), walletFrame,
SLOT(changePassphrase()));
connect(signMessageAction, SIGNAL(triggered()), this,
SLOT(gotoSignMessageTab()));
connect(verifyMessageAction, SIGNAL(triggered()), this,
SLOT(gotoVerifyMessageTab()));
connect(usedSendingAddressesAction, SIGNAL(triggered()), walletFrame,
SLOT(usedSendingAddresses()));
connect(usedReceivingAddressesAction, SIGNAL(triggered()), walletFrame,
SLOT(usedReceivingAddresses()));
connect(openAction, SIGNAL(triggered()), this, SLOT(openClicked()));
}
#endif // ENABLE_WALLET
new QShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_C), this,
SLOT(showDebugWindowActivateConsole()));
new QShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_D), this,
SLOT(showDebugWindow()));
}
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"));
if (walletFrame) {
file->addAction(openAction);
file->addAction(backupWalletAction);
file->addAction(signMessageAction);
file->addAction(verifyMessageAction);
file->addSeparator();
file->addAction(usedSendingAddressesAction);
file->addAction(usedReceivingAddressesAction);
file->addSeparator();
}
file->addAction(quitAction);
QMenu *settings = appMenuBar->addMenu(tr("&Settings"));
if (walletFrame) {
settings->addAction(encryptWalletAction);
settings->addAction(changePassphraseAction);
settings->addSeparator();
}
settings->addAction(optionsAction);
QMenu *help = appMenuBar->addMenu(tr("&Help"));
if (walletFrame) {
help->addAction(openRPCConsoleAction);
}
help->addAction(showHelpMessageAction);
help->addSeparator();
help->addAction(aboutAction);
help->addAction(aboutQtAction);
}
void BitcoinGUI::createToolBars() {
if (walletFrame) {
QToolBar *toolbar = addToolBar(tr("Tabs toolbar"));
appToolBar = toolbar;
toolbar->setContextMenuPolicy(Qt::PreventContextMenu);
toolbar->setMovable(false);
toolbar->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
toolbar->addAction(overviewAction);
toolbar->addAction(sendCoinsAction);
toolbar->addAction(receiveCoinsAction);
toolbar->addAction(historyAction);
overviewAction->setChecked(true);
#ifdef ENABLE_WALLET
QWidget *spacer = new QWidget();
spacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
toolbar->addWidget(spacer);
m_wallet_selector = new QComboBox();
- connect(m_wallet_selector, SIGNAL(currentIndexChanged(const QString &)),
- this, SLOT(setCurrentWallet(const QString &)));
+ connect(m_wallet_selector, SIGNAL(currentIndexChanged(int)), this,
+ SLOT(setCurrentWalletBySelectorIndex(int)));
#endif
}
}
void BitcoinGUI::setClientModel(ClientModel *_clientModel) {
this->clientModel = _clientModel;
if (_clientModel) {
// Create system tray menu (or setup the dock menu) that late to prevent
// users from calling actions, while the client has not yet fully loaded
createTrayIconMenu();
// Keep up to date with client
updateNetworkState();
connect(_clientModel, SIGNAL(numConnectionsChanged(int)), this,
SLOT(setNumConnections(int)));
connect(_clientModel, SIGNAL(networkActiveChanged(bool)), this,
SLOT(setNetworkActive(bool)));
modalOverlay->setKnownBestHeight(
_clientModel->getHeaderTipHeight(),
QDateTime::fromTime_t(_clientModel->getHeaderTipTime()));
setNumBlocks(m_node.getNumBlocks(),
QDateTime::fromTime_t(m_node.getLastBlockTime()),
m_node.getVerificationProgress(), false);
connect(_clientModel,
SIGNAL(numBlocksChanged(int, QDateTime, double, bool)), this,
SLOT(setNumBlocks(int, QDateTime, double, bool)));
// Receive and report messages from client model
connect(_clientModel, SIGNAL(message(QString, QString, unsigned int)),
this, SLOT(message(QString, QString, unsigned int)));
// Show progress dialog
connect(_clientModel, SIGNAL(showProgress(QString, int)), this,
SLOT(showProgress(QString, int)));
rpcConsole->setClientModel(_clientModel);
#ifdef ENABLE_WALLET
if (walletFrame) {
walletFrame->setClientModel(_clientModel);
}
#endif // ENABLE_WALLET
unitDisplayControl->setOptionsModel(_clientModel->getOptionsModel());
OptionsModel *optionsModel = _clientModel->getOptionsModel();
if (optionsModel) {
// be aware of the tray icon disable state change reported by the
// OptionsModel object.
connect(optionsModel, SIGNAL(hideTrayIconChanged(bool)), this,
SLOT(setTrayIconVisible(bool)));
// initialize the disable state of the tray icon with the current
// value in the model.
setTrayIconVisible(optionsModel->getHideTrayIcon());
}
} else {
// Disable possibility to show main window via action
toggleHideAction->setEnabled(false);
if (trayIconMenu) {
// Disable context menu on tray icon
trayIconMenu->clear();
}
// Propagate cleared model to child objects
rpcConsole->setClientModel(nullptr);
#ifdef ENABLE_WALLET
if (walletFrame) {
walletFrame->setClientModel(nullptr);
}
#endif // ENABLE_WALLET
unitDisplayControl->setOptionsModel(nullptr);
}
}
#ifdef ENABLE_WALLET
bool BitcoinGUI::addWallet(WalletModel *walletModel) {
if (!walletFrame) return false;
const QString name = walletModel->getWalletName();
+ QString display_name =
+ name.isEmpty() ? "[" + tr("default wallet") + "]" : name;
setWalletActionsEnabled(true);
- m_wallet_selector->addItem(name);
+ m_wallet_selector->addItem(display_name, name);
if (m_wallet_selector->count() == 2) {
m_wallet_selector_label = new QLabel();
m_wallet_selector_label->setText(tr("Wallet:") + " ");
m_wallet_selector_label->setBuddy(m_wallet_selector);
appToolBar->addWidget(m_wallet_selector_label);
appToolBar->addWidget(m_wallet_selector);
}
rpcConsole->addWallet(walletModel);
return walletFrame->addWallet(walletModel);
}
bool BitcoinGUI::setCurrentWallet(const QString &name) {
if (!walletFrame) return false;
return walletFrame->setCurrentWallet(name);
}
+bool BitcoinGUI::setCurrentWalletBySelectorIndex(int index) {
+ QString internal_name = m_wallet_selector->itemData(index).toString();
+ return setCurrentWallet(internal_name);
+}
+
void BitcoinGUI::removeAllWallets() {
if (!walletFrame) return;
setWalletActionsEnabled(false);
walletFrame->removeAllWallets();
}
#endif // ENABLE_WALLET
void BitcoinGUI::setWalletActionsEnabled(bool enabled) {
overviewAction->setEnabled(enabled);
sendCoinsAction->setEnabled(enabled);
sendCoinsMenuAction->setEnabled(enabled);
receiveCoinsAction->setEnabled(enabled);
receiveCoinsMenuAction->setEnabled(enabled);
historyAction->setEnabled(enabled);
encryptWalletAction->setEnabled(enabled);
backupWalletAction->setEnabled(enabled);
changePassphraseAction->setEnabled(enabled);
signMessageAction->setEnabled(enabled);
verifyMessageAction->setEnabled(enabled);
usedSendingAddressesAction->setEnabled(enabled);
usedReceivingAddressesAction->setEnabled(enabled);
openAction->setEnabled(enabled);
}
void BitcoinGUI::createTrayIcon(const NetworkStyle *networkStyle) {
#ifndef Q_OS_MAC
trayIcon = new QSystemTrayIcon(this);
QString toolTip = tr("%1 client").arg(tr(PACKAGE_NAME)) + " " +
networkStyle->getTitleAddText();
trayIcon->setToolTip(toolTip);
trayIcon->setIcon(networkStyle->getTrayAndWindowIcon());
trayIcon->hide();
#endif
notificator =
new Notificator(QApplication::applicationName(), trayIcon, this);
}
void BitcoinGUI::createTrayIconMenu() {
#ifndef Q_OS_MAC
// return if trayIcon is unset (only on non-Mac OSes)
if (!trayIcon) return;
trayIconMenu = new QMenu(this);
trayIcon->setContextMenu(trayIconMenu);
connect(trayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)),
this, SLOT(trayIconActivated(QSystemTrayIcon::ActivationReason)));
#else
// Note: On Mac, the dock icon is used to provide the tray's functionality.
MacDockIconHandler *dockIconHandler = MacDockIconHandler::instance();
dockIconHandler->setMainWindow(static_cast<QMainWindow *>(this));
trayIconMenu = dockIconHandler->dockMenu();
#endif
// Configuration of the tray icon (or dock icon) icon menu
trayIconMenu->addAction(toggleHideAction);
trayIconMenu->addSeparator();
trayIconMenu->addAction(sendCoinsMenuAction);
trayIconMenu->addAction(receiveCoinsMenuAction);
trayIconMenu->addSeparator();
trayIconMenu->addAction(signMessageAction);
trayIconMenu->addAction(verifyMessageAction);
trayIconMenu->addSeparator();
trayIconMenu->addAction(optionsAction);
trayIconMenu->addAction(openRPCConsoleAction);
#ifndef Q_OS_MAC // This is built-in on Mac
trayIconMenu->addSeparator();
trayIconMenu->addAction(quitAction);
#endif
}
#ifndef Q_OS_MAC
void BitcoinGUI::trayIconActivated(QSystemTrayIcon::ActivationReason reason) {
if (reason == QSystemTrayIcon::Trigger) {
// Click on system tray icon triggers show/hide of the main window
toggleHidden();
}
}
#endif
void BitcoinGUI::optionsClicked() {
if (!clientModel || !clientModel->getOptionsModel()) return;
OptionsDialog dlg(this, enableWallet);
dlg.setModel(clientModel->getOptionsModel());
dlg.exec();
}
void BitcoinGUI::aboutClicked() {
if (!clientModel) return;
HelpMessageDialog dlg(m_node, this, true);
dlg.exec();
}
void BitcoinGUI::showDebugWindow() {
rpcConsole->showNormal();
rpcConsole->show();
rpcConsole->raise();
rpcConsole->activateWindow();
}
void BitcoinGUI::showDebugWindowActivateConsole() {
rpcConsole->setTabFocus(RPCConsole::TAB_CONSOLE);
showDebugWindow();
}
void BitcoinGUI::showHelpMessageClicked() {
helpMessageDialog->show();
}
#ifdef ENABLE_WALLET
void BitcoinGUI::openClicked() {
OpenURIDialog dlg(config->GetChainParams(), this);
if (dlg.exec()) {
Q_EMIT receivedURI(dlg.getURI());
}
}
void BitcoinGUI::gotoOverviewPage() {
overviewAction->setChecked(true);
if (walletFrame) walletFrame->gotoOverviewPage();
}
void BitcoinGUI::gotoHistoryPage() {
historyAction->setChecked(true);
if (walletFrame) walletFrame->gotoHistoryPage();
}
void BitcoinGUI::gotoReceiveCoinsPage() {
receiveCoinsAction->setChecked(true);
if (walletFrame) walletFrame->gotoReceiveCoinsPage();
}
void BitcoinGUI::gotoSendCoinsPage(QString addr) {
sendCoinsAction->setChecked(true);
if (walletFrame) walletFrame->gotoSendCoinsPage(addr);
}
void BitcoinGUI::gotoSignMessageTab(QString addr) {
if (walletFrame) walletFrame->gotoSignMessageTab(addr);
}
void BitcoinGUI::gotoVerifyMessageTab(QString addr) {
if (walletFrame) walletFrame->gotoVerifyMessageTab(addr);
}
#endif // ENABLE_WALLET
void BitcoinGUI::updateNetworkState() {
int count = clientModel->getNumConnections();
QString icon;
switch (count) {
case 0:
icon = ":/icons/connect_0";
break;
case 1:
case 2:
case 3:
icon = ":/icons/connect_1";
break;
case 4:
case 5:
case 6:
icon = ":/icons/connect_2";
break;
case 7:
case 8:
case 9:
icon = ":/icons/connect_3";
break;
default:
icon = ":/icons/connect_4";
break;
}
QString tooltip;
if (m_node.getNetworkActive()) {
tooltip = tr("%n active connection(s) to Bitcoin network", "", count) +
QString(".<br>") + tr("Click to disable network activity.");
} else {
tooltip = tr("Network activity disabled.") + QString("<br>") +
tr("Click to enable network activity again.");
icon = ":/icons/network_disabled";
}
// Don't word-wrap this (fixed-width) tooltip
tooltip = QString("<nobr>") + tooltip + QString("</nobr>");
connectionsControl->setToolTip(tooltip);
connectionsControl->setPixmap(platformStyle->SingleColorIcon(icon).pixmap(
STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE));
}
void BitcoinGUI::setNumConnections(int count) {
updateNetworkState();
}
void BitcoinGUI::setNetworkActive(bool networkActive) {
updateNetworkState();
}
void BitcoinGUI::updateHeadersSyncProgressLabel() {
int64_t headersTipTime = clientModel->getHeaderTipTime();
int headersTipHeight = clientModel->getHeaderTipHeight();
int estHeadersLeft =
(GetTime() - headersTipTime) /
config->GetChainParams().GetConsensus().nPowTargetSpacing;
if (estHeadersLeft > HEADER_HEIGHT_DELTA_SYNC) {
progressBarLabel->setText(
tr("Syncing Headers (%1%)...")
.arg(QString::number(100.0 /
(headersTipHeight + estHeadersLeft) *
headersTipHeight,
'f', 1)));
}
}
void BitcoinGUI::setNumBlocks(int count, const QDateTime &blockDate,
double nVerificationProgress, bool header) {
if (modalOverlay) {
if (header) {
modalOverlay->setKnownBestHeight(count, blockDate);
} else {
modalOverlay->tipUpdate(count, blockDate, nVerificationProgress);
}
}
if (!clientModel) {
return;
}
// Prevent orphan statusbar messages (e.g. hover Quit in main menu, wait
// until chain-sync starts -> garbled text)
statusBar()->clearMessage();
// Acquire current block source
enum BlockSource blockSource = clientModel->getBlockSource();
switch (blockSource) {
case BlockSource::NETWORK:
if (header) {
updateHeadersSyncProgressLabel();
return;
}
progressBarLabel->setText(tr("Synchronizing with network..."));
updateHeadersSyncProgressLabel();
break;
case BlockSource::DISK:
if (header) {
progressBarLabel->setText(tr("Indexing blocks on disk..."));
} else {
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;
}
progressBarLabel->setText(tr("Connecting to peers..."));
break;
}
QString tooltip;
QDateTime currentDate = QDateTime::currentDateTime();
qint64 secs = blockDate.secsTo(currentDate);
tooltip = tr("Processed %n block(s) of transaction history.", "", count);
// Set icon state: spinning if catching up, tick otherwise
if (secs < MAX_BLOCK_TIME_GAP) {
tooltip = tr("Up to date") + QString(".<br>") + tooltip;
labelBlocksIcon->setPixmap(
platformStyle->SingleColorIcon(":/icons/synced")
.pixmap(STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE));
#ifdef ENABLE_WALLET
if (walletFrame) {
walletFrame->showOutOfSyncWarning(false);
modalOverlay->showHide(true, true);
}
#endif // ENABLE_WALLET
progressBarLabel->setVisible(false);
progressBar->setVisible(false);
} else {
QString timeBehindText = GUIUtil::formatNiceTimeOffset(secs);
progressBarLabel->setVisible(true);
progressBar->setFormat(tr("%1 behind").arg(timeBehindText));
progressBar->setMaximum(1000000000);
progressBar->setValue(nVerificationProgress * 1000000000.0 + 0.5);
progressBar->setVisible(true);
tooltip = tr("Catching up...") + QString("<br>") + tooltip;
if (count != prevBlocks) {
labelBlocksIcon->setPixmap(
platformStyle
->SingleColorIcon(QString(":/movies/spinner-%1")
.arg(spinnerFrame, 3, 10, QChar('0')))
.pixmap(STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE));
spinnerFrame = (spinnerFrame + 1) % SPINNER_FRAMES;
}
prevBlocks = count;
#ifdef ENABLE_WALLET
if (walletFrame) {
walletFrame->showOutOfSyncWarning(true);
modalOverlay->showHide();
}
#endif // ENABLE_WALLET
tooltip += QString("<br>");
tooltip +=
tr("Last received block was generated %1 ago.").arg(timeBehindText);
tooltip += QString("<br>");
tooltip += tr("Transactions after this will not yet be visible.");
}
// Don't word-wrap this (fixed-width) tooltip
tooltip = QString("<nobr>") + tooltip + QString("</nobr>");
labelBlocksIcon->setToolTip(tooltip);
progressBarLabel->setToolTip(tooltip);
progressBar->setToolTip(tooltip);
}
void BitcoinGUI::message(const QString &title, const QString &message,
unsigned int style, bool *ret) {
// default title
QString strTitle = tr("Bitcoin");
// Default to information icon
int nMBoxIcon = QMessageBox::Information;
int nNotifyIcon = Notificator::Information;
QString msgType;
// Prefer supplied title over style based title
if (!title.isEmpty()) {
msgType = title;
} else {
switch (style) {
case CClientUIInterface::MSG_ERROR:
msgType = tr("Error");
break;
case CClientUIInterface::MSG_WARNING:
msgType = tr("Warning");
break;
case CClientUIInterface::MSG_INFORMATION:
msgType = tr("Information");
break;
default:
break;
}
}
// Append title to "Bitcoin - "
if (!msgType.isEmpty()) {
strTitle += " - " + msgType;
}
// Check for error/warning icon
if (style & CClientUIInterface::ICON_ERROR) {
nMBoxIcon = QMessageBox::Critical;
nNotifyIcon = Notificator::Critical;
} else if (style & CClientUIInterface::ICON_WARNING) {
nMBoxIcon = QMessageBox::Warning;
nNotifyIcon = Notificator::Warning;
}
// Display message
if (style & CClientUIInterface::MODAL) {
// Check for buttons, use OK as default, if none was supplied
QMessageBox::StandardButton buttons;
if (!(buttons = (QMessageBox::StandardButton)(
style & CClientUIInterface::BTN_MASK)))
buttons = QMessageBox::Ok;
showNormalIfMinimized();
QMessageBox mBox(static_cast<QMessageBox::Icon>(nMBoxIcon), strTitle,
message, buttons, this);
int r = mBox.exec();
if (ret != nullptr) {
*ret = r == QMessageBox::Ok;
}
} else
notificator->notify(static_cast<Notificator::Class>(nNotifyIcon),
strTitle, message);
}
void BitcoinGUI::changeEvent(QEvent *e) {
QMainWindow::changeEvent(e);
#ifndef Q_OS_MAC // Ignored on Mac
if (e->type() == QEvent::WindowStateChange) {
if (clientModel && clientModel->getOptionsModel() &&
clientModel->getOptionsModel()->getMinimizeToTray()) {
QWindowStateChangeEvent *wsevt =
static_cast<QWindowStateChangeEvent *>(e);
if (!(wsevt->oldState() & Qt::WindowMinimized) && isMinimized()) {
QTimer::singleShot(0, this, SLOT(hide()));
e->ignore();
} else if ((wsevt->oldState() & Qt::WindowMinimized) &&
!isMinimized()) {
QTimer::singleShot(0, this, SLOT(show()));
e->ignore();
}
}
}
#endif
}
void BitcoinGUI::closeEvent(QCloseEvent *event) {
#ifndef Q_OS_MAC // Ignored on Mac
if (clientModel && clientModel->getOptionsModel()) {
if (!clientModel->getOptionsModel()->getMinimizeOnClose()) {
// close rpcConsole in case it was open to make some space for the
// shutdown window
rpcConsole->close();
QApplication::quit();
} else {
QMainWindow::showMinimized();
event->ignore();
}
}
#else
QMainWindow::closeEvent(event);
#endif
}
void BitcoinGUI::showEvent(QShowEvent *event) {
// enable the debug window when the main window shows up
openRPCConsoleAction->setEnabled(true);
aboutAction->setEnabled(true);
optionsAction->setEnabled(true);
}
#ifdef ENABLE_WALLET
void BitcoinGUI::incomingTransaction(const QString &date, int unit,
const Amount amount, const QString &type,
const QString &address,
const QString &label,
const QString &walletName) {
// On new transaction, make an info balloon
QString msg = tr("Date: %1\n").arg(date) +
tr("Amount: %1\n")
.arg(BitcoinUnits::formatWithUnit(unit, amount, true));
if (m_node.getWallets().size() > 1 && !walletName.isEmpty()) {
msg += tr("Wallet: %1\n").arg(walletName);
}
msg += tr("Type: %1\n").arg(type);
if (!label.isEmpty()) {
msg += tr("Label: %1\n").arg(label);
} else if (!address.isEmpty()) {
msg += tr("Address: %1\n").arg(address);
}
message(amount < Amount::zero() ? tr("Sent transaction")
: tr("Incoming transaction"),
msg, CClientUIInterface::MSG_INFORMATION);
}
#endif // ENABLE_WALLET
void BitcoinGUI::dragEnterEvent(QDragEnterEvent *event) {
// Accept only URIs
if (event->mimeData()->hasUrls()) {
event->acceptProposedAction();
}
}
void BitcoinGUI::dropEvent(QDropEvent *event) {
if (event->mimeData()->hasUrls()) {
for (const QUrl &uri : event->mimeData()->urls()) {
Q_EMIT receivedURI(uri.toString());
}
}
event->acceptProposedAction();
}
bool BitcoinGUI::eventFilter(QObject *object, QEvent *event) {
// Catch status tip events
if (event->type() == QEvent::StatusTip) {
// Prevent adding text from setStatusTip(), if we currently use the
// status bar for displaying other stuff
if (progressBarLabel->isVisible() || progressBar->isVisible()) {
return true;
}
}
return QMainWindow::eventFilter(object, event);
}
#ifdef ENABLE_WALLET
bool BitcoinGUI::handlePaymentRequest(const SendCoinsRecipient &recipient) {
// URI has to be valid
if (walletFrame && walletFrame->handlePaymentRequest(recipient)) {
showNormalIfMinimized();
gotoSendCoinsPage();
return true;
}
return false;
}
void BitcoinGUI::setHDStatus(int hdEnabled) {
labelWalletHDStatusIcon->setPixmap(
platformStyle
->SingleColorIcon(hdEnabled ? ":/icons/hd_enabled"
: ":/icons/hd_disabled")
.pixmap(STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE));
labelWalletHDStatusIcon->setToolTip(
hdEnabled ? tr("HD key generation is <b>enabled</b>")
: tr("HD key generation is <b>disabled</b>"));
// eventually disable the QLabel to set its opacity to 50%
labelWalletHDStatusIcon->setEnabled(hdEnabled);
}
void BitcoinGUI::setEncryptionStatus(int status) {
switch (status) {
case WalletModel::Unencrypted:
labelWalletEncryptionIcon->hide();
encryptWalletAction->setChecked(false);
changePassphraseAction->setEnabled(false);
encryptWalletAction->setEnabled(true);
break;
case WalletModel::Unlocked:
labelWalletEncryptionIcon->show();
labelWalletEncryptionIcon->setPixmap(
platformStyle->SingleColorIcon(":/icons/lock_open")
.pixmap(STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE));
labelWalletEncryptionIcon->setToolTip(
tr("Wallet is <b>encrypted</b> and currently <b>unlocked</b>"));
encryptWalletAction->setChecked(true);
changePassphraseAction->setEnabled(true);
encryptWalletAction->setEnabled(
false); // TODO: decrypt currently not supported
break;
case WalletModel::Locked:
labelWalletEncryptionIcon->show();
labelWalletEncryptionIcon->setPixmap(
platformStyle->SingleColorIcon(":/icons/lock_closed")
.pixmap(STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE));
labelWalletEncryptionIcon->setToolTip(
tr("Wallet is <b>encrypted</b> and currently <b>locked</b>"));
encryptWalletAction->setChecked(true);
changePassphraseAction->setEnabled(true);
encryptWalletAction->setEnabled(
false); // TODO: decrypt currently not supported
break;
}
}
void BitcoinGUI::updateWalletStatus() {
if (!walletFrame) {
return;
}
WalletView *const walletView = walletFrame->currentWalletView();
if (!walletView) {
return;
}
WalletModel *const walletModel = walletView->getWalletModel();
setEncryptionStatus(walletModel->getEncryptionStatus());
setHDStatus(walletModel->wallet().hdEnabled());
}
#endif // ENABLE_WALLET
void BitcoinGUI::showNormalIfMinimized(bool fToggleHidden) {
if (!clientModel) {
return;
}
// activateWindow() (sometimes) helps with keyboard focus on Windows
if (isHidden()) {
show();
activateWindow();
} else if (isMinimized()) {
showNormal();
activateWindow();
} else if (GUIUtil::isObscured(this)) {
raise();
activateWindow();
} else if (fToggleHidden) {
hide();
}
}
void BitcoinGUI::toggleHidden() {
showNormalIfMinimized(true);
}
void BitcoinGUI::detectShutdown() {
if (m_node.shutdownRequested()) {
if (rpcConsole) {
rpcConsole->hide();
}
qApp->quit();
}
}
void BitcoinGUI::showProgress(const QString &title, int nProgress) {
if (nProgress == 0) {
progressDialog = new QProgressDialog(title, "", 0, 100);
progressDialog->setWindowModality(Qt::ApplicationModal);
progressDialog->setMinimumDuration(0);
progressDialog->setCancelButton(0);
progressDialog->setAutoClose(false);
progressDialog->setValue(0);
} else if (progressDialog) {
if (nProgress == 100) {
progressDialog->close();
progressDialog->deleteLater();
} else {
progressDialog->setValue(nProgress);
}
}
}
void BitcoinGUI::setTrayIconVisible(bool fHideTrayIcon) {
if (trayIcon) {
trayIcon->setVisible(!fHideTrayIcon);
}
}
void BitcoinGUI::showModalOverlay() {
if (modalOverlay &&
(progressBar->isVisible() || modalOverlay->isLayerVisible())) {
modalOverlay->toggleVisibility();
}
}
static bool ThreadSafeMessageBox(BitcoinGUI *gui, const std::string &message,
const std::string &caption,
unsigned int style) {
bool modal = (style & CClientUIInterface::MODAL);
// The SECURE flag has no effect in the Qt GUI.
// bool secure = (style & CClientUIInterface::SECURE);
style &= ~CClientUIInterface::SECURE;
bool ret = false;
// In case of modal message, use blocking connection to wait for user to
// click a button
QMetaObject::invokeMethod(gui, "message",
modal ? GUIUtil::blockingGUIThreadConnection()
: Qt::QueuedConnection,
Q_ARG(QString, QString::fromStdString(caption)),
Q_ARG(QString, QString::fromStdString(message)),
Q_ARG(unsigned int, style), Q_ARG(bool *, &ret));
return ret;
}
void BitcoinGUI::subscribeToCoreSignals() {
// Connect signals to client
m_handler_message_box = m_node.handleMessageBox(
std::bind(ThreadSafeMessageBox, this, std::placeholders::_1,
std::placeholders::_2, std::placeholders::_3));
m_handler_question = m_node.handleQuestion(
std::bind(ThreadSafeMessageBox, this, std::placeholders::_1,
std::placeholders::_3, std::placeholders::_4));
}
void BitcoinGUI::unsubscribeFromCoreSignals() {
// Disconnect signals from client
m_handler_message_box->disconnect();
m_handler_question->disconnect();
}
void BitcoinGUI::toggleNetworkActive() {
m_node.setNetworkActive(!m_node.getNetworkActive());
}
UnitDisplayStatusBarControl::UnitDisplayStatusBarControl(
const PlatformStyle *platformStyle)
: optionsModel(0), menu(0) {
createContextMenu();
setToolTip(tr("Unit to show amounts in. Click to select another unit."));
QList<BitcoinUnits::Unit> units = BitcoinUnits::availableUnits();
int max_width = 0;
const QFontMetrics fm(font());
for (const BitcoinUnits::Unit unit : units) {
max_width = qMax(max_width, fm.width(BitcoinUnits::name(unit)));
}
setMinimumSize(max_width, 0);
setAlignment(Qt::AlignRight | Qt::AlignVCenter);
setStyleSheet(QString("QLabel { color : %1 }")
.arg(platformStyle->SingleColor().name()));
}
/** So that it responds to button clicks */
void UnitDisplayStatusBarControl::mousePressEvent(QMouseEvent *event) {
onDisplayUnitsClicked(event->pos());
}
/** Creates context menu, its actions, and wires up all the relevant signals for
* mouse events. */
void UnitDisplayStatusBarControl::createContextMenu() {
menu = new QMenu(this);
for (const BitcoinUnits::Unit u : BitcoinUnits::availableUnits()) {
QAction *menuAction = new QAction(QString(BitcoinUnits::name(u)), this);
menuAction->setData(QVariant(u));
menu->addAction(menuAction);
}
connect(menu, SIGNAL(triggered(QAction *)), this,
SLOT(onMenuSelection(QAction *)));
}
/** Lets the control know about the Options Model (and its signals) */
void UnitDisplayStatusBarControl::setOptionsModel(OptionsModel *_optionsModel) {
if (_optionsModel) {
this->optionsModel = _optionsModel;
// be aware of a display unit change reported by the OptionsModel
// object.
connect(_optionsModel, SIGNAL(displayUnitChanged(int)), this,
SLOT(updateDisplayUnit(int)));
// initialize the display units label with the current value in the
// model.
updateDisplayUnit(_optionsModel->getDisplayUnit());
}
}
/** 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) {
setText(BitcoinUnits::name(newUnits));
}
/** Shows context menu with Display Unit options by the mouse coordinates */
void UnitDisplayStatusBarControl::onDisplayUnitsClicked(const QPoint &point) {
QPoint globalPos = mapToGlobal(point);
menu->exec(globalPos);
}
/** Tells underlying optionsModel to update its current display unit. */
void UnitDisplayStatusBarControl::onMenuSelection(QAction *action) {
if (action) {
optionsModel->setDisplayUnit(action->data());
}
}
diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h
index 226ef982f1..c4720400ea 100644
--- a/src/qt/bitcoingui.h
+++ b/src/qt/bitcoingui.h
@@ -1,313 +1,314 @@
// Copyright (c) 2011-2016 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef BITCOIN_QT_BITCOINGUI_H
#define BITCOIN_QT_BITCOINGUI_H
#if defined(HAVE_CONFIG_H)
#include <config/bitcoin-config.h>
#endif
#include <amount.h>
#include <QLabel>
#include <QMainWindow>
#include <QMap>
#include <QMenu>
#include <QPoint>
#include <QSystemTrayIcon>
#include <memory>
class ClientModel;
class NetworkStyle;
class Notificator;
class OptionsModel;
class PlatformStyle;
class RPCConsole;
class SendCoinsRecipient;
class UnitDisplayStatusBarControl;
class WalletFrame;
class WalletModel;
class HelpMessageDialog;
class ModalOverlay;
class Config;
namespace interfaces {
class Handler;
class Node;
} // namespace interfaces
QT_BEGIN_NAMESPACE
class QAction;
class QComboBox;
class QProgressBar;
class QProgressDialog;
QT_END_NAMESPACE
/**
* Bitcoin GUI main class. This class represents the main window of the Bitcoin
* UI. It communicates with both the client and wallet models to give the user
* an up-to-date view of the current core state.
*/
class BitcoinGUI : public QMainWindow {
Q_OBJECT
public:
static const std::string DEFAULT_UIPLATFORM;
explicit BitcoinGUI(interfaces::Node &node, const Config *,
const PlatformStyle *platformStyle,
const NetworkStyle *networkStyle, QWidget *parent = 0);
~BitcoinGUI();
/**
* Set the client model.
* The client model represents the part of the core that communicates with
* the P2P network, and is wallet-agnostic.
*/
void setClientModel(ClientModel *clientModel);
#ifdef ENABLE_WALLET
/**
* Set the wallet model.
* The wallet model represents a bitcoin wallet, and offers access to the
* list of transactions, address book and sending functionality.
*/
bool addWallet(WalletModel *walletModel);
void removeAllWallets();
#endif // ENABLE_WALLET
bool enableWallet = false;
protected:
void changeEvent(QEvent *e) override;
void closeEvent(QCloseEvent *event) override;
void showEvent(QShowEvent *event) override;
void dragEnterEvent(QDragEnterEvent *event) override;
void dropEvent(QDropEvent *event) override;
bool eventFilter(QObject *object, QEvent *event) override;
private:
interfaces::Node &m_node;
std::unique_ptr<interfaces::Handler> m_handler_message_box;
std::unique_ptr<interfaces::Handler> m_handler_question;
ClientModel *clientModel = nullptr;
WalletFrame *walletFrame = nullptr;
UnitDisplayStatusBarControl *unitDisplayControl = nullptr;
QLabel *labelWalletEncryptionIcon = nullptr;
QLabel *labelWalletHDStatusIcon = nullptr;
QLabel *connectionsControl = nullptr;
QLabel *labelBlocksIcon = nullptr;
QLabel *progressBarLabel = nullptr;
QProgressBar *progressBar = nullptr;
QProgressDialog *progressDialog = nullptr;
QMenuBar *appMenuBar = nullptr;
QToolBar *appToolBar = nullptr;
QAction *overviewAction = nullptr;
QAction *historyAction = nullptr;
QAction *quitAction = nullptr;
QAction *sendCoinsAction = nullptr;
QAction *sendCoinsMenuAction = nullptr;
QAction *usedSendingAddressesAction = nullptr;
QAction *usedReceivingAddressesAction = nullptr;
QAction *signMessageAction = nullptr;
QAction *verifyMessageAction = nullptr;
QAction *aboutAction = nullptr;
QAction *receiveCoinsAction = nullptr;
QAction *receiveCoinsMenuAction = nullptr;
QAction *optionsAction = nullptr;
QAction *toggleHideAction = nullptr;
QAction *encryptWalletAction = nullptr;
QAction *backupWalletAction = nullptr;
QAction *changePassphraseAction = nullptr;
QAction *aboutQtAction = nullptr;
QAction *openRPCConsoleAction = nullptr;
QAction *openAction = nullptr;
QAction *showHelpMessageAction = nullptr;
QLabel *m_wallet_selector_label = nullptr;
QComboBox *m_wallet_selector = nullptr;
QSystemTrayIcon *trayIcon = nullptr;
QMenu *trayIconMenu = nullptr;
Notificator *notificator = nullptr;
RPCConsole *rpcConsole = nullptr;
HelpMessageDialog *helpMessageDialog = nullptr;
ModalOverlay *modalOverlay = nullptr;
/** Keep track of previous number of blocks, to detect progress */
int prevBlocks = 0;
int spinnerFrame = 0;
const PlatformStyle *platformStyle;
const Config *config;
/** Create the main UI actions. */
void createActions();
/** Create the menu bar and sub-menus. */
void createMenuBar();
/** Create the toolbars */
void createToolBars();
/** Create system tray icon and notification */
void createTrayIcon(const NetworkStyle *networkStyle);
/** Create system tray menu (or setup the dock menu) */
void createTrayIconMenu();
/** Enable or disable all wallet-related actions */
void setWalletActionsEnabled(bool enabled);
/** Connect core signals to GUI client */
void subscribeToCoreSignals();
/** Disconnect core signals from GUI client */
void unsubscribeFromCoreSignals();
/** Update UI with latest network info from model. */
void updateNetworkState();
void updateHeadersSyncProgressLabel();
Q_SIGNALS:
/** Signal raised when a URI was entered or dragged to the GUI */
void receivedURI(const QString &uri);
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);
/** Set number of blocks and last block date shown in the UI */
void setNumBlocks(int count, const QDateTime &blockDate,
double nVerificationProgress, bool headers);
/** Notify the user of an event from the core network or transaction
handling code.
@param[in] title the message box / notification title
@param[in] message the displayed text
@param[in] style modality and style definitions (icon and used
buttons - buttons only for message boxes)
@see CClientUIInterface::MessageBoxFlags
@param[in] ret pointer to a bool that will be modified to whether
Ok was clicked (modal only)
*/
void message(const QString &title, const QString &message,
unsigned int style, bool *ret = nullptr);
#ifdef ENABLE_WALLET
bool setCurrentWallet(const QString &name);
+ bool setCurrentWalletBySelectorIndex(int index);
/** Set the UI status indicators based on the currently selected wallet.
*/
void updateWalletStatus();
private:
/** Set the encryption status as shown in the UI.
@param[in] status current encryption status
@see WalletModel::EncryptionStatus
*/
void setEncryptionStatus(int status);
/** Set the hd-enabled status as shown in the UI.
@param[in] status current hd enabled status
@see WalletModel::EncryptionStatus
*/
void setHDStatus(int hdEnabled);
public Q_SLOTS:
bool handlePaymentRequest(const SendCoinsRecipient &recipient);
/** Show incoming transaction notification for new transactions. */
void incomingTransaction(const QString &date, int unit, const Amount amount,
const QString &type, const QString &address,
const QString &label, const QString &walletName);
#endif // ENABLE_WALLET
private Q_SLOTS:
#ifdef ENABLE_WALLET
/** Switch to overview (home) page */
void gotoOverviewPage();
/** Switch to history (transactions) page */
void gotoHistoryPage();
/** Switch to receive coins page */
void gotoReceiveCoinsPage();
/** Switch to send coins page */
void gotoSendCoinsPage(QString addr = "");
/** Show Sign/Verify Message dialog and switch to sign message tab */
void gotoSignMessageTab(QString addr = "");
/** Show Sign/Verify Message dialog and switch to verify message tab */
void gotoVerifyMessageTab(QString addr = "");
/** Show open dialog */
void openClicked();
#endif // ENABLE_WALLET
/** Show configuration dialog */
void optionsClicked();
/** Show about dialog */
void aboutClicked();
/** Show debug window */
void showDebugWindow();
/** Show debug window and set focus to the console */
void showDebugWindowActivateConsole();
/** Show help message dialog */
void showHelpMessageClicked();
#ifndef Q_OS_MAC
/** Handle tray icon clicked */
void trayIconActivated(QSystemTrayIcon::ActivationReason reason);
#endif
/** Show window if hidden, unminimize when minimized, rise when obscured or
* show if hidden and fToggleHidden is true */
void showNormalIfMinimized(bool fToggleHidden = false);
/** Simply calls showNormalIfMinimized(true) for use in SLOT() macro */
void toggleHidden();
/** called by a timer to check if fRequestShutdown has been set **/
void detectShutdown();
/** Show progress dialog e.g. for verifychain */
void showProgress(const QString &title, int nProgress);
/** When hideTrayIcon setting is changed in OptionsModel hide or show the
* icon accordingly. */
void setTrayIconVisible(bool);
/** Toggle networking */
void toggleNetworkActive();
void showModalOverlay();
};
class UnitDisplayStatusBarControl : public QLabel {
Q_OBJECT
public:
explicit UnitDisplayStatusBarControl(const PlatformStyle *platformStyle);
/** Lets the control know about the Options Model (and its signals) */
void setOptionsModel(OptionsModel *optionsModel);
protected:
/** So that it responds to left-button clicks */
void mousePressEvent(QMouseEvent *event) override;
private:
OptionsModel *optionsModel;
QMenu *menu;
/** Shows context menu with Display Unit options by the mouse coordinates */
void onDisplayUnitsClicked(const QPoint &point);
/** Creates context menu, its actions, and wires up all the relevant signals
* for mouse events. */
void createContextMenu();
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);
/** Tells underlying optionsModel to update its current display unit. */
void onMenuSelection(QAction *action);
};
#endif // BITCOIN_QT_BITCOINGUI_H
diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp
index 47cf15b1f4..c4e0107e9b 100644
--- a/src/qt/rpcconsole.cpp
+++ b/src/qt/rpcconsole.cpp
@@ -1,1441 +1,1443 @@
// Copyright (c) 2011-2016 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#if defined(HAVE_CONFIG_H)
#include <config/bitcoin-config.h>
#endif
#include <qt/rpcconsole.h>
#include <chainparams.h>
#include <config.h>
#include <interfaces/node.h>
#include <netbase.h>
#include <qt/bantablemodel.h>
#include <qt/clientmodel.h>
#include <qt/forms/ui_debugwindow.h>
#include <qt/guiutil.h>
#include <qt/platformstyle.h>
#include <qt/walletmodel.h>
#include <rpc/client.h>
#include <rpc/server.h>
#include <util/system.h>
#ifdef ENABLE_WALLET
#include <wallet/wallet.h>
#include <db_cxx.h>
#endif
#include <QDesktopWidget>
#include <QKeyEvent>
#include <QMenu>
#include <QMessageBox>
#include <QScrollBar>
#include <QSettings>
#include <QSignalMapper>
#include <QStringList>
#include <QThread>
#include <QTime>
#include <QTimer>
#include <univalue.h>
// TODO: add a scrollback limit, as there is currently none
// TODO: make it possible to filter out categories (esp debug messages when
// implemented)
// TODO: receive errors and debug messages through ClientModel
const int CONSOLE_HISTORY = 50;
const int INITIAL_TRAFFIC_GRAPH_MINS = 30;
const QSize FONT_RANGE(4, 40);
const char fontSizeSettingsKey[] = "consoleFontSize";
const struct {
const char *url;
const char *source;
} ICON_MAPPING[] = {{"cmd-request", ":/icons/tx_input"},
{"cmd-reply", ":/icons/tx_output"},
{"cmd-error", ":/icons/tx_output"},
{"misc", ":/icons/tx_inout"},
{nullptr, nullptr}};
namespace {
// don't add private key handling cmd's to the history
const QStringList historyFilter = QStringList() << "importprivkey"
<< "importmulti"
<< "sethdseed"
<< "signmessagewithprivkey"
<< "signrawtransactionwithkey"
<< "walletpassphrase"
<< "walletpassphrasechange"
<< "encryptwallet";
} // namespace
/* Object for executing console RPC commands in a separate thread.
*/
class RPCExecutor : public QObject {
Q_OBJECT
public:
RPCExecutor(interfaces::Node &node) : m_node(node) {}
public Q_SLOTS:
void request(const QString &command, const QString &walletID);
Q_SIGNALS:
void reply(int category, const QString &command);
private:
interfaces::Node &m_node;
};
/** Class for handling RPC timers
* (used for e.g. re-locking the wallet after a timeout)
*/
class QtRPCTimerBase : public QObject, public RPCTimerBase {
Q_OBJECT
public:
QtRPCTimerBase(std::function<void()> &_func, int64_t millis) : func(_func) {
timer.setSingleShot(true);
connect(&timer, SIGNAL(timeout()), this, SLOT(timeout()));
timer.start(millis);
}
~QtRPCTimerBase() {}
private Q_SLOTS:
void timeout() { func(); }
private:
QTimer timer;
std::function<void()> func;
};
class QtRPCTimerInterface : public RPCTimerInterface {
public:
~QtRPCTimerInterface() {}
const char *Name() override { return "Qt"; }
RPCTimerBase *NewTimer(std::function<void()> &func,
int64_t millis) override {
return new QtRPCTimerBase(func, millis);
}
};
#include <qt/rpcconsole.moc>
/**
* Split shell command line into a list of arguments and optionally execute the
* command(s).
* Aims to emulate \c bash and friends.
*
* - Command nesting is possible with parenthesis; for example:
* validateaddress(getnewaddress())
* - Arguments are delimited with whitespace or comma
* - Extra whitespace at the beginning and end and between arguments will be
* ignored
* - Text can be "double" or 'single' quoted
* - The backslash \c \ is used as escape character
* - Outside quotes, any character can be escaped
* - Within double quotes, only escape \c " and backslashes before a \c " or
* another backslash
* - Within single quotes, no escaping is possible and no special
* interpretation takes place
*
* @param[in] node optional node to execute command on
* @param[out] result stringified Result from the executed command(chain)
* @param[in] strCommand Command line to split
* @param[in] fExecute set true if you want the command to be executed
* @param[out] pstrFilteredOut Command line, filtered to remove any sensitive
* data
*/
bool RPCConsole::RPCParseCommandLine(interfaces::Node *node,
std::string &strResult,
const std::string &strCommand,
const bool fExecute,
std::string *const pstrFilteredOut,
const std::string *walletID) {
std::vector<std::vector<std::string>> stack;
stack.push_back(std::vector<std::string>());
enum CmdParseState {
STATE_EATING_SPACES,
STATE_EATING_SPACES_IN_ARG,
STATE_EATING_SPACES_IN_BRACKETS,
STATE_ARGUMENT,
STATE_SINGLEQUOTED,
STATE_DOUBLEQUOTED,
STATE_ESCAPE_OUTER,
STATE_ESCAPE_DOUBLEQUOTED,
STATE_COMMAND_EXECUTED,
STATE_COMMAND_EXECUTED_INNER
} state = STATE_EATING_SPACES;
std::string curarg;
UniValue lastResult;
unsigned nDepthInsideSensitive = 0;
size_t filter_begin_pos = 0, chpos;
std::vector<std::pair<size_t, size_t>> filter_ranges;
auto add_to_current_stack = [&](const std::string &strArg) {
if (stack.back().empty() && (!nDepthInsideSensitive) &&
historyFilter.contains(QString::fromStdString(strArg),
Qt::CaseInsensitive)) {
nDepthInsideSensitive = 1;
filter_begin_pos = chpos;
}
// Make sure stack is not empty before adding something
if (stack.empty()) {
stack.push_back(std::vector<std::string>());
}
stack.back().push_back(strArg);
};
auto close_out_params = [&]() {
if (nDepthInsideSensitive) {
if (!--nDepthInsideSensitive) {
assert(filter_begin_pos);
filter_ranges.push_back(
std::make_pair(filter_begin_pos, chpos));
filter_begin_pos = 0;
}
}
stack.pop_back();
};
std::string strCommandTerminated = strCommand;
if (strCommandTerminated.back() != '\n') strCommandTerminated += "\n";
for (chpos = 0; chpos < strCommandTerminated.size(); ++chpos) {
char ch = strCommandTerminated[chpos];
switch (state) {
case STATE_COMMAND_EXECUTED_INNER:
case STATE_COMMAND_EXECUTED: {
bool breakParsing = true;
switch (ch) {
case '[':
curarg.clear();
state = STATE_COMMAND_EXECUTED_INNER;
break;
default:
if (state == STATE_COMMAND_EXECUTED_INNER) {
if (ch != ']') {
// append char to the current argument (which is
// also used for the query command)
curarg += ch;
break;
}
if (curarg.size() && fExecute) {
// if we have a value query, query arrays with
// index and objects with a string key
UniValue subelement;
if (lastResult.isArray()) {
for (char argch : curarg) {
if (!std::isdigit(argch)) {
throw std::runtime_error(
"Invalid result query");
}
}
subelement =
lastResult[atoi(curarg.c_str())];
} else if (lastResult.isObject()) {
subelement = find_value(lastResult, curarg);
} else {
// no array or object: abort
throw std::runtime_error(
"Invalid result query");
}
lastResult = subelement;
}
state = STATE_COMMAND_EXECUTED;
break;
}
// don't break parsing when the char is required for the
// next argument
breakParsing = false;
// pop the stack and return the result to the current
// command arguments
close_out_params();
// don't stringify the json in case of a string to avoid
// doublequotes
if (lastResult.isStr()) {
curarg = lastResult.get_str();
} else {
curarg = lastResult.write(2);
}
// if we have a non empty result, use it as stack
// argument otherwise as general result
if (curarg.size()) {
if (stack.size()) {
add_to_current_stack(curarg);
} else {
strResult = curarg;
}
}
curarg.clear();
// assume eating space state
state = STATE_EATING_SPACES;
}
if (breakParsing) {
break;
}
}
// FALLTHROUGH
case STATE_ARGUMENT: // In or after argument
case STATE_EATING_SPACES_IN_ARG:
case STATE_EATING_SPACES_IN_BRACKETS:
case STATE_EATING_SPACES: // Handle runs of whitespace
switch (ch) {
case '"':
state = STATE_DOUBLEQUOTED;
break;
case '\'':
state = STATE_SINGLEQUOTED;
break;
case '\\':
state = STATE_ESCAPE_OUTER;
break;
case '(':
case ')':
case '\n':
if (state == STATE_EATING_SPACES_IN_ARG) {
throw std::runtime_error("Invalid Syntax");
}
if (state == STATE_ARGUMENT) {
if (ch == '(' && stack.size() &&
stack.back().size() > 0) {
if (nDepthInsideSensitive) {
++nDepthInsideSensitive;
}
stack.push_back(std::vector<std::string>());
}
// don't allow commands after executed commands on
// baselevel
if (!stack.size()) {
throw std::runtime_error("Invalid Syntax");
}
add_to_current_stack(curarg);
curarg.clear();
state = STATE_EATING_SPACES_IN_BRACKETS;
}
if ((ch == ')' || ch == '\n') && stack.size() > 0) {
if (fExecute) {
// Convert argument list to JSON objects in
// method-dependent way, and pass it along with
// the method name to the dispatcher.
UniValue params = RPCConvertValues(
stack.back()[0],
std::vector<std::string>(
stack.back().begin() + 1,
stack.back().end()));
std::string method = stack.back()[0];
std::string uri;
#ifdef ENABLE_WALLET
if (walletID) {
QByteArray encodedName =
QUrl::toPercentEncoding(
QString::fromStdString(*walletID));
uri = "/wallet/" +
std::string(encodedName.constData(),
encodedName.length());
}
#endif
GlobalConfig config;
assert(node);
lastResult = node->executeRpc(config, method,
params, uri);
}
state = STATE_COMMAND_EXECUTED;
curarg.clear();
}
break;
case ' ':
case ',':
case '\t':
if (state == STATE_EATING_SPACES_IN_ARG &&
curarg.empty() && ch == ',') {
throw std::runtime_error("Invalid Syntax");
} else if (state == STATE_ARGUMENT) {
// Space ends argument
add_to_current_stack(curarg);
curarg.clear();
}
if ((state == STATE_EATING_SPACES_IN_BRACKETS ||
state == STATE_ARGUMENT) &&
ch == ',') {
state = STATE_EATING_SPACES_IN_ARG;
break;
}
state = STATE_EATING_SPACES;
break;
default:
curarg += ch;
state = STATE_ARGUMENT;
}
break;
case STATE_SINGLEQUOTED: // Single-quoted string
switch (ch) {
case '\'':
state = STATE_ARGUMENT;
break;
default:
curarg += ch;
}
break;
case STATE_DOUBLEQUOTED: // Double-quoted string
switch (ch) {
case '"':
state = STATE_ARGUMENT;
break;
case '\\':
state = STATE_ESCAPE_DOUBLEQUOTED;
break;
default:
curarg += ch;
}
break;
case STATE_ESCAPE_OUTER: // '\' outside quotes
curarg += ch;
state = STATE_ARGUMENT;
break;
case STATE_ESCAPE_DOUBLEQUOTED: // '\' in double-quoted text
if (ch != '"' && ch != '\\') {
// keep '\' for everything but the quote and '\' itself
curarg += '\\';
}
curarg += ch;
state = STATE_DOUBLEQUOTED;
break;
}
}
if (pstrFilteredOut) {
if (STATE_COMMAND_EXECUTED == state) {
assert(!stack.empty());
close_out_params();
}
*pstrFilteredOut = strCommand;
for (auto i = filter_ranges.rbegin(); i != filter_ranges.rend(); ++i) {
pstrFilteredOut->replace(i->first, i->second - i->first, "(…)");
}
}
// final state
switch (state) {
case STATE_COMMAND_EXECUTED:
if (lastResult.isStr()) {
strResult = lastResult.get_str();
} else {
strResult = lastResult.write(2);
}
// FALLTHROUGH
case STATE_ARGUMENT:
case STATE_EATING_SPACES:
return true;
default: // ERROR to end in one of the other states
return false;
}
}
void RPCExecutor::request(const QString &command, const QString &walletID) {
try {
std::string result;
std::string executableCommand = command.toStdString() + "\n";
// Catch the console-only-help command before RPC call is executed and
// reply with help text as-if a RPC reply.
if (executableCommand == "help-console\n") {
Q_EMIT reply(
RPCConsole::CMD_REPLY,
QString(("\n"
"This console accepts RPC commands using the standard "
"syntax.\n"
" example: getblockhash 0\n\n"
"This console can also accept RPC commands using "
"parenthesized syntax.\n"
" example: getblockhash(0)\n\n"
"Commands may be nested when specified with the "
"parenthesized syntax.\n"
" example: getblock(getblockhash(0) 1)\n\n"
"A space or a comma can be used to delimit arguments "
"for either syntax.\n"
" example: getblockhash 0\n"
" getblockhash,0\n\n"
"Named results can be queried with a non-quoted key "
"string in brackets.\n"
" example: getblock(getblockhash(0) true)[tx]\n\n"
"Results without keys can be queried using an integer "
"in brackets.\n"
" example: "
"getblock(getblockhash(0),true)[tx][0]\n\n")));
return;
}
std::string wallet_id = walletID.toStdString();
if (!RPCConsole::RPCExecuteCommandLine(
m_node, result, executableCommand, nullptr,
walletID.isNull() ? nullptr : &wallet_id)) {
Q_EMIT reply(RPCConsole::CMD_ERROR,
QString("Parse error: unbalanced ' or \""));
return;
}
Q_EMIT reply(RPCConsole::CMD_REPLY, QString::fromStdString(result));
} catch (UniValue &objError) {
// Nice formatting for standard-format error
try {
int code = find_value(objError, "code").get_int();
std::string message = find_value(objError, "message").get_str();
Q_EMIT reply(RPCConsole::CMD_ERROR,
QString::fromStdString(message) + " (code " +
QString::number(code) + ")");
} catch (const std::runtime_error &) {
// raised when converting to invalid type, i.e. missing code or
// message. Show raw JSON object.
Q_EMIT reply(RPCConsole::CMD_ERROR,
QString::fromStdString(objError.write()));
}
} catch (const std::exception &e) {
Q_EMIT reply(RPCConsole::CMD_ERROR,
QString("Error: ") + QString::fromStdString(e.what()));
}
}
RPCConsole::RPCConsole(interfaces::Node &node,
const PlatformStyle *_platformStyle, QWidget *parent)
: QWidget(parent), m_node(node), ui(new Ui::RPCConsole),
platformStyle(_platformStyle) {
ui->setupUi(this);
QSettings settings;
if (!restoreGeometry(
settings.value("RPCConsoleWindowGeometry").toByteArray())) {
// Restore failed (perhaps missing setting), center the window
move(QApplication::desktop()->availableGeometry().center() -
frameGeometry().center());
}
QChar nonbreaking_hyphen(8209);
ui->dataDir->setToolTip(
ui->dataDir->toolTip().arg(QString(nonbreaking_hyphen) + "datadir"));
ui->blocksDir->setToolTip(ui->blocksDir->toolTip().arg(
QString(nonbreaking_hyphen) + "blocksdir"));
ui->openDebugLogfileButton->setToolTip(
ui->openDebugLogfileButton->toolTip().arg(tr(PACKAGE_NAME)));
if (platformStyle->getImagesOnButtons()) {
ui->openDebugLogfileButton->setIcon(
platformStyle->SingleColorIcon(":/icons/export"));
}
ui->clearButton->setIcon(platformStyle->SingleColorIcon(":/icons/remove"));
ui->fontBiggerButton->setIcon(
platformStyle->SingleColorIcon(":/icons/fontbigger"));
ui->fontSmallerButton->setIcon(
platformStyle->SingleColorIcon(":/icons/fontsmaller"));
// Install event filter for up and down arrow
ui->lineEdit->installEventFilter(this);
ui->messagesWidget->installEventFilter(this);
connect(ui->clearButton, SIGNAL(clicked()), this, SLOT(clear()));
connect(ui->fontBiggerButton, SIGNAL(clicked()), this, SLOT(fontBigger()));
connect(ui->fontSmallerButton, SIGNAL(clicked()), this,
SLOT(fontSmaller()));
connect(ui->btnClearTrafficGraph, SIGNAL(clicked()), ui->trafficGraph,
SLOT(clear()));
// disable the wallet selector by default
ui->WalletSelector->setVisible(false);
ui->WalletSelectorLabel->setVisible(false);
// set library version labels
#ifdef ENABLE_WALLET
ui->berkeleyDBVersion->setText(DbEnv::version(0, 0, 0));
#else
ui->label_berkeleyDBVersion->hide();
ui->berkeleyDBVersion->hide();
#endif
// Register RPC timer interface
rpcTimerInterface = new QtRPCTimerInterface();
// avoid accidentally overwriting an existing, non QTThread
// based timer interface
m_node.rpcSetTimerInterfaceIfUnset(rpcTimerInterface);
setTrafficGraphRange(INITIAL_TRAFFIC_GRAPH_MINS);
ui->detailWidget->hide();
ui->peerHeading->setText(tr("Select a peer to view detailed information."));
consoleFontSize =
settings.value(fontSizeSettingsKey, QFontInfo(QFont()).pointSize())
.toInt();
clear();
}
RPCConsole::~RPCConsole() {
QSettings settings;
settings.setValue("RPCConsoleWindowGeometry", saveGeometry());
m_node.rpcUnsetTimerInterface(rpcTimerInterface);
delete rpcTimerInterface;
delete ui;
}
bool RPCConsole::eventFilter(QObject *obj, QEvent *event) {
// Special key handling
if (event->type() == QEvent::KeyPress) {
QKeyEvent *keyevt = static_cast<QKeyEvent *>(event);
int key = keyevt->key();
Qt::KeyboardModifiers mod = keyevt->modifiers();
switch (key) {
case Qt::Key_Up:
if (obj == ui->lineEdit) {
browseHistory(-1);
return true;
}
break;
case Qt::Key_Down:
if (obj == ui->lineEdit) {
browseHistory(1);
return true;
}
break;
case Qt::Key_PageUp: /* pass paging keys to messages widget */
case Qt::Key_PageDown:
if (obj == ui->lineEdit) {
QApplication::postEvent(ui->messagesWidget,
new QKeyEvent(*keyevt));
return true;
}
break;
case Qt::Key_Return:
case Qt::Key_Enter:
// forward these events to lineEdit
if (obj == autoCompleter->popup()) {
QApplication::postEvent(ui->lineEdit,
new QKeyEvent(*keyevt));
return true;
}
break;
default:
// Typing in messages widget brings focus to line edit, and
// redirects key there. Exclude most combinations and keys that
// emit no text, except paste shortcuts.
if (obj == ui->messagesWidget &&
((!mod && !keyevt->text().isEmpty() &&
key != Qt::Key_Tab) ||
((mod & Qt::ControlModifier) && key == Qt::Key_V) ||
((mod & Qt::ShiftModifier) && key == Qt::Key_Insert))) {
ui->lineEdit->setFocus();
QApplication::postEvent(ui->lineEdit,
new QKeyEvent(*keyevt));
return true;
}
}
}
return QWidget::eventFilter(obj, event);
}
void RPCConsole::setClientModel(ClientModel *model) {
clientModel = model;
ui->trafficGraph->setClientModel(model);
if (model && clientModel->getPeerTableModel() &&
clientModel->getBanTableModel()) {
// Keep up to date with client
setNumConnections(model->getNumConnections());
connect(model, SIGNAL(numConnectionsChanged(int)), this,
SLOT(setNumConnections(int)));
interfaces::Node &node = clientModel->node();
setNumBlocks(node.getNumBlocks(),
QDateTime::fromTime_t(node.getLastBlockTime()),
node.getVerificationProgress(), false);
connect(model, SIGNAL(numBlocksChanged(int, QDateTime, double, bool)),
this, SLOT(setNumBlocks(int, QDateTime, double, bool)));
updateNetworkState();
connect(model, SIGNAL(networkActiveChanged(bool)), this,
SLOT(setNetworkActive(bool)));
updateTrafficStats(node.getTotalBytesRecv(), node.getTotalBytesSent());
connect(model, SIGNAL(bytesChanged(quint64, quint64)), this,
SLOT(updateTrafficStats(quint64, quint64)));
connect(model, SIGNAL(mempoolSizeChanged(long, size_t)), this,
SLOT(setMempoolSize(long, size_t)));
// set up peer table
ui->peerWidget->setModel(model->getPeerTableModel());
ui->peerWidget->verticalHeader()->hide();
ui->peerWidget->setEditTriggers(QAbstractItemView::NoEditTriggers);
ui->peerWidget->setSelectionBehavior(QAbstractItemView::SelectRows);
ui->peerWidget->setSelectionMode(QAbstractItemView::ExtendedSelection);
ui->peerWidget->setContextMenuPolicy(Qt::CustomContextMenu);
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);
// create peer table context menu actions
QAction *disconnectAction = new QAction(tr("&Disconnect"), this);
QAction *banAction1h =
new QAction(tr("Ban for") + " " + tr("1 &hour"), this);
QAction *banAction24h =
new QAction(tr("Ban for") + " " + tr("1 &day"), this);
QAction *banAction7d =
new QAction(tr("Ban for") + " " + tr("1 &week"), this);
QAction *banAction365d =
new QAction(tr("Ban for") + " " + tr("1 &year"), this);
// create peer table context menu
peersTableContextMenu = new QMenu(this);
peersTableContextMenu->addAction(disconnectAction);
peersTableContextMenu->addAction(banAction1h);
peersTableContextMenu->addAction(banAction24h);
peersTableContextMenu->addAction(banAction7d);
peersTableContextMenu->addAction(banAction365d);
// Add a signal mapping to allow dynamic context menu arguments. We need
// to use int (instead of int64_t), because signal mapper only supports
// int or objects, which is okay because max bantime (1 year) is <
// int_max.
QSignalMapper *signalMapper = new QSignalMapper(this);
signalMapper->setMapping(banAction1h, 60 * 60);
signalMapper->setMapping(banAction24h, 60 * 60 * 24);
signalMapper->setMapping(banAction7d, 60 * 60 * 24 * 7);
signalMapper->setMapping(banAction365d, 60 * 60 * 24 * 365);
connect(banAction1h, SIGNAL(triggered()), signalMapper, SLOT(map()));
connect(banAction24h, SIGNAL(triggered()), signalMapper, SLOT(map()));
connect(banAction7d, SIGNAL(triggered()), signalMapper, SLOT(map()));
connect(banAction365d, SIGNAL(triggered()), signalMapper, SLOT(map()));
connect(signalMapper, SIGNAL(mapped(int)), this,
SLOT(banSelectedNode(int)));
// peer table context menu signals
connect(ui->peerWidget,
SIGNAL(customContextMenuRequested(const QPoint &)), this,
SLOT(showPeersTableContextMenu(const QPoint &)));
connect(disconnectAction, SIGNAL(triggered()), this,
SLOT(disconnectSelectedNode()));
// peer table signal handling - update peer details when selecting new
// node
connect(
ui->peerWidget->selectionModel(),
SIGNAL(selectionChanged(const QItemSelection &,
const QItemSelection &)),
this,
SLOT(peerSelected(const QItemSelection &, const QItemSelection &)));
// peer table signal handling - update peer details when new nodes are
// added to the model
connect(model->getPeerTableModel(), SIGNAL(layoutChanged()), this,
SLOT(peerLayoutChanged()));
// peer table signal handling - cache selected node ids
connect(model->getPeerTableModel(), SIGNAL(layoutAboutToBeChanged()),
this, SLOT(peerLayoutAboutToChange()));
// set up ban table
ui->banlistWidget->setModel(model->getBanTableModel());
ui->banlistWidget->verticalHeader()->hide();
ui->banlistWidget->setEditTriggers(QAbstractItemView::NoEditTriggers);
ui->banlistWidget->setSelectionBehavior(QAbstractItemView::SelectRows);
ui->banlistWidget->setSelectionMode(QAbstractItemView::SingleSelection);
ui->banlistWidget->setContextMenuPolicy(Qt::CustomContextMenu);
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 action
QAction *unbanAction = new QAction(tr("&Unban"), this);
// create ban table context menu
banTableContextMenu = new QMenu(this);
banTableContextMenu->addAction(unbanAction);
// ban table context menu signals
connect(ui->banlistWidget,
SIGNAL(customContextMenuRequested(const QPoint &)), this,
SLOT(showBanTableContextMenu(const QPoint &)));
connect(unbanAction, SIGNAL(triggered()), this,
SLOT(unbanSelectedNode()));
// ban table signal handling - clear peer details when clicking a peer
// in the ban table
connect(ui->banlistWidget, SIGNAL(clicked(const QModelIndex &)), this,
SLOT(clearSelectedNode()));
// ban table signal handling - ensure ban table is shown or hidden (if
// empty)
connect(model->getBanTableModel(), SIGNAL(layoutChanged()), this,
SLOT(showOrHideBanTableIfRequired()));
showOrHideBanTableIfRequired();
// Provide initial values
ui->clientVersion->setText(model->formatFullVersion());
ui->clientUserAgent->setText(model->formatSubVersion());
ui->dataDir->setText(model->dataDir());
ui->blocksDir->setText(model->blocksDir());
ui->startupTime->setText(model->formatClientStartupTime());
ui->networkName->setText(
QString::fromStdString(Params().NetworkIDString()));
// Setup autocomplete and attach it
QStringList wordList;
std::vector<std::string> commandList = m_node.listRpcCommands();
for (size_t i = 0; i < commandList.size(); ++i) {
wordList << commandList[i].c_str();
wordList << ("help " + commandList[i]).c_str();
}
wordList << "help-console";
wordList.sort();
autoCompleter = new QCompleter(wordList, this);
autoCompleter->setModelSorting(QCompleter::CaseSensitivelySortedModel);
ui->lineEdit->setCompleter(autoCompleter);
autoCompleter->popup()->installEventFilter(this);
// Start thread to execute RPC commands.
startExecutor();
}
if (!model) {
// Client model is being set to 0, this means shutdown() is about to be
// called. Make sure we clean up the executor thread
Q_EMIT stopExecutor();
thread.wait();
}
}
#ifdef ENABLE_WALLET
void RPCConsole::addWallet(WalletModel *const walletModel) {
const QString name = walletModel->getWalletName();
// use name for text and internal data object (to allow to move to a wallet
// id later)
- ui->WalletSelector->addItem(name, name);
+ QString display_name =
+ name.isEmpty() ? "[" + tr("default wallet") + "]" : name;
+ ui->WalletSelector->addItem(display_name, name);
if (ui->WalletSelector->count() == 2 && !isVisible()) {
// First wallet added, set to default so long as the window isn't
// presently visible (and potentially in use)
ui->WalletSelector->setCurrentIndex(1);
}
if (ui->WalletSelector->count() > 2) {
ui->WalletSelector->setVisible(true);
ui->WalletSelectorLabel->setVisible(true);
}
}
#endif
static QString categoryClass(int category) {
switch (category) {
case RPCConsole::CMD_REQUEST:
return "cmd-request";
break;
case RPCConsole::CMD_REPLY:
return "cmd-reply";
break;
case RPCConsole::CMD_ERROR:
return "cmd-error";
break;
default:
return "misc";
}
}
void RPCConsole::fontBigger() {
setFontSize(consoleFontSize + 1);
}
void RPCConsole::fontSmaller() {
setFontSize(consoleFontSize - 1);
}
void RPCConsole::setFontSize(int newSize) {
QSettings settings;
// don't allow an insane font size
if (newSize < FONT_RANGE.width() || newSize > FONT_RANGE.height()) return;
// temp. store the console content
QString str = ui->messagesWidget->toHtml();
// replace font tags size in current content
str.replace(QString("font-size:%1pt").arg(consoleFontSize),
QString("font-size:%1pt").arg(newSize));
// store the new font size
consoleFontSize = newSize;
settings.setValue(fontSizeSettingsKey, consoleFontSize);
// clear console (reset icon sizes, default stylesheet) and re-add the
// content
float oldPosFactor = 1.0 /
ui->messagesWidget->verticalScrollBar()->maximum() *
ui->messagesWidget->verticalScrollBar()->value();
clear(false);
ui->messagesWidget->setHtml(str);
ui->messagesWidget->verticalScrollBar()->setValue(
oldPosFactor * ui->messagesWidget->verticalScrollBar()->maximum());
}
void RPCConsole::clear(bool clearHistory) {
ui->messagesWidget->clear();
if (clearHistory) {
history.clear();
historyPtr = 0;
}
ui->lineEdit->clear();
ui->lineEdit->setFocus();
// Add smoothly scaled icon images.
// (when using width/height on an img, Qt uses nearest instead of linear
// interpolation)
for (int i = 0; ICON_MAPPING[i].url; ++i) {
ui->messagesWidget->document()->addResource(
QTextDocument::ImageResource, QUrl(ICON_MAPPING[i].url),
platformStyle->SingleColorImage(ICON_MAPPING[i].source)
.scaled(QSize(consoleFontSize * 2, consoleFontSize * 2),
Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
}
// Set default style sheet
QFontInfo fixedFontInfo(GUIUtil::fixedPitchFont());
ui->messagesWidget->document()->setDefaultStyleSheet(
QString("table { }"
"td.time { color: #808080; font-size: %2; padding-top: 3px; } "
"td.message { font-family: %1; font-size: %2; "
"white-space:pre-wrap; } "
"td.cmd-request { color: #006060; } "
"td.cmd-error { color: red; } "
".secwarning { color: red; }"
"b { color: #006060; } ")
.arg(fixedFontInfo.family(), QString("%1pt").arg(consoleFontSize)));
#ifdef Q_OS_MAC
QString clsKey = "(⌘)-L";
#else
QString clsKey = "Ctrl-L";
#endif
message(CMD_REPLY,
(tr("Welcome to the %1 RPC console.").arg(tr(PACKAGE_NAME)) +
"<br>" +
tr("Use up and down arrows to navigate history, and "
"%1 to clear screen.")
.arg("<b>" + clsKey + "</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);
}
void RPCConsole::keyPressEvent(QKeyEvent *event) {
if (windowType() != Qt::Widget && event->key() == Qt::Key_Escape) {
close();
}
}
void RPCConsole::message(int category, const QString &message, bool html) {
QTime time = QTime::currentTime();
QString timeString = time.toString();
QString out;
out += "<table><tr><td class=\"time\" width=\"65\">" + timeString + "</td>";
out += "<td class=\"icon\" width=\"32\"><img src=\"" +
categoryClass(category) + "\"></td>";
out += "<td class=\"message " + categoryClass(category) +
"\" valign=\"middle\">";
if (html) {
out += message;
} else {
out += GUIUtil::HtmlEscape(message, false);
}
out += "</td></tr></table>";
ui->messagesWidget->append(out);
}
void RPCConsole::updateNetworkState() {
QString connections =
QString::number(clientModel->getNumConnections()) + " (";
connections +=
tr("In:") + " " +
QString::number(clientModel->getNumConnections(CONNECTIONS_IN)) + " / ";
connections +=
tr("Out:") + " " +
QString::number(clientModel->getNumConnections(CONNECTIONS_OUT)) + ")";
if (!clientModel->node().getNetworkActive()) {
connections += " (" + tr("Network activity disabled") + ")";
}
ui->numberOfConnections->setText(connections);
}
void RPCConsole::setNumConnections(int count) {
if (!clientModel) {
return;
}
updateNetworkState();
}
void RPCConsole::setNetworkActive(bool networkActive) {
updateNetworkState();
}
void RPCConsole::setNumBlocks(int count, const QDateTime &blockDate,
double nVerificationProgress, bool headers) {
if (!headers) {
ui->numberOfBlocks->setText(QString::number(count));
ui->lastBlockTime->setText(blockDate.toString());
}
}
void RPCConsole::setMempoolSize(long numberOfTxs, size_t dynUsage) {
ui->mempoolNumberTxs->setText(QString::number(numberOfTxs));
if (dynUsage < 1000000) {
ui->mempoolSize->setText(QString::number(dynUsage / 1000.0, 'f', 2) +
" KB");
} else {
ui->mempoolSize->setText(QString::number(dynUsage / 1000000.0, 'f', 2) +
" MB");
}
}
void RPCConsole::on_lineEdit_returnPressed() {
QString cmd = ui->lineEdit->text();
if (!cmd.isEmpty()) {
std::string strFilteredCmd;
try {
std::string dummy;
if (!RPCParseCommandLine(nullptr, dummy, cmd.toStdString(), false,
&strFilteredCmd)) {
// Failed to parse command, so we cannot even filter it for the
// history
throw std::runtime_error("Invalid command line");
}
} catch (const std::exception &e) {
QMessageBox::critical(this, "Error",
QString("Error: ") +
QString::fromStdString(e.what()));
return;
}
ui->lineEdit->clear();
cmdBeforeBrowsing = QString();
QString walletID;
#ifdef ENABLE_WALLET
const int wallet_index = ui->WalletSelector->currentIndex();
if (wallet_index > 0) {
walletID = (QString)ui->WalletSelector->itemData(wallet_index)
.value<QString>();
}
if (m_last_wallet_id != walletID) {
if (walletID.isNull()) {
message(CMD_REQUEST,
tr("Executing command without any wallet"));
} else {
message(
CMD_REQUEST,
tr("Executing command using \"%1\" wallet").arg(walletID));
}
m_last_wallet_id = walletID;
}
#endif
message(CMD_REQUEST, QString::fromStdString(strFilteredCmd));
Q_EMIT cmdRequest(cmd, walletID);
cmd = QString::fromStdString(strFilteredCmd);
// Remove command, if already in history
history.removeOne(cmd);
// Append command to history
history.append(cmd);
// Enforce maximum history size
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) {
// store current text when start browsing through the history
if (historyPtr == history.size()) {
cmdBeforeBrowsing = ui->lineEdit->text();
}
historyPtr += offset;
if (historyPtr < 0) {
historyPtr = 0;
}
if (historyPtr > history.size()) {
historyPtr = history.size();
}
QString cmd;
if (historyPtr < history.size()) {
cmd = history.at(historyPtr);
} else if (!cmdBeforeBrowsing.isNull()) {
cmd = cmdBeforeBrowsing;
}
ui->lineEdit->setText(cmd);
}
void RPCConsole::startExecutor() {
RPCExecutor *executor = new RPCExecutor(m_node);
executor->moveToThread(&thread);
// Replies from executor object must go to this object
connect(executor, SIGNAL(reply(int, QString)), this,
SLOT(message(int, QString)));
// Requests from this object must go to executor
connect(this, SIGNAL(cmdRequest(QString, QString)), executor,
SLOT(request(QString, QString)));
// On stopExecutor signal
// - quit the Qt event loop in the execution thread
connect(this, SIGNAL(stopExecutor()), &thread, SLOT(quit()));
// - queue executor for deletion (in execution thread)
connect(&thread, SIGNAL(finished()), executor, SLOT(deleteLater()),
Qt::DirectConnection);
// Default implementation of QThread::run() simply spins up an event loop in
// the thread, which is what we want.
thread.start();
}
void RPCConsole::on_tabWidget_currentChanged(int index) {
if (ui->tabWidget->widget(index) == ui->tab_console) {
ui->lineEdit->setFocus();
} else if (ui->tabWidget->widget(index) != ui->tab_peers) {
clearSelectedNode();
}
}
void RPCConsole::on_openDebugLogfileButton_clicked() {
GUIUtil::openDebugLogfile();
}
void RPCConsole::scrollToEnd() {
QScrollBar *scrollbar = ui->messagesWidget->verticalScrollBar();
scrollbar->setValue(scrollbar->maximum());
}
void RPCConsole::on_sldGraphRange_valueChanged(int value) {
const int multiplier = 5; // each position on the slider represents 5 min
int mins = value * multiplier;
setTrafficGraphRange(mins);
}
void RPCConsole::setTrafficGraphRange(int mins) {
ui->trafficGraph->setGraphRangeMins(mins);
ui->lblGraphRange->setText(GUIUtil::formatDurationStr(mins * 60));
}
void RPCConsole::updateTrafficStats(quint64 totalBytesIn,
quint64 totalBytesOut) {
ui->lblBytesIn->setText(GUIUtil::formatBytes(totalBytesIn));
ui->lblBytesOut->setText(GUIUtil::formatBytes(totalBytesOut));
}
void RPCConsole::peerSelected(const QItemSelection &selected,
const QItemSelection &deselected) {
Q_UNUSED(deselected);
if (!clientModel || !clientModel->getPeerTableModel() ||
selected.indexes().isEmpty())
return;
const CNodeCombinedStats *stats =
clientModel->getPeerTableModel()->getNodeStats(
selected.indexes().first().row());
if (stats) updateNodeDetail(stats);
}
void RPCConsole::peerLayoutAboutToChange() {
QModelIndexList selected =
ui->peerWidget->selectionModel()->selectedIndexes();
cachedNodeids.clear();
for (int i = 0; i < selected.size(); i++) {
const CNodeCombinedStats *stats =
clientModel->getPeerTableModel()->getNodeStats(
selected.at(i).row());
cachedNodeids.append(stats->nodeStats.nodeid);
}
}
void RPCConsole::peerLayoutChanged() {
if (!clientModel || !clientModel->getPeerTableModel()) {
return;
}
const CNodeCombinedStats *stats = nullptr;
bool fUnselect = false;
bool fReselect = false;
if (cachedNodeids.empty()) // no node selected yet
return;
// find the currently selected row
int selectedRow = -1;
QModelIndexList selectedModelIndex =
ui->peerWidget->selectionModel()->selectedIndexes();
if (!selectedModelIndex.isEmpty()) {
selectedRow = selectedModelIndex.first().row();
}
// check if our detail node has a row in the table (it may not necessarily
// be at selectedRow since its position can change after a layout change)
int detailNodeRow =
clientModel->getPeerTableModel()->getRowByNodeId(cachedNodeids.first());
if (detailNodeRow < 0) {
// detail node disappeared from table (node disconnected)
fUnselect = true;
} else {
if (detailNodeRow != selectedRow) {
// detail node moved position
fUnselect = true;
fReselect = true;
}
// get fresh stats on the detail node.
stats = clientModel->getPeerTableModel()->getNodeStats(detailNodeRow);
}
if (fUnselect && selectedRow >= 0) {
clearSelectedNode();
}
if (fReselect) {
for (int i = 0; i < cachedNodeids.size(); i++) {
ui->peerWidget->selectRow(
clientModel->getPeerTableModel()->getRowByNodeId(
cachedNodeids.at(i)));
}
}
if (stats) updateNodeDetail(stats);
}
void RPCConsole::updateNodeDetail(const CNodeCombinedStats *stats) {
// update the detail ui with latest node information
QString peerAddrDetails(QString::fromStdString(stats->nodeStats.addrName) +
" ");
peerAddrDetails +=
tr("(node id: %1)").arg(QString::number(stats->nodeStats.nodeid));
if (!stats->nodeStats.addrLocal.empty()) {
peerAddrDetails += "<br />" + tr("via %1").arg(QString::fromStdString(
stats->nodeStats.addrLocal));
}
ui->peerHeading->setText(peerAddrDetails);
ui->peerServices->setText(
GUIUtil::formatServicesStr(stats->nodeStats.nServices));
ui->peerLastSend->setText(
stats->nodeStats.nLastSend
? GUIUtil::formatDurationStr(GetSystemTimeInSeconds() -
stats->nodeStats.nLastSend)
: tr("never"));
ui->peerLastRecv->setText(
stats->nodeStats.nLastRecv
? GUIUtil::formatDurationStr(GetSystemTimeInSeconds() -
stats->nodeStats.nLastRecv)
: tr("never"));
ui->peerBytesSent->setText(
GUIUtil::formatBytes(stats->nodeStats.nSendBytes));
ui->peerBytesRecv->setText(
GUIUtil::formatBytes(stats->nodeStats.nRecvBytes));
ui->peerConnTime->setText(GUIUtil::formatDurationStr(
GetSystemTimeInSeconds() - stats->nodeStats.nTimeConnected));
ui->peerPingTime->setText(
GUIUtil::formatPingTime(stats->nodeStats.dPingTime));
ui->peerPingWait->setText(
GUIUtil::formatPingTime(stats->nodeStats.dPingWait));
ui->peerMinPing->setText(
GUIUtil::formatPingTime(stats->nodeStats.dMinPing));
ui->timeoffset->setText(
GUIUtil::formatTimeOffset(stats->nodeStats.nTimeOffset));
ui->peerVersion->setText(
QString("%1").arg(QString::number(stats->nodeStats.nVersion)));
ui->peerSubversion->setText(
QString::fromStdString(stats->nodeStats.cleanSubVer));
ui->peerDirection->setText(stats->nodeStats.fInbound ? tr("Inbound")
: tr("Outbound"));
ui->peerHeight->setText(
QString("%1").arg(QString::number(stats->nodeStats.nStartingHeight)));
ui->peerWhitelisted->setText(stats->nodeStats.fWhitelisted ? tr("Yes")
: tr("No"));
// This check fails for example if the lock was busy and
// nodeStateStats couldn't be fetched.
if (stats->fNodeStateStatsAvailable) {
// Ban score is init to 0
ui->peerBanScore->setText(
QString("%1").arg(stats->nodeStateStats.nMisbehavior));
// Sync height is init to -1
if (stats->nodeStateStats.nSyncHeight > -1) {
ui->peerSyncHeight->setText(
QString("%1").arg(stats->nodeStateStats.nSyncHeight));
} else {
ui->peerSyncHeight->setText(tr("Unknown"));
}
// Common height is init to -1
if (stats->nodeStateStats.nCommonHeight > -1) {
ui->peerCommonHeight->setText(
QString("%1").arg(stats->nodeStateStats.nCommonHeight));
} else {
ui->peerCommonHeight->setText(tr("Unknown"));
}
}
ui->detailWidget->show();
}
void RPCConsole::resizeEvent(QResizeEvent *event) {
QWidget::resizeEvent(event);
}
void RPCConsole::showEvent(QShowEvent *event) {
QWidget::showEvent(event);
if (!clientModel || !clientModel->getPeerTableModel()) {
return;
}
// start PeerTableModel auto refresh
clientModel->getPeerTableModel()->startAutoRefresh();
}
void RPCConsole::hideEvent(QHideEvent *event) {
QWidget::hideEvent(event);
if (!clientModel || !clientModel->getPeerTableModel()) {
return;
}
// stop PeerTableModel auto refresh
clientModel->getPeerTableModel()->stopAutoRefresh();
}
void RPCConsole::showPeersTableContextMenu(const QPoint &point) {
QModelIndex index = ui->peerWidget->indexAt(point);
if (index.isValid()) peersTableContextMenu->exec(QCursor::pos());
}
void RPCConsole::showBanTableContextMenu(const QPoint &point) {
QModelIndex index = ui->banlistWidget->indexAt(point);
if (index.isValid()) banTableContextMenu->exec(QCursor::pos());
}
void RPCConsole::disconnectSelectedNode() {
// Get selected peer addresses
QList<QModelIndex> nodes =
GUIUtil::getEntryData(ui->peerWidget, PeerTableModel::NetNodeId);
for (int i = 0; i < nodes.count(); i++) {
// Get currently selected peer address
NodeId id = nodes.at(i).data().toLongLong();
// Find the node, disconnect it and clear the selected node
if (m_node.disconnect(id)) {
clearSelectedNode();
}
}
}
void RPCConsole::banSelectedNode(int bantime) {
if (!clientModel) {
return;
}
// Get selected peer addresses
QList<QModelIndex> nodes =
GUIUtil::getEntryData(ui->peerWidget, PeerTableModel::NetNodeId);
for (int i = 0; i < nodes.count(); i++) {
// Get currently selected peer address
NodeId id = nodes.at(i).data().toLongLong();
// Get currently selected peer address
int detailNodeRow =
clientModel->getPeerTableModel()->getRowByNodeId(id);
if (detailNodeRow < 0) {
return;
}
// Find possible nodes, ban it and clear the selected node
const CNodeCombinedStats *stats =
clientModel->getPeerTableModel()->getNodeStats(detailNodeRow);
if (stats) {
m_node.ban(stats->nodeStats.addr, BanReasonManuallyAdded, bantime);
m_node.disconnect(stats->nodeStats.addr);
}
}
clearSelectedNode();
clientModel->getBanTableModel()->refresh();
}
void RPCConsole::unbanSelectedNode() {
if (!clientModel) {
return;
}
// Get selected ban addresses
QList<QModelIndex> 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().c_str(), possibleSubnet);
if (possibleSubnet.IsValid() && m_node.unban(possibleSubnet)) {
clientModel->getBanTableModel()->refresh();
}
}
}
void RPCConsole::clearSelectedNode() {
ui->peerWidget->selectionModel()->clearSelection();
cachedNodeids.clear();
ui->detailWidget->hide();
ui->peerHeading->setText(tr("Select a peer to view detailed information."));
}
void RPCConsole::showOrHideBanTableIfRequired() {
if (!clientModel) {
return;
}
bool visible = clientModel->getBanTableModel()->shouldShow();
ui->banlistWidget->setVisible(visible);
ui->banHeading->setVisible(visible);
}
void RPCConsole::setTabFocus(enum TabTypes tabType) {
ui->tabWidget->setCurrentIndex(tabType);
}
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Sun, Mar 2, 12:16 (23 h, 57 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
5187817
Default Alt Text
(116 KB)
Attached To
rSTAGING Bitcoin ABC staging
Event Timeline
Log In to Comment