git / moonbite-core / src/qt/peertablemodel.cpp · raw
// Copyright (c) 2011-2020 The Bitcoin Core developers// Distributed under the MIT software license, see the accompanying// file COPYING or http://www.opensource.org/licenses/mit-license.php.#include <qt/peertablemodel.h>#include <qt/guiconstants.h>#include <qt/guiutil.h>#include <interfaces/node.h>#include <utility>#include <QDebug>#include <QList>#include <QTimer>bool NodeLessThan::operator()(const CNodeCombinedStats &left, const CNodeCombinedStats &right) const{ const CNodeStats *pLeft = &(left.nodeStats); const CNodeStats *pRight = &(right.nodeStats); if (order == Qt::DescendingOrder) std::swap(pLeft, pRight); switch(column) { case PeerTableModel::NetNodeId: return pLeft->nodeid < pRight->nodeid; case PeerTableModel::Address: return pLeft->addrName.compare(pRight->addrName) < 0; case PeerTableModel::Subversion: return pLeft->cleanSubVer.compare(pRight->cleanSubVer) < 0; case PeerTableModel::Ping: return pLeft->m_min_ping_usec < pRight->m_min_ping_usec; case PeerTableModel::Sent: return pLeft->nSendBytes < pRight->nSendBytes; case PeerTableModel::Received: return pLeft->nRecvBytes < pRight->nRecvBytes; } return false;}// private implementationclass PeerTablePriv{public: /** Local cache of peer information */ QList<CNodeCombinedStats> cachedNodeStats; /** Column to sort nodes by (default to unsorted) */ int sortColumn{-1}; /** Order (ascending or descending) to sort nodes by */ Qt::SortOrder sortOrder; /** Index of rows by node ID */ std::map<NodeId, int> mapNodeRows; /** Pull a full list of peers from vNodes into our cache */ void refreshPeers(interfaces::Node& node) { { cachedNodeStats.clear(); interfaces::Node::NodesStats nodes_stats; node.getNodesStats(nodes_stats); cachedNodeStats.reserve(nodes_stats.size()); for (const auto& node_stats : nodes_stats) { CNodeCombinedStats stats; stats.nodeStats = std::get<0>(node_stats); stats.fNodeStateStatsAvailable = std::get<1>(node_stats); stats.nodeStateStats = std::get<2>(node_stats); cachedNodeStats.append(stats); } } if (sortColumn >= 0) // sort cacheNodeStats (use stable sort to prevent rows jumping around unnecessarily) std::stable_sort(cachedNodeStats.begin(), cachedNodeStats.end(), NodeLessThan(sortColumn, sortOrder)); // build index map mapNodeRows.clear(); int row = 0; for (const CNodeCombinedStats& stats : cachedNodeStats) mapNodeRows.insert(std::pair<NodeId, int>(stats.nodeStats.nodeid, row++)); } int size() const { return cachedNodeStats.size(); } CNodeCombinedStats *index(int idx) { if (idx >= 0 && idx < cachedNodeStats.size()) return &cachedNodeStats[idx]; return nullptr; }};PeerTableModel::PeerTableModel(interfaces::Node& node, QObject* parent) : QAbstractTableModel(parent), m_node(node), timer(nullptr){ columns << tr("NodeId") << tr("Node/Service") << tr("Ping") << tr("Sent") << tr("Received") << tr("User Agent"); priv.reset(new PeerTablePriv()); // set up timer for auto refresh timer = new QTimer(this); connect(timer, &QTimer::timeout, this, &PeerTableModel::refresh); timer->setInterval(MODEL_UPDATE_DELAY); // load initial data refresh();}PeerTableModel::~PeerTableModel(){ // Intentionally left empty}void PeerTableModel::startAutoRefresh(){ timer->start();}void PeerTableModel::stopAutoRefresh(){ timer->stop();}int PeerTableModel::rowCount(const QModelIndex &parent) const{ Q_UNUSED(parent); return priv->size();}int PeerTableModel::columnCount(const QModelIndex &parent) const{ Q_UNUSED(parent); return columns.length();}QVariant PeerTableModel::data(const QModelIndex &index, int role) const{ if(!index.isValid()) return QVariant(); CNodeCombinedStats *rec = static_cast<CNodeCombinedStats*>(index.internalPointer()); if (role == Qt::DisplayRole) { switch(index.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.addrName); case Subversion: return QString::fromStdString(rec->nodeStats.cleanSubVer); case Ping: return GUIUtil::formatPingTime(rec->nodeStats.m_min_ping_usec); case Sent: return GUIUtil::formatBytes(rec->nodeStats.nSendBytes); case Received: return GUIUtil::formatBytes(rec->nodeStats.nRecvBytes); } } else if (role == Qt::TextAlignmentRole) { switch (index.column()) { case Ping: case Sent: case Received: return QVariant(Qt::AlignRight | Qt::AlignVCenter); default: return QVariant(); } } return QVariant();}QVariant PeerTableModel::headerData(int section, Qt::Orientation orientation, int role) const{ if(orientation == Qt::Horizontal) { if(role == Qt::DisplayRole && section < columns.size()) { return columns[section]; } } return QVariant();}Qt::ItemFlags PeerTableModel::flags(const QModelIndex &index) const{ if (!index.isValid()) return Qt::NoItemFlags; Qt::ItemFlags retval = Qt::ItemIsSelectable | Qt::ItemIsEnabled; return retval;}QModelIndex PeerTableModel::index(int row, int column, const QModelIndex &parent) const{ Q_UNUSED(parent); CNodeCombinedStats *data = priv->index(row); if (data) return createIndex(row, column, data); return QModelIndex();}const CNodeCombinedStats *PeerTableModel::getNodeStats(int idx){ return priv->index(idx);}void PeerTableModel::refresh(){ Q_EMIT layoutAboutToBeChanged(); priv->refreshPeers(m_node); Q_EMIT layoutChanged();}int PeerTableModel::getRowByNodeId(NodeId nodeid){ std::map<NodeId, int>::iterator it = priv->mapNodeRows.find(nodeid); if (it == priv->mapNodeRows.end()) return -1; return it->second;}void PeerTableModel::sort(int column, Qt::SortOrder order){ priv->sortColumn = column; priv->sortOrder = order; refresh();}