aboutsummaryrefslogtreecommitdiff
path: root/src/qt/peertablemodel.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/qt/peertablemodel.cpp')
-rw-r--r--src/qt/peertablemodel.cpp236
1 files changed, 236 insertions, 0 deletions
diff --git a/src/qt/peertablemodel.cpp b/src/qt/peertablemodel.cpp
new file mode 100644
index 0000000000..981d063c49
--- /dev/null
+++ b/src/qt/peertablemodel.cpp
@@ -0,0 +1,236 @@
+// Copyright (c) 2011-2013 The Bitcoin developers
+// Distributed under the MIT/X11 software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include "peertablemodel.h"
+
+#include "clientmodel.h"
+
+#include "net.h"
+#include "sync.h"
+
+#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::Address:
+ return pLeft->addrName.compare(pRight->addrName) < 0;
+ case PeerTableModel::Subversion:
+ return pLeft->cleanSubVer.compare(pRight->cleanSubVer) < 0;
+ case PeerTableModel::Height:
+ return pLeft->nStartingHeight < pRight->nStartingHeight;
+ }
+
+ return false;
+}
+
+// private implementation
+class PeerTablePriv
+{
+public:
+ /** Local cache of peer information */
+ QList<CNodeCombinedStats> cachedNodeStats;
+ /** Column to sort nodes by */
+ int sortColumn;
+ /** 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() {
+ {
+ TRY_LOCK(cs_vNodes, lockNodes);
+ if (!lockNodes)
+ {
+ // skip the refresh if we can't immediately get the lock
+ return;
+ }
+ cachedNodeStats.clear();
+#if QT_VERSION >= 0x040700
+ cachedNodeStats.reserve(vNodes.size());
+#endif
+ BOOST_FOREACH(CNode* pnode, vNodes)
+ {
+ CNodeCombinedStats stats;
+ stats.statestats.nMisbehavior = -1;
+ pnode->copyStats(stats.nodestats);
+ cachedNodeStats.append(stats);
+ }
+ }
+
+ // if we can, retrieve the CNodeStateStats for each node.
+ {
+ TRY_LOCK(cs_main, lockMain);
+ if (lockMain)
+ {
+ BOOST_FOREACH(CNodeCombinedStats &stats, cachedNodeStats)
+ {
+ GetNodeStateStats(stats.nodestats.nodeid, stats.statestats);
+ }
+ }
+ }
+
+ if (sortColumn >= 0)
+ // sort cacheNodeStats (use stable sort to prevent rows jumping around unneceesarily)
+ qStableSort(cachedNodeStats.begin(), cachedNodeStats.end(), NodeLessThan(sortColumn, sortOrder));
+
+ // build index map
+ mapNodeRows.clear();
+ int row = 0;
+ BOOST_FOREACH(CNodeCombinedStats &stats, cachedNodeStats)
+ {
+ mapNodeRows.insert(std::pair<NodeId, int>(stats.nodestats.nodeid, row++));
+ }
+ }
+
+ int size()
+ {
+ return cachedNodeStats.size();
+ }
+
+ CNodeCombinedStats *index(int idx)
+ {
+ if(idx >= 0 && idx < cachedNodeStats.size()) {
+ return &cachedNodeStats[idx];
+ }
+ else
+ {
+ return 0;
+ }
+ }
+};
+
+PeerTableModel::PeerTableModel(ClientModel *parent) :
+ QAbstractTableModel(parent),clientModel(parent),timer(0)
+{
+ columns << tr("Address") << tr("User Agent") << tr("Start Height");
+ priv = new PeerTablePriv();
+ // default to unsorted
+ priv->sortColumn = -1;
+
+ // set up timer for auto refresh
+ timer = new QTimer();
+ connect(timer, SIGNAL(timeout()), SLOT(refresh()));
+
+ // load initial data
+ refresh();
+}
+
+void PeerTableModel::startAutoRefresh(int msecs)
+{
+ timer->setInterval(1000);
+ 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 3;
+}
+
+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 Address:
+ return QVariant(rec->nodestats.addrName.c_str());
+ case Subversion:
+ return QVariant(rec->nodestats.cleanSubVer.c_str());
+ case Height:
+ return rec->nodestats.nStartingHeight;
+ }
+ }
+ 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 0;
+
+ 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);
+ }
+ else
+ {
+ return QModelIndex();
+ }
+}
+
+const CNodeCombinedStats *PeerTableModel::getNodeStats(int idx) {
+ return priv->index(idx);
+}
+
+void PeerTableModel::refresh()
+{
+ emit layoutAboutToBeChanged();
+ priv->refreshPeers();
+ 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();
+}