Line data Source code
1 : // Copyright (c) 2011-2013 The Bitcoin Core developers
2 : // Distributed under the MIT software license, see the accompanying
3 : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
4 :
5 : #include "peertablemodel.h"
6 :
7 : #include "clientmodel.h"
8 : #include "guiconstants.h"
9 : #include "guiutil.h"
10 :
11 : #include "sync.h"
12 :
13 : #include <QDebug>
14 : #include <QList>
15 : #include <QTimer>
16 :
17 0 : bool NodeLessThan::operator()(const CNodeCombinedStats &left, const CNodeCombinedStats &right) const
18 : {
19 0 : const CNodeStats *pLeft = &(left.nodeStats);
20 0 : const CNodeStats *pRight = &(right.nodeStats);
21 :
22 0 : if (order == Qt::DescendingOrder)
23 : std::swap(pLeft, pRight);
24 :
25 0 : switch(column)
26 : {
27 : case PeerTableModel::Address:
28 0 : return pLeft->addrName.compare(pRight->addrName) < 0;
29 : case PeerTableModel::Subversion:
30 0 : return pLeft->cleanSubVer.compare(pRight->cleanSubVer) < 0;
31 : case PeerTableModel::Ping:
32 0 : return pLeft->dPingTime < pRight->dPingTime;
33 : }
34 :
35 : return false;
36 : }
37 :
38 : // private implementation
39 0 : class PeerTablePriv
40 : {
41 : public:
42 : /** Local cache of peer information */
43 : QList<CNodeCombinedStats> cachedNodeStats;
44 : /** Column to sort nodes by */
45 : int sortColumn;
46 : /** Order (ascending or descending) to sort nodes by */
47 : Qt::SortOrder sortOrder;
48 : /** Index of rows by node ID */
49 : std::map<NodeId, int> mapNodeRows;
50 :
51 : /** Pull a full list of peers from vNodes into our cache */
52 0 : void refreshPeers()
53 : {
54 : {
55 0 : TRY_LOCK(cs_vNodes, lockNodes);
56 0 : if (!lockNodes)
57 : {
58 : // skip the refresh if we can't immediately get the lock
59 0 : return;
60 : }
61 0 : cachedNodeStats.clear();
62 : #if QT_VERSION >= 0x040700
63 0 : cachedNodeStats.reserve(vNodes.size());
64 : #endif
65 0 : Q_FOREACH (CNode* pnode, vNodes)
66 : {
67 : CNodeCombinedStats stats;
68 0 : stats.nodeStateStats.nMisbehavior = 0;
69 0 : stats.nodeStateStats.nSyncHeight = -1;
70 0 : stats.nodeStateStats.nCommonHeight = -1;
71 0 : stats.fNodeStateStatsAvailable = false;
72 0 : pnode->copyStats(stats.nodeStats);
73 0 : cachedNodeStats.append(stats);
74 0 : }
75 : }
76 :
77 : // Try to retrieve the CNodeStateStats for each node.
78 : {
79 0 : TRY_LOCK(cs_main, lockMain);
80 0 : if (lockMain)
81 : {
82 0 : BOOST_FOREACH(CNodeCombinedStats &stats, cachedNodeStats)
83 0 : stats.fNodeStateStatsAvailable = GetNodeStateStats(stats.nodeStats.nodeid, stats.nodeStateStats);
84 : }
85 : }
86 :
87 0 : if (sortColumn >= 0)
88 : // sort cacheNodeStats (use stable sort to prevent rows jumping around unneceesarily)
89 0 : qStableSort(cachedNodeStats.begin(), cachedNodeStats.end(), NodeLessThan(sortColumn, sortOrder));
90 :
91 : // build index map
92 0 : mapNodeRows.clear();
93 0 : int row = 0;
94 0 : Q_FOREACH (const CNodeCombinedStats& stats, cachedNodeStats)
95 0 : mapNodeRows.insert(std::pair<NodeId, int>(stats.nodeStats.nodeid, row++));
96 : }
97 :
98 : int size() const
99 : {
100 0 : return cachedNodeStats.size();
101 : }
102 :
103 0 : CNodeCombinedStats *index(int idx)
104 : {
105 0 : if (idx >= 0 && idx < cachedNodeStats.size())
106 0 : return &cachedNodeStats[idx];
107 :
108 : return 0;
109 : }
110 : };
111 :
112 0 : PeerTableModel::PeerTableModel(ClientModel *parent) :
113 : QAbstractTableModel(parent),
114 : clientModel(parent),
115 0 : timer(0)
116 : {
117 0 : columns << tr("Node/Service") << tr("User Agent") << tr("Ping Time");
118 0 : priv = new PeerTablePriv();
119 : // default to unsorted
120 0 : priv->sortColumn = -1;
121 :
122 : // set up timer for auto refresh
123 0 : timer = new QTimer();
124 0 : connect(timer, SIGNAL(timeout()), SLOT(refresh()));
125 0 : timer->setInterval(MODEL_UPDATE_DELAY);
126 :
127 : // load initial data
128 0 : refresh();
129 0 : }
130 :
131 0 : void PeerTableModel::startAutoRefresh()
132 : {
133 0 : timer->start();
134 0 : }
135 :
136 0 : void PeerTableModel::stopAutoRefresh()
137 : {
138 0 : timer->stop();
139 0 : }
140 :
141 0 : int PeerTableModel::rowCount(const QModelIndex &parent) const
142 : {
143 : Q_UNUSED(parent);
144 0 : return priv->size();
145 : }
146 :
147 0 : int PeerTableModel::columnCount(const QModelIndex &parent) const
148 : {
149 : Q_UNUSED(parent);
150 0 : return columns.length();;
151 : }
152 :
153 0 : QVariant PeerTableModel::data(const QModelIndex &index, int role) const
154 : {
155 0 : if(!index.isValid())
156 : return QVariant();
157 :
158 0 : CNodeCombinedStats *rec = static_cast<CNodeCombinedStats*>(index.internalPointer());
159 :
160 0 : if (role == Qt::DisplayRole) {
161 0 : switch(index.column())
162 : {
163 : case Address:
164 0 : return QString::fromStdString(rec->nodeStats.addrName);
165 : case Subversion:
166 0 : return QString::fromStdString(rec->nodeStats.cleanSubVer);
167 : case Ping:
168 0 : return GUIUtil::formatPingTime(rec->nodeStats.dPingTime);
169 : }
170 0 : } else if (role == Qt::TextAlignmentRole) {
171 0 : if (index.column() == Ping)
172 0 : return (QVariant)(Qt::AlignRight | Qt::AlignVCenter);
173 : }
174 :
175 : return QVariant();
176 : }
177 :
178 0 : QVariant PeerTableModel::headerData(int section, Qt::Orientation orientation, int role) const
179 : {
180 0 : if(orientation == Qt::Horizontal)
181 : {
182 0 : if(role == Qt::DisplayRole && section < columns.size())
183 : {
184 0 : return columns[section];
185 : }
186 : }
187 : return QVariant();
188 : }
189 :
190 0 : Qt::ItemFlags PeerTableModel::flags(const QModelIndex &index) const
191 : {
192 0 : if(!index.isValid())
193 : return 0;
194 :
195 : Qt::ItemFlags retval = Qt::ItemIsSelectable | Qt::ItemIsEnabled;
196 : return retval;
197 : }
198 :
199 0 : QModelIndex PeerTableModel::index(int row, int column, const QModelIndex &parent) const
200 : {
201 : Q_UNUSED(parent);
202 0 : CNodeCombinedStats *data = priv->index(row);
203 :
204 0 : if (data)
205 0 : return createIndex(row, column, data);
206 : return QModelIndex();
207 : }
208 :
209 0 : const CNodeCombinedStats *PeerTableModel::getNodeStats(int idx)
210 : {
211 0 : return priv->index(idx);
212 : }
213 :
214 0 : void PeerTableModel::refresh()
215 : {
216 0 : Q_EMIT layoutAboutToBeChanged();
217 0 : priv->refreshPeers();
218 0 : Q_EMIT layoutChanged();
219 0 : }
220 :
221 0 : int PeerTableModel::getRowByNodeId(NodeId nodeid)
222 : {
223 0 : std::map<NodeId, int>::iterator it = priv->mapNodeRows.find(nodeid);
224 0 : if (it == priv->mapNodeRows.end())
225 : return -1;
226 :
227 0 : return it->second;
228 : }
229 :
230 0 : void PeerTableModel::sort(int column, Qt::SortOrder order)
231 : {
232 0 : priv->sortColumn = column;
233 0 : priv->sortOrder = order;
234 0 : refresh();
235 0 : }
|