Merge #8874: Multiple Selection for peer and ban tables

1077577 Fix auto-deselection of peers (Andrew Chow)
addfdeb Multiple Selection for peer and ban tables (Andrew Chow)
This commit is contained in:
Wladimir J. van der Laan 2016-11-09 09:24:30 +01:00
commit e9847303e7
No known key found for this signature in database
GPG key ID: 74810B012346C9A6
4 changed files with 77 additions and 54 deletions

View file

@ -291,17 +291,11 @@ void copyEntryData(QAbstractItemView *view, int column, int role)
} }
} }
QVariant getEntryData(QAbstractItemView *view, int column, int role) QList<QModelIndex> getEntryData(QAbstractItemView *view, int column)
{ {
if(!view || !view->selectionModel()) if(!view || !view->selectionModel())
return QVariant(); return QList<QModelIndex>();
QModelIndexList selection = view->selectionModel()->selectedRows(column); return view->selectionModel()->selectedRows(column);
if(!selection.isEmpty()) {
// Return first item
return (selection.at(0).data(role));
}
return QVariant();
} }
QString getSaveFileName(QWidget *parent, const QString &caption, const QString &dir, QString getSaveFileName(QWidget *parent, const QString &caption, const QString &dir,

View file

@ -67,10 +67,9 @@ namespace GUIUtil
/** Return a field of the currently selected entry as a QString. Does nothing if nothing /** Return a field of the currently selected entry as a QString. Does nothing if nothing
is selected. is selected.
@param[in] column Data column to extract from the model @param[in] column Data column to extract from the model
@param[in] role Data role to extract from the model
@see TransactionView::copyLabel, TransactionView::copyAmount, TransactionView::copyAddress @see TransactionView::copyLabel, TransactionView::copyAmount, TransactionView::copyAddress
*/ */
QVariant getEntryData(QAbstractItemView *view, int column, int role); QList<QModelIndex> getEntryData(QAbstractItemView *view, int column);
void setClipboard(const QString& str); void setClipboard(const QString& str);

View file

@ -343,7 +343,6 @@ RPCConsole::RPCConsole(const PlatformStyle *_platformStyle, QWidget *parent) :
ui(new Ui::RPCConsole), ui(new Ui::RPCConsole),
clientModel(0), clientModel(0),
historyPtr(0), historyPtr(0),
cachedNodeid(-1),
platformStyle(_platformStyle), platformStyle(_platformStyle),
peersTableContextMenu(0), peersTableContextMenu(0),
banTableContextMenu(0), banTableContextMenu(0),
@ -469,7 +468,7 @@ void RPCConsole::setClientModel(ClientModel *model)
ui->peerWidget->verticalHeader()->hide(); ui->peerWidget->verticalHeader()->hide();
ui->peerWidget->setEditTriggers(QAbstractItemView::NoEditTriggers); ui->peerWidget->setEditTriggers(QAbstractItemView::NoEditTriggers);
ui->peerWidget->setSelectionBehavior(QAbstractItemView::SelectRows); ui->peerWidget->setSelectionBehavior(QAbstractItemView::SelectRows);
ui->peerWidget->setSelectionMode(QAbstractItemView::SingleSelection); ui->peerWidget->setSelectionMode(QAbstractItemView::ExtendedSelection);
ui->peerWidget->setContextMenuPolicy(Qt::CustomContextMenu); ui->peerWidget->setContextMenuPolicy(Qt::CustomContextMenu);
ui->peerWidget->setColumnWidth(PeerTableModel::Address, ADDRESS_COLUMN_WIDTH); ui->peerWidget->setColumnWidth(PeerTableModel::Address, ADDRESS_COLUMN_WIDTH);
ui->peerWidget->setColumnWidth(PeerTableModel::Subversion, SUBVERSION_COLUMN_WIDTH); ui->peerWidget->setColumnWidth(PeerTableModel::Subversion, SUBVERSION_COLUMN_WIDTH);
@ -477,11 +476,11 @@ void RPCConsole::setClientModel(ClientModel *model)
ui->peerWidget->horizontalHeader()->setStretchLastSection(true); ui->peerWidget->horizontalHeader()->setStretchLastSection(true);
// create peer table context menu actions // create peer table context menu actions
QAction* disconnectAction = new QAction(tr("&Disconnect Node"), this); QAction* disconnectAction = new QAction(tr("&Disconnect"), this);
QAction* banAction1h = new QAction(tr("Ban Node for") + " " + tr("1 &hour"), this); QAction* banAction1h = new QAction(tr("Ban for") + " " + tr("1 &hour"), this);
QAction* banAction24h = new QAction(tr("Ban Node for") + " " + tr("1 &day"), this); QAction* banAction24h = new QAction(tr("Ban for") + " " + tr("1 &day"), this);
QAction* banAction7d = new QAction(tr("Ban Node for") + " " + tr("1 &week"), this); QAction* banAction7d = new QAction(tr("Ban for") + " " + tr("1 &week"), this);
QAction* banAction365d = new QAction(tr("Ban Node for") + " " + tr("1 &year"), this); QAction* banAction365d = new QAction(tr("Ban for") + " " + tr("1 &year"), this);
// create peer table context menu // create peer table context menu
peersTableContextMenu = new QMenu(); peersTableContextMenu = new QMenu();
@ -514,7 +513,9 @@ void RPCConsole::setClientModel(ClientModel *model)
this, SLOT(peerSelected(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 // peer table signal handling - update peer details when new nodes are added to the model
connect(model->getPeerTableModel(), SIGNAL(layoutChanged()), this, SLOT(peerLayoutChanged())); connect(model->getPeerTableModel(), SIGNAL(layoutChanged()), this, SLOT(peerLayoutChanged()));
// peer table signal handling - cache selected node ids
connect(model->getPeerTableModel(), SIGNAL(layoutAboutToChange()), this, SLOT(peerLayoutAboutToChange()));
// set up ban table // set up ban table
ui->banlistWidget->setModel(model->getBanTableModel()); ui->banlistWidget->setModel(model->getBanTableModel());
ui->banlistWidget->verticalHeader()->hide(); ui->banlistWidget->verticalHeader()->hide();
@ -527,7 +528,7 @@ void RPCConsole::setClientModel(ClientModel *model)
ui->banlistWidget->horizontalHeader()->setStretchLastSection(true); ui->banlistWidget->horizontalHeader()->setStretchLastSection(true);
// create ban table context menu action // create ban table context menu action
QAction* unbanAction = new QAction(tr("&Unban Node"), this); QAction* unbanAction = new QAction(tr("&Unban"), this);
// create ban table context menu // create ban table context menu
banTableContextMenu = new QMenu(); banTableContextMenu = new QMenu();
@ -825,6 +826,17 @@ void RPCConsole::peerSelected(const QItemSelection &selected, const QItemSelecti
updateNodeDetail(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() void RPCConsole::peerLayoutChanged()
{ {
if (!clientModel || !clientModel->getPeerTableModel()) if (!clientModel || !clientModel->getPeerTableModel())
@ -834,7 +846,7 @@ void RPCConsole::peerLayoutChanged()
bool fUnselect = false; bool fUnselect = false;
bool fReselect = false; bool fReselect = false;
if (cachedNodeid == -1) // no node selected yet if (cachedNodeids.empty()) // no node selected yet
return; return;
// find the currently selected row // find the currently selected row
@ -846,7 +858,7 @@ void RPCConsole::peerLayoutChanged()
// check if our detail node has a row in the table (it may not necessarily // 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) // be at selectedRow since its position can change after a layout change)
int detailNodeRow = clientModel->getPeerTableModel()->getRowByNodeId(cachedNodeid); int detailNodeRow = clientModel->getPeerTableModel()->getRowByNodeId(cachedNodeids.first());
if (detailNodeRow < 0) if (detailNodeRow < 0)
{ {
@ -872,7 +884,10 @@ void RPCConsole::peerLayoutChanged()
if (fReselect) if (fReselect)
{ {
ui->peerWidget->selectRow(detailNodeRow); for(int i = 0; i < cachedNodeids.size(); i++)
{
ui->peerWidget->selectRow(clientModel->getPeerTableModel()->getRowByNodeId(cachedNodeids.at(i)));
}
} }
if (stats) if (stats)
@ -881,9 +896,6 @@ void RPCConsole::peerLayoutChanged()
void RPCConsole::updateNodeDetail(const CNodeCombinedStats *stats) void RPCConsole::updateNodeDetail(const CNodeCombinedStats *stats)
{ {
// Update cached nodeid
cachedNodeid = stats->nodeStats.nodeid;
// update the detail ui with latest node information // update the detail ui with latest node information
QString peerAddrDetails(QString::fromStdString(stats->nodeStats.addrName) + " "); QString peerAddrDetails(QString::fromStdString(stats->nodeStats.addrName) + " ");
peerAddrDetails += tr("(node id: %1)").arg(QString::number(stats->nodeStats.nodeid)); peerAddrDetails += tr("(node id: %1)").arg(QString::number(stats->nodeStats.nodeid));
@ -973,33 +985,44 @@ void RPCConsole::disconnectSelectedNode()
{ {
if(!g_connman) if(!g_connman)
return; return;
// Get currently selected peer address
NodeId id = GUIUtil::getEntryData(ui->peerWidget, 0, PeerTableModel::NetNodeId).toInt(); // Get selected peer addresses
// Find the node, disconnect it and clear the selected node QList<QModelIndex> nodes = GUIUtil::getEntryData(ui->peerWidget, 0);
if(g_connman->DisconnectNode(id)) for(int i = 0; i < nodes.count(); i++)
clearSelectedNode(); {
// Get currently selected peer address
NodeId id = nodes.at(i).data(PeerTableModel::NetNodeId).toInt();
// Find the node, disconnect it and clear the selected node
if(g_connman->DisconnectNode(id))
clearSelectedNode();
}
} }
void RPCConsole::banSelectedNode(int bantime) void RPCConsole::banSelectedNode(int bantime)
{ {
if (!clientModel || !g_connman) if (!clientModel || !g_connman)
return; return;
// Get selected peer addresses
QList<QModelIndex> nodes = GUIUtil::getEntryData(ui->peerWidget, 0);
for(int i = 0; i < nodes.count(); i++)
{
// Get currently selected peer address
NodeId id = nodes.at(i).data(PeerTableModel::NetNodeId).toInt();
if(cachedNodeid == -1) // Get currently selected peer address
return; int detailNodeRow = clientModel->getPeerTableModel()->getRowByNodeId(id);
if(detailNodeRow < 0)
return;
// Get currently selected peer address // Find possible nodes, ban it and clear the selected node
int detailNodeRow = clientModel->getPeerTableModel()->getRowByNodeId(cachedNodeid); const CNodeCombinedStats *stats = clientModel->getPeerTableModel()->getNodeStats(detailNodeRow);
if(detailNodeRow < 0) if(stats) {
return; g_connman->Ban(stats->nodeStats.addr, BanReasonManuallyAdded, bantime);
}
// Find possible nodes, ban it and clear the selected node
const CNodeCombinedStats *stats = clientModel->getPeerTableModel()->getNodeStats(detailNodeRow);
if(stats) {
g_connman->Ban(stats->nodeStats.addr, BanReasonManuallyAdded, bantime);
clearSelectedNode();
clientModel->getBanTableModel()->refresh();
} }
clearSelectedNode();
clientModel->getBanTableModel()->refresh();
} }
void RPCConsole::unbanSelectedNode() void RPCConsole::unbanSelectedNode()
@ -1007,22 +1030,27 @@ void RPCConsole::unbanSelectedNode()
if (!clientModel) if (!clientModel)
return; return;
// Get currently selected ban address // Get selected ban addresses
QString strNode = GUIUtil::getEntryData(ui->banlistWidget, 0, BanTableModel::Address).toString(); QList<QModelIndex> nodes = GUIUtil::getEntryData(ui->banlistWidget, 0);
CSubNet possibleSubnet; for(int i = 0; i < nodes.count(); i++)
LookupSubNet(strNode.toStdString().c_str(), possibleSubnet);
if (possibleSubnet.IsValid() && g_connman)
{ {
g_connman->Unban(possibleSubnet); // Get currently selected ban address
clientModel->getBanTableModel()->refresh(); QString strNode = nodes.at(i).data(BanTableModel::Address).toString();
CSubNet possibleSubnet;
LookupSubNet(strNode.toStdString().c_str(), possibleSubnet);
if (possibleSubnet.IsValid() && g_connman)
{
g_connman->Unban(possibleSubnet);
clientModel->getBanTableModel()->refresh();
}
} }
} }
void RPCConsole::clearSelectedNode() void RPCConsole::clearSelectedNode()
{ {
ui->peerWidget->selectionModel()->clearSelection(); ui->peerWidget->selectionModel()->clearSelection();
cachedNodeid = -1; cachedNodeids.clear();
ui->detailWidget->hide(); ui->detailWidget->hide();
ui->peerHeading->setText(tr("Select a peer to view detailed information.")); ui->peerHeading->setText(tr("Select a peer to view detailed information."));
} }

View file

@ -98,6 +98,8 @@ public Q_SLOTS:
void scrollToEnd(); void scrollToEnd();
/** Handle selection of peer in peers list */ /** Handle selection of peer in peers list */
void peerSelected(const QItemSelection &selected, const QItemSelection &deselected); void peerSelected(const QItemSelection &selected, const QItemSelection &deselected);
/** Handle selection caching before update */
void peerLayoutAboutToChange();
/** Handle updated peer information */ /** Handle updated peer information */
void peerLayoutChanged(); void peerLayoutChanged();
/** Disconnect a selected node on the Peers tab */ /** Disconnect a selected node on the Peers tab */
@ -135,7 +137,7 @@ private:
ClientModel *clientModel; ClientModel *clientModel;
QStringList history; QStringList history;
int historyPtr; int historyPtr;
NodeId cachedNodeid; QList<NodeId> cachedNodeids;
const PlatformStyle *platformStyle; const PlatformStyle *platformStyle;
RPCTimerInterface *rpcTimerInterface; RPCTimerInterface *rpcTimerInterface;
QMenu *peersTableContextMenu; QMenu *peersTableContextMenu;