diff --git a/contrib/debian/copyright b/contrib/debian/copyright index fe85494d99be..2f9c7ced90f2 100644 --- a/contrib/debian/copyright +++ b/contrib/debian/copyright @@ -31,6 +31,10 @@ Files: src/qt/res/icons/proxy.png Copyright: Cristian Mircea Messel License: public-domain +Files: src/qt/fonts/RobotoMono-Bold.ttf +License: Apache-2.0 +Comment: Site: https://fonts.google.com/specimen/Roboto+Mono + License: Expat Permission is hereby granted, free of charge, to any person obtaining a @@ -88,3 +92,14 @@ Comment: License: public-domain This work is in the public domain. + +License: Apache-2.0 + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include index cabc199d6222..8ca112d131d9 100644 --- a/src/Makefile.qt.include +++ b/src/Makefile.qt.include @@ -352,7 +352,8 @@ RES_FONTS = \ qt/res/fonts/Montserrat/Montserrat-SemiBold.otf \ qt/res/fonts/Montserrat/Montserrat-SemiBoldItalic.otf \ qt/res/fonts/Montserrat/Montserrat-Thin.otf \ - qt/res/fonts/Montserrat/Montserrat-ThinItalic.otf + qt/res/fonts/Montserrat/Montserrat-ThinItalic.otf \ + qt/res/fonts/RobotoMono-Bold.ttf RES_ANIMATION = $(wildcard $(srcdir)/qt/res/animation/spinner-*.png) diff --git a/src/net.h b/src/net.h index 77856e8bf91f..a345802d4eab 100644 --- a/src/net.h +++ b/src/net.h @@ -148,7 +148,8 @@ struct CSerializedNetMsg * connection. Aside from INBOUND, all types are initiated by us. * * If adding or removing types, please update CONNECTION_TYPE_DOC in - * src/rpc/net.cpp. */ + * src/rpc/net.cpp and src/qt/rpcconsole.cpp, as well as the descriptions in + * src/qt/guiutil.cpp and src/bitcoin-cli.cpp::NetinfoRequestHandler. */ enum class ConnectionType { /** * Inbound connections are those initiated by a peer. This is the only @@ -159,7 +160,7 @@ enum class ConnectionType { /** * These are the default connections that we use to connect with the - * network. There is no restriction on what is relayed- by default we relay + * network. There is no restriction on what is relayed; by default we relay * blocks, addresses & transactions. We automatically attempt to open * MAX_OUTBOUND_FULL_RELAY_CONNECTIONS using addresses from our AddrMan. */ @@ -167,8 +168,8 @@ enum class ConnectionType { /** - * We open manual connections to addresses that users explicitly inputted - * via the addnode RPC, or the -connect command line argument. Even if a + * We open manual connections to addresses that users explicitly requested + * via the addnode RPC or the -addnode/-connect configuration options. Even if a * manual connection is misbehaving, we do not automatically disconnect or * add it to our discouragement filter. */ @@ -187,7 +188,7 @@ enum class ConnectionType { * although in our codebase feeler connections encompass test-before-evict as well. * We make these connections approximately every FEELER_INTERVAL: * first we resolve previously found collisions if they exist (test-before-evict), - * otherwise connect to a node from the new table. + * otherwise we connect to a node from the new table. */ FEELER, diff --git a/src/qt/addresstablemodel.cpp b/src/qt/addresstablemodel.cpp index 15cc63cc5a24..258beaec70cd 100644 --- a/src/qt/addresstablemodel.cpp +++ b/src/qt/addresstablemodel.cpp @@ -195,42 +195,38 @@ QVariant AddressTableModel::data(const QModelIndex &index, int role) const AddressTableEntry *rec = static_cast(index.internalPointer()); - if(role == Qt::DisplayRole || role == Qt::EditRole) - { - switch(index.column()) - { + const auto column = static_cast(index.column()); + if (role == Qt::DisplayRole || role == Qt::EditRole) { + switch (column) { case Label: - if(rec->label.isEmpty() && role == Qt::DisplayRole) - { + if (rec->label.isEmpty() && role == Qt::DisplayRole) { return tr("(no label)"); - } - else - { + } else { return rec->label; } case Address: return rec->address; - } - } - else if (role == Qt::FontRole) - { - QFont font; - if(index.column() == Address) - { - font = GUIUtil::getFontNormal(); - } - return font; - } - else if (role == TypeRole) - { + } // no default case, so the compiler can warn about missing cases + assert(false); + } else if (role == Qt::FontRole) { + switch (column) { + case Label: + return QFont(); + case Address: + return GUIUtil::getFontNormal(); + } // no default case, so the compiler can warn about missing cases + assert(false); + } else if (role == TypeRole) { switch(rec->type) { case AddressTableEntry::Sending: return Send; case AddressTableEntry::Receiving: return Receive; - default: break; - } + case AddressTableEntry::Hidden: + return {}; + } // no default case, so the compiler can warn about missing cases + assert(false); } return QVariant(); } diff --git a/src/qt/bantablemodel.cpp b/src/qt/bantablemodel.cpp index a01a7bc38618..6cb4a4c54682 100644 --- a/src/qt/bantablemodel.cpp +++ b/src/qt/bantablemodel.cpp @@ -23,15 +23,13 @@ bool BannedNodeLessThan::operator()(const CCombinedBan& left, const CCombinedBan if (order == Qt::DescendingOrder) std::swap(pLeft, pRight); - switch(column) - { + switch (static_cast(column)) { case BanTableModel::Address: return pLeft->subnet.ToString().compare(pRight->subnet.ToString()) < 0; case BanTableModel::Bantime: return pLeft->banEntry.nBanUntil < pRight->banEntry.nBanUntil; - } - - return false; + } // no default case, so the compiler can warn about missing cases + assert(false); } // private implementation @@ -119,16 +117,17 @@ QVariant BanTableModel::data(const QModelIndex &index, int role) const CCombinedBan *rec = static_cast(index.internalPointer()); + const auto column = static_cast(index.column()); if (role == Qt::DisplayRole) { - switch(index.column()) - { + switch (column) { case Address: return QString::fromStdString(rec->subnet.ToString()); case Bantime: QDateTime date = QDateTime::fromMSecsSinceEpoch(0); date = date.addSecs(rec->banEntry.nBanUntil); return QLocale::system().toString(date, QLocale::LongFormat); - } + } // no default case, so the compiler can warn about missing cases + assert(false); } return QVariant(); diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index 02d6a7991a67..1196a37dd7ad 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -47,6 +47,7 @@ #include #include +#include #include #include #include @@ -576,6 +577,7 @@ int GuiMain(int argc, char* argv[]) QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); BitcoinApplication app; + QFontDatabase::addApplicationFont(":/fonts/monospace"); /// 2. Parse command-line options. We do this after qt in order to show an error if there are problems parsing these // Command-line options take precedence: diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 089725b44ed7..08a4bec14461 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -95,6 +95,8 @@ BitcoinGUI::BitcoinGUI(interfaces::Node& node, const NetworkStyle* networkStyle, move(QGuiApplication::primaryScreen()->availableGeometry().center() - frameGeometry().center()); } + setContextMenuPolicy(Qt::PreventContextMenu); + #ifdef ENABLE_WALLET enableWallet = WalletModel::isWalletEnabled(); #endif // ENABLE_WALLET @@ -669,7 +671,6 @@ void BitcoinGUI::createToolBars() { QToolBar *toolbar = new QToolBar(tr("Tabs toolbar")); appToolBar = toolbar; - toolbar->setContextMenuPolicy(Qt::PreventContextMenu); toolbar->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); toolbar->setToolButtonStyle(Qt::ToolButtonTextOnly); toolbar->setMovable(false); // remove unused icon in upper left corner diff --git a/src/qt/dash.qrc b/src/qt/dash.qrc index 8ba59831cfe5..e6cd77ad8549 100644 --- a/src/qt/dash.qrc +++ b/src/qt/dash.qrc @@ -36,6 +36,7 @@ res/css/traditional.css + res/fonts/RobotoMono-Bold.ttf res/fonts/Montserrat/Montserrat-Black.otf res/fonts/Montserrat/Montserrat-BlackItalic.otf res/fonts/Montserrat/Montserrat-Bold.otf diff --git a/src/qt/forms/debugwindow.ui b/src/qt/forms/debugwindow.ui index 11a5cad0a669..6e79ad9d2c59 100644 --- a/src/qt/forms/debugwindow.ui +++ b/src/qt/forms/debugwindow.ui @@ -1028,10 +1028,10 @@ - The type of peer connection:<ul><li>Inbound: initiated by peer</li><li>Outbound Full Relay: default</li><li>Outbound Block Relay: does not relay transactions or addresses</li><li>Outbound Manual: added using RPC %1 or %2/%3 configuration options</li><li>Outbound Feeler: short-lived, for testing addresses</li><li>Outbound Address Fetch: short-lived, for soliciting addresses</li></ul> + The direction and type of peer connection: %1 - Connection Type + Direction/Type diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp index 967a4fb870bb..d7c412ce78a0 100644 --- a/src/qt/guiutil.cpp +++ b/src/qt/guiutil.cpp @@ -260,6 +260,14 @@ QString dateTimeStr(qint64 nTime) return dateTimeStr(QDateTime::fromTime_t((qint32)nTime)); } +QFont fixedPitchFont(bool use_embedded_font) +{ + if (use_embedded_font) { + return {"Roboto Mono"}; + } + return QFontDatabase::systemFont(QFontDatabase::FixedFont); +} + // Just some dummy data to generate a convincing random-looking (but consistent) address static const uint8_t dummydata[] = {0xeb,0x15,0x23,0x1d,0xfc,0xeb,0x60,0x92,0x58,0x86,0xb6,0x7d,0x06,0x52,0x99,0x92,0x59,0x15,0xae,0xb1,0x72,0xc0,0x66,0x47}; @@ -1661,15 +1669,19 @@ QString NetworkToQString(Network net) assert(false); } -QString ConnectionTypeToQString(ConnectionType conn_type) +QString ConnectionTypeToQString(ConnectionType conn_type, bool prepend_direction) { + QString prefix; + if (prepend_direction) { + prefix = (conn_type == ConnectionType::INBOUND) ? QObject::tr("Inbound") : QObject::tr("Outbound") + " "; + } switch (conn_type) { - case ConnectionType::INBOUND: return QObject::tr("Inbound"); - case ConnectionType::OUTBOUND_FULL_RELAY: return QObject::tr("Outbound Full Relay"); - case ConnectionType::BLOCK_RELAY: return QObject::tr("Outbound Block Relay"); - case ConnectionType::MANUAL: return QObject::tr("Outbound Manual"); - case ConnectionType::FEELER: return QObject::tr("Outbound Feeler"); - case ConnectionType::ADDR_FETCH: return QObject::tr("Outbound Address Fetch"); + case ConnectionType::INBOUND: return prefix; + case ConnectionType::OUTBOUND_FULL_RELAY: return prefix + QObject::tr("Full Relay"); + case ConnectionType::BLOCK_RELAY: return prefix + QObject::tr("Block Relay"); + case ConnectionType::MANUAL: return prefix + QObject::tr("Manual"); + case ConnectionType::FEELER: return prefix + QObject::tr("Feeler"); + case ConnectionType::ADDR_FETCH: return prefix + QObject::tr("Address Fetch"); } // no default case, so the compiler can warn about missing cases assert(false); } diff --git a/src/qt/guiutil.h b/src/qt/guiutil.h index 53f1b98a558c..710525a4c848 100644 --- a/src/qt/guiutil.h +++ b/src/qt/guiutil.h @@ -126,6 +126,9 @@ namespace GUIUtil QString dateTimeStr(const QDateTime &datetime); QString dateTimeStr(qint64 nTime); + // Return a monospace font + QFont fixedPitchFont(bool use_embedded_font = false); + // Set up widget for address void setupAddressWidget(QValidatedLineEdit *widget, QWidget *parent, bool fAllowURI = false); @@ -398,7 +401,7 @@ namespace GUIUtil QString NetworkToQString(Network net); /** Convert enum ConnectionType to QString */ - QString ConnectionTypeToQString(ConnectionType conn_type); + QString ConnectionTypeToQString(ConnectionType conn_type, bool prepend_direction); /** Convert seconds into a QString with days, hours, mins, secs */ QString formatDurationStr(std::chrono::seconds dur); diff --git a/src/qt/intro.cpp b/src/qt/intro.cpp index 3622de4ec1e8..ed522c61f4d1 100644 --- a/src/qt/intro.cpp +++ b/src/qt/intro.cpp @@ -304,12 +304,12 @@ void Intro::setStatus(int status, const QString &message, quint64 bytesAvailable void Intro::UpdateFreeSpaceLabel() { - QString freeString = tr("%n GB of free space available", "", m_bytes_available / GB_BYTES); + QString freeString = tr("%1 GB of free space available").arg(m_bytes_available / GB_BYTES); if (m_bytes_available < m_required_space_gb * GB_BYTES) { - freeString += " " + tr("(of %n GB needed)", "", m_required_space_gb); + freeString += " " + tr("(of %1 GB needed)").arg(m_required_space_gb); ui->freeSpace->setStyleSheet(GUIUtil::getThemedStyleQString(GUIUtil::ThemedStyle::TS_ERROR)); } else if (m_bytes_available / GB_BYTES - m_required_space_gb < 10) { - freeString += " " + tr("(%n GB needed for full chain)", "", m_required_space_gb); + freeString += " " + tr("(%1 GB needed for full chain)").arg(m_required_space_gb); ui->freeSpace->setStyleSheet(GUIUtil::getThemedStyleQString(GUIUtil::ThemedStyle::TS_WARNING)); } else { ui->freeSpace->setStyleSheet(""); diff --git a/src/qt/paymentserver.cpp b/src/qt/paymentserver.cpp index 4419acd8d498..693177fd800b 100644 --- a/src/qt/paymentserver.cpp +++ b/src/qt/paymentserver.cpp @@ -233,14 +233,17 @@ void PaymentServer::handleURIOrFile(const QString& s) SendCoinsRecipient recipient; if (GUIUtil::parseBitcoinURI(s, &recipient)) { - if (!IsValidDestinationString(recipient.address.toStdString())) { + std::string error_msg; + const CTxDestination dest = DecodeDestination(recipient.address.toStdString(), error_msg); + + if (!IsValidDestination(dest)) { if (uri.hasQueryItem("r")) { // payment request Q_EMIT message(tr("URI handling"), tr("Cannot process payment request as BIP70 is no longer supported.")+ tr("Due to discontinued support, you should request the merchant to provide you with a BIP21 compatible URI or use a wallet that does continue to support BIP70."), CClientUIInterface::ICON_WARNING); } else { - Q_EMIT message(tr("URI handling"), tr("Invalid payment address %1").arg(recipient.address), + Q_EMIT message(tr("URI handling"), QString::fromStdString(error_msg), CClientUIInterface::MSG_ERROR); } } diff --git a/src/qt/peertablemodel.cpp b/src/qt/peertablemodel.cpp index 8166e4866ee1..5c60e25f8f9f 100644 --- a/src/qt/peertablemodel.cpp +++ b/src/qt/peertablemodel.cpp @@ -23,12 +23,13 @@ bool NodeLessThan::operator()(const CNodeCombinedStats &left, const CNodeCombine if (order == Qt::DescendingOrder) std::swap(pLeft, pRight); - switch(column) - { + switch (static_cast(column)) { case PeerTableModel::NetNodeId: return pLeft->nodeid < pRight->nodeid; case PeerTableModel::Address: return pLeft->m_addr_name.compare(pRight->m_addr_name) < 0; + case PeerTableModel::ConnectionType: + return pLeft->m_conn_type < pRight->m_conn_type; case PeerTableModel::Network: return pLeft->m_network < pRight->m_network; case PeerTableModel::Ping: @@ -39,9 +40,8 @@ bool NodeLessThan::operator()(const CNodeCombinedStats &left, const CNodeCombine return pLeft->nRecvBytes < pRight->nRecvBytes; case PeerTableModel::Subversion: return pLeft->cleanSubVer.compare(pRight->cleanSubVer) < 0; - } - - return false; + } // no default case, so the compiler can warn about missing cases + assert(false); } // private implementation @@ -156,14 +156,16 @@ QVariant PeerTableModel::data(const QModelIndex &index, int role) const CNodeCombinedStats *rec = static_cast(index.internalPointer()); + const auto column = static_cast(index.column()); if (role == Qt::DisplayRole) { - switch(index.column()) - { + switch (column) { case NetNodeId: return (qint64)rec->nodeStats.nodeid; case Address: // prepend to peer address down-arrow symbol for inbound connection and up-arrow for outbound connection return QString(rec->nodeStats.fInbound ? "↓ " : "↑ ") + QString::fromStdString(rec->nodeStats.m_addr_name); + case ConnectionType: + return GUIUtil::ConnectionTypeToQString(rec->nodeStats.m_conn_type, /* prepend_direction */ false); case Network: return GUIUtil::NetworkToQString(rec->nodeStats.m_network); case Ping: @@ -174,18 +176,25 @@ QVariant PeerTableModel::data(const QModelIndex &index, int role) const return GUIUtil::formatBytes(rec->nodeStats.nRecvBytes); case Subversion: return QString::fromStdString(rec->nodeStats.cleanSubVer); - } + } // no default case, so the compiler can warn about missing cases + assert(false); } else if (role == Qt::TextAlignmentRole) { - switch (index.column()) { - case Network: - return QVariant(Qt::AlignCenter); - case Ping: - case Sent: - case Received: - return QVariant(Qt::AlignRight | Qt::AlignVCenter); - default: - return QVariant(); - } + switch (column) { + case NetNodeId: + return QVariant(Qt::AlignRight | Qt::AlignVCenter); + case Address: + return {}; + case ConnectionType: + case Network: + return QVariant(Qt::AlignCenter); + case Ping: + case Sent: + case Received: + return QVariant(Qt::AlignRight | Qt::AlignVCenter); + case Subversion: + return {}; + } // no default case, so the compiler can warn about missing cases + assert(false); } else if (role == StatsRole) { switch (index.column()) { case NetNodeId: return QVariant::fromValue(rec); diff --git a/src/qt/peertablemodel.h b/src/qt/peertablemodel.h index 16643621f304..66228d609953 100644 --- a/src/qt/peertablemodel.h +++ b/src/qt/peertablemodel.h @@ -59,12 +59,13 @@ class PeerTableModel : public QAbstractTableModel enum ColumnIndex { NetNodeId = 0, - Address = 1, - Network = 2, - Ping = 3, - Sent = 4, - Received = 5, - Subversion = 6 + Address, + ConnectionType, + Network, + Ping, + Sent, + Received, + Subversion }; enum { @@ -94,6 +95,9 @@ public Q_SLOTS: /*: Title of Peers Table column which contains the IP/Onion/I2P address of the connected peer. */ tr("Address"), + /*: Title of Peers Table column which describes the type of + peer connection. The "type" describes why the connection exists. */ + tr("Type"), /*: Title of Peers Table column which states the network the peer connected through. */ tr("Network"), diff --git a/src/qt/receivecoinsdialog.cpp b/src/qt/receivecoinsdialog.cpp index 3a1e47ec2b50..5dcb2b66031f 100644 --- a/src/qt/receivecoinsdialog.cpp +++ b/src/qt/receivecoinsdialog.cpp @@ -34,9 +34,9 @@ ReceiveCoinsDialog::ReceiveCoinsDialog(QWidget* parent) : // context menu actions QAction *copyURIAction = new QAction(tr("Copy URI"), this); QAction* copyAddressAction = new QAction(tr("Copy address"), this); - QAction *copyLabelAction = new QAction(tr("Copy label"), this); - QAction *copyMessageAction = new QAction(tr("Copy message"), this); - QAction *copyAmountAction = new QAction(tr("Copy amount"), this); + copyLabelAction = new QAction(tr("Copy label"), this); + copyMessageAction = new QAction(tr("Copy message"), this); + copyAmountAction = new QAction(tr("Copy amount"), this); // context menu contextMenu = new QMenu(this); @@ -239,9 +239,18 @@ void ReceiveCoinsDialog::copyColumnToClipboard(int column) // context menu void ReceiveCoinsDialog::showMenu(const QPoint &point) { - if (!selectedRow().isValid()) { + const QModelIndex sel = selectedRow(); + if (!sel.isValid()) { return; } + + // disable context menu actions when appropriate + const RecentRequestsTableModel* const submodel = model->getRecentRequestsTableModel(); + const RecentRequestEntry& req = submodel->entry(sel.row()); + copyLabelAction->setDisabled(req.recipient.label.isEmpty()); + copyMessageAction->setDisabled(req.recipient.message.isEmpty()); + copyAmountAction->setDisabled(req.recipient.amount == 0); + contextMenu->exec(QCursor::pos()); } diff --git a/src/qt/receivecoinsdialog.h b/src/qt/receivecoinsdialog.h index 632c65e72426..5a33a16a1069 100644 --- a/src/qt/receivecoinsdialog.h +++ b/src/qt/receivecoinsdialog.h @@ -52,6 +52,9 @@ public Q_SLOTS: Ui::ReceiveCoinsDialog *ui; WalletModel *model; QMenu *contextMenu; + QAction* copyLabelAction; + QAction* copyMessageAction; + QAction* copyAmountAction; QModelIndex selectedRow(); void copyColumnToClipboard(int column); diff --git a/src/qt/res/fonts/RobotoMono-Bold.ttf b/src/qt/res/fonts/RobotoMono-Bold.ttf new file mode 100644 index 000000000000..900fce684821 Binary files /dev/null and b/src/qt/res/fonts/RobotoMono-Bold.ttf differ diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp index a8ce8f8d60eb..f15dce30e2cc 100644 --- a/src/qt/rpcconsole.cpp +++ b/src/qt/rpcconsole.cpp @@ -18,9 +18,10 @@ #include #include #include -#include #include +#include #include +#include #include #include #include @@ -42,16 +43,18 @@ #include #include #include +#include +#include #include #include #include #include #include -#include -#include #include #include - +#include +#include +#include const int CONSOLE_HISTORY = 50; const QSize FONT_RANGE(4, 40); @@ -129,6 +132,20 @@ class QtRPCTimerInterface: public RPCTimerInterface } }; +class PeerIdViewDelegate : public QStyledItemDelegate +{ + Q_OBJECT +public: + explicit PeerIdViewDelegate(QObject* parent = nullptr) + : QStyledItemDelegate(parent) {} + + QString displayText(const QVariant& value, const QLocale& locale) const override + { + // Additional spaces should visually separate right-aligned content + // from the next column to the right. + return value.toString() + QLatin1String(" "); + } +}; #include @@ -469,18 +486,37 @@ RPCConsole::RPCConsole(interfaces::Node& node, QWidget* parent, Qt::WindowFlags GUIUtil::disableMacFocusRect(this); QSettings settings; - if (!restoreGeometry(settings.value("RPCConsoleWindowGeometry").toByteArray())) { - // Restore failed (perhaps missing setting), center the window - move(QGuiApplication::primaryScreen()->availableGeometry().center() - frameGeometry().center()); +#ifdef ENABLE_WALLET + if (WalletModel::isWalletEnabled()) { + // RPCConsole widget is a window. + if (!restoreGeometry(settings.value("RPCConsoleWindowGeometry").toByteArray())) { + // Restore failed (perhaps missing setting), center the window + move(QGuiApplication::primaryScreen()->availableGeometry().center() - frameGeometry().center()); + } + ui->splitter->restoreState(settings.value("RPCConsoleWindowPeersTabSplitterSizes").toByteArray()); + } else +#endif // ENABLE_WALLET + { + // RPCConsole is a child widget. + ui->splitter->restoreState(settings.value("RPCConsoleWidgetPeersTabSplitterSizes").toByteArray()); } - ui->splitter->restoreState(settings.value("PeersTabSplitterSizes").toByteArray()); - - QChar nonbreaking_hyphen(8209); + constexpr QChar nonbreaking_hyphen(8209); + const std::vector CONNECTION_TYPE_DOC{ + tr("Inbound: initiated by peer"), + tr("Outbound Full Relay: default"), + tr("Outbound Block Relay: does not relay transactions or addresses"), + tr("Outbound Manual: added using RPC %1 or %2/%3 configuration options") + .arg("addnode") + .arg(QString(nonbreaking_hyphen) + "addnode") + .arg(QString(nonbreaking_hyphen) + "connect"), + tr("Outbound Feeler: short-lived, for testing addresses"), + tr("Outbound Address Fetch: short-lived, for soliciting addresses")}; + const QString list{"
  • " + Join(CONNECTION_TYPE_DOC, QString("
  • ")) + "
"}; + ui->peerConnectionTypeLabel->setToolTip(ui->peerConnectionTypeLabel->toolTip().arg(list)); 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(PACKAGE_NAME)); - ui->peerConnectionTypeLabel->setToolTip(ui->peerConnectionTypeLabel->toolTip().arg("addnode").arg(QString(nonbreaking_hyphen) + "addnode").arg(QString(nonbreaking_hyphen) + "connect")); setButtonIcons(); @@ -515,7 +551,7 @@ RPCConsole::RPCConsole(interfaces::Node& node, QWidget* parent, Qt::WindowFlags setTrafficGraphRange(INITIAL_TRAFFIC_GRAPH_SETTING); updateDetailWidget(); - setFontSize(settings.value(fontSizeSettingsKey, QFontInfo(QFontDatabase::systemFont(QFontDatabase::FixedFont)).pointSize()).toInt()); + setFontSize(settings.value(fontSizeSettingsKey, QFontInfo(GUIUtil::fixedPitchFont()).pointSize()).toInt()); pageButtons = new QButtonGroup(this); pageButtons->addButton(ui->btnInfo, pageButtons->buttons().size()); @@ -539,8 +575,18 @@ RPCConsole::RPCConsole(interfaces::Node& node, QWidget* parent, Qt::WindowFlags RPCConsole::~RPCConsole() { QSettings settings; - settings.setValue("RPCConsoleWindowGeometry", saveGeometry()); - settings.setValue("PeersTabSplitterSizes", ui->splitter->saveState()); +#ifdef ENABLE_WALLET + if (WalletModel::isWalletEnabled()) { + // RPCConsole widget is a window. + settings.setValue("RPCConsoleWindowGeometry", saveGeometry()); + settings.setValue("RPCConsoleWindowPeersTabSplitterSizes", ui->splitter->saveState()); + } else +#endif // ENABLE_WALLET + { + // RPCConsole is a child widget. + settings.setValue("RPCConsoleWidgetPeersTabSplitterSizes", ui->splitter->saveState()); + } + m_node.rpcUnsetTimerInterface(rpcTimerInterface); delete rpcTimerInterface; delete pageButtons; @@ -636,6 +682,7 @@ void RPCConsole::setClientModel(ClientModel *model, int bestblock_height, int64_ ui->peerWidget->setColumnWidth(PeerTableModel::Subversion, SUBVERSION_COLUMN_WIDTH); ui->peerWidget->setColumnWidth(PeerTableModel::Ping, PING_COLUMN_WIDTH); ui->peerWidget->horizontalHeader()->setStretchLastSection(true); + ui->peerWidget->setItemDelegateForColumn(PeerTableModel::NetNodeId, new PeerIdViewDelegate(this)); // create peer table context menu actions QAction* disconnectAction = new QAction(tr("&Disconnect"), this); @@ -880,7 +927,7 @@ void RPCConsole::clear(bool keep_prompt) ui->lineEdit->setFocus(); // Set default style sheet - ui->messagesWidget->setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont)); + ui->messagesWidget->setFont(GUIUtil::fixedPitchFont()); ui->messagesWidget->document()->setDefaultStyleSheet( QString( "table { }" @@ -1241,7 +1288,7 @@ void RPCConsole::updateDetailWidget() ui->timeoffset->setText(GUIUtil::formatTimeOffset(stats->nodeStats.nTimeOffset)); ui->peerVersion->setText(QString::number(stats->nodeStats.nVersion)); ui->peerSubversion->setText(QString::fromStdString(stats->nodeStats.cleanSubVer)); - ui->peerConnectionType->setText(GUIUtil::ConnectionTypeToQString(stats->nodeStats.m_conn_type)); + ui->peerConnectionType->setText(GUIUtil::ConnectionTypeToQString(stats->nodeStats.m_conn_type, /* prepend_direction */ true)); ui->peerNetwork->setText(GUIUtil::NetworkToQString(stats->nodeStats.m_network)); if (stats->nodeStats.m_permissionFlags == NetPermissionFlags::None) { ui->peerPermissions->setText(tr("N/A")); diff --git a/src/qt/transactiontablemodel.cpp b/src/qt/transactiontablemodel.cpp index fc76722a4c22..c08c7db2f92e 100644 --- a/src/qt/transactiontablemodel.cpp +++ b/src/qt/transactiontablemodel.cpp @@ -607,26 +607,29 @@ QVariant TransactionTableModel::data(const QModelIndex &index, int role) const return QVariant(); TransactionRecord *rec = static_cast(index.internalPointer()); - switch(role) - { + const auto column = static_cast(index.column()); + switch (role) { case RawDecorationRole: - switch(index.column()) - { + switch (column) { case Status: return txStatusDecoration(rec); case Watchonly: return txWatchonlyDecoration(rec); + case Date: return {}; + case Type: return {}; case ToAddress: return txAddressDecoration(rec); - } - break; + case Amount: return {}; + } // no default case, so the compiler can warn about missing cases + assert(false); case Qt::DecorationRole: { return qvariant_cast(index.data(RawDecorationRole)); } case Qt::DisplayRole: - switch(index.column()) - { + switch (column) { + case Status: return {}; + case Watchonly: return {}; case Date: return formatTxDate(rec); case Type: @@ -635,12 +638,11 @@ QVariant TransactionTableModel::data(const QModelIndex &index, int role) const return formatTxToAddress(rec, false); case Amount: return formatTxAmount(rec, true, BitcoinUnits::SeparatorStyle::ALWAYS); - } - break; + } // no default case, so the compiler can warn about missing cases + assert(false); case Qt::EditRole: // Edit role is used for sorting, so return the unformatted values - switch(index.column()) - { + switch (column) { case Status: return QString::fromStdString(rec->status.sortKey); case Date: @@ -653,8 +655,8 @@ QVariant TransactionTableModel::data(const QModelIndex &index, int role) const return formatTxToAddress(rec, true); case Amount: return qint64(rec->credit + rec->debit); - } - break; + } // no default case, so the compiler can warn about missing cases + assert(false); case Qt::ToolTipRole: return formatTooltip(rec); case Qt::TextAlignmentRole: