33 #include <QApplication>
35 #include <QHeaderView>
36 #include <QHostAddress>
38 #include <QMessageBox>
41 #include <QSortFilterProxyModel>
42 #include <QStandardItemModel>
45 #include <QWheelEvent>
81 , m_properties(parent)
86 setUniformRowHeights(
true);
87 setRootIsDecorated(
false);
88 setItemsExpandable(
false);
89 setAllColumnsShowFocus(
true);
90 setEditTriggers(QAbstractItemView::NoEditTriggers);
91 setSelectionMode(QAbstractItemView::ExtendedSelection);
92 header()->setStretchLastSection(
false);
93 header()->setTextElideMode(Qt::ElideRight);
97 m_listModel->setHeaderData(PeerListColumns::COUNTRY, Qt::Horizontal, tr(
"Country/Region"));
98 m_listModel->setHeaderData(PeerListColumns::IP, Qt::Horizontal, tr(
"IP"));
99 m_listModel->setHeaderData(PeerListColumns::PORT, Qt::Horizontal, tr(
"Port"));
100 m_listModel->setHeaderData(PeerListColumns::FLAGS, Qt::Horizontal, tr(
"Flags"));
101 m_listModel->setHeaderData(PeerListColumns::CONNECTION, Qt::Horizontal, tr(
"Connection"));
102 m_listModel->setHeaderData(PeerListColumns::CLIENT, Qt::Horizontal, tr(
"Client",
"i.e.: Client application"));
104 m_listModel->setHeaderData(PeerListColumns::DOWN_SPEED, Qt::Horizontal, tr(
"Down Speed",
"i.e: Download speed"));
105 m_listModel->setHeaderData(PeerListColumns::UP_SPEED, Qt::Horizontal, tr(
"Up Speed",
"i.e: Upload speed"));
106 m_listModel->setHeaderData(PeerListColumns::TOT_DOWN, Qt::Horizontal, tr(
"Downloaded",
"i.e: total data downloaded"));
107 m_listModel->setHeaderData(PeerListColumns::TOT_UP, Qt::Horizontal, tr(
"Uploaded",
"i.e: total data uploaded"));
108 m_listModel->setHeaderData(PeerListColumns::RELEVANCE, Qt::Horizontal, tr(
"Relevance",
"i.e: How relevant this peer is to us. How many pieces it has that we don't."));
109 m_listModel->setHeaderData(PeerListColumns::DOWNLOADING_PIECE, Qt::Horizontal, tr(
"Files",
"i.e. files that are being downloaded right now"));
111 m_listModel->setHeaderData(PeerListColumns::PORT, Qt::Horizontal, QVariant(Qt::AlignRight | Qt::AlignVCenter), Qt::TextAlignmentRole);
113 m_listModel->setHeaderData(PeerListColumns::DOWN_SPEED, Qt::Horizontal, QVariant(Qt::AlignRight | Qt::AlignVCenter), Qt::TextAlignmentRole);
114 m_listModel->setHeaderData(PeerListColumns::UP_SPEED, Qt::Horizontal, QVariant(Qt::AlignRight | Qt::AlignVCenter), Qt::TextAlignmentRole);
115 m_listModel->setHeaderData(PeerListColumns::TOT_DOWN, Qt::Horizontal, QVariant(Qt::AlignRight | Qt::AlignVCenter), Qt::TextAlignmentRole);
116 m_listModel->setHeaderData(PeerListColumns::TOT_UP, Qt::Horizontal, QVariant(Qt::AlignRight | Qt::AlignVCenter), Qt::TextAlignmentRole);
117 m_listModel->setHeaderData(PeerListColumns::RELEVANCE, Qt::Horizontal, QVariant(Qt::AlignRight | Qt::AlignVCenter), Qt::TextAlignmentRole);
122 m_proxyModel->setSortCaseSensitivity(Qt::CaseInsensitive);
124 hideColumn(PeerListColumns::IP_HIDDEN);
128 hideColumn(PeerListColumns::COUNTRY);
130 bool atLeastOne =
false;
131 for (
int i = 0; i < PeerListColumns::IP_HIDDEN; ++i)
133 if (!isColumnHidden(i))
140 setColumnHidden(PeerListColumns::IP,
false);
144 for (
int i = 0; i < PeerListColumns::IP_HIDDEN; ++i)
146 if ((columnWidth(i) <= 0) && !isColumnHidden(i))
147 resizeColumnToContents(i);
150 setContextMenuPolicy(Qt::CustomContextMenu);
153 setSortingEnabled(
true);
157 header()->setContextMenuPolicy(Qt::CustomContextMenu);
164 const auto *copyHotkey =
new QShortcut(QKeySequence::Copy,
this,
nullptr,
nullptr, Qt::WidgetShortcut);
170 unused.setVerticalHeader(this->header());
171 this->header()->setParent(
this);
172 unused.setVerticalHeader(
new QHeaderView(Qt::Horizontal));
182 QMenu *menu =
new QMenu(
this);
183 menu->setAttribute(Qt::WA_DeleteOnClose);
184 menu->setTitle(tr(
"Column visibility"));
186 for (
int i = 0; i < PeerListColumns::IP_HIDDEN; ++i)
191 QAction *myAct = menu->addAction(
m_listModel->headerData(i, Qt::Horizontal, Qt::DisplayRole).toString());
192 myAct->setCheckable(
true);
193 myAct->setChecked(!isColumnHidden(i));
197 connect(menu, &QMenu::triggered,
this, [
this](
const QAction *
action)
200 for (
int i = 0; i < PeerListColumns::IP_HIDDEN; ++i)
202 if (!isColumnHidden(i))
209 const int col =
action->data().toInt();
211 if (!isColumnHidden(col) && (visibleCols == 1))
214 setColumnHidden(col, !isColumnHidden(col));
216 if (!isColumnHidden(col) && (columnWidth(col) <= 5))
217 resizeColumnToContents(col);
222 menu->popup(QCursor::pos());
253 showColumn(PeerListColumns::COUNTRY);
254 if (columnWidth(PeerListColumns::COUNTRY) <= 0)
255 resizeColumnToContents(PeerListColumns::COUNTRY);
259 hideColumn(PeerListColumns::COUNTRY);
266 if (!torrent)
return;
268 auto *menu =
new QMenu(
this);
269 menu->setAttribute(Qt::WA_DeleteOnClose);
270 menu->setToolTipsVisible(
true);
273 ,
this, [
this, torrent]()
276 const int peerCount = std::count_if(peersList.cbegin(), peersList.cend(), [torrent](
const BitTorrent::PeerAddress &peer)
278 return torrent->connectPeer(peer);
280 if (peerCount < peersList.length())
281 QMessageBox::information(
this, tr(
"Adding peers"), tr(
"Some peers cannot be added. Check the Log for details."));
282 else if (peerCount > 0)
283 QMessageBox::information(
this, tr(
"Adding peers"), tr(
"Peers are added to this torrent."));
287 menu->addSeparator();
292 const auto disableAction = [](QAction *
action,
const QString &tooltip)
294 action->setEnabled(
false);
295 action->setToolTip(tooltip);
299 disableAction(addNewPeer, tr(
"Cannot add peers to a private torrent"));
301 disableAction(addNewPeer, tr(
"Cannot add peers when the torrent is checking"));
303 disableAction(addNewPeer, tr(
"Cannot add peers when the torrent is queued"));
305 if (selectionModel()->selectedRows().isEmpty())
307 const QString tooltip = tr(
"No peer was selected");
308 disableAction(copyPeers, tooltip);
309 disableAction(banPeers, tooltip);
312 menu->popup(QCursor::pos());
318 const QModelIndexList selectedIndexes = selectionModel()->selectedRows();
320 QVector<QString> selectedIPs;
321 selectedIPs.reserve(selectedIndexes.size());
323 for (
const QModelIndex &index : selectedIndexes)
326 const QString ip =
m_listModel->item(row, PeerListColumns::IP_HIDDEN)->text();
331 const QMessageBox::StandardButton btn = QMessageBox::question(
this, tr(
"Ban peer permanently")
332 , tr(
"Are you sure you want to permanently ban the selected peers?"));
333 if (btn != QMessageBox::Yes)
return;
335 for (
const QString &ip : selectedIPs)
338 LogMsg(tr(
"Peer \"%1\" is manually banned").arg(ip));
346 const QModelIndexList selectedIndexes = selectionModel()->selectedRows();
347 QStringList selectedPeers;
349 for (
const QModelIndex &index : selectedIndexes)
352 const QString ip =
m_listModel->item(row, PeerListColumns::IP_HIDDEN)->text();
353 const QString port =
m_listModel->item(row, PeerListColumns::PORT)->text();
355 if (!ip.contains(
'.'))
356 selectedPeers << (
'[' + ip +
"]:" + port);
358 selectedPeers << (ip +
':' + port);
361 QApplication::clipboard()->setText(selectedPeers.join(
'\n'));
385 if (!torrent)
return;
387 const QVector<BitTorrent::PeerInfo> peers = torrent->
peers();
388 QSet<PeerEndpoint> existingPeers;
390 existingPeers << i.key();
394 if (peer.address().ip.isNull())
continue;
396 bool isNewPeer =
false;
401 existingPeers.remove(peerEndpoint);
408 QStandardItem *item =
m_peerItems.take(peerEndpoint);
410 QSet<QStandardItem *> &items =
m_itemsByIP[peerEndpoint.address.ip];
422 const QString peerIp = peerEndpoint.address.ip.toString();
423 const Qt::Alignment intDataTextAlignment = Qt::AlignRight | Qt::AlignVCenter;
425 const auto setModelData =
426 [
this] (
const int row,
const int column,
const QString &displayData
427 ,
const QVariant &underlyingData,
const Qt::Alignment textAlignmentData = {}
428 ,
const QString &toolTip = {})
430 const QMap<int, QVariant> data =
432 {Qt::DisplayRole, displayData},
434 {Qt::TextAlignmentRole, QVariant {textAlignmentData}},
435 {Qt::ToolTipRole, toolTip}
448 setModelData(row, PeerListColumns::IP, peerIp, peerIp, {}, peerIp);
449 setModelData(row, PeerListColumns::PORT, QString::number(peer.
address().
port), peer.
address().
port, intDataTextAlignment);
450 setModelData(row, PeerListColumns::IP_HIDDEN, peerIp, peerIp);
453 m_itemsByIP[peerEndpoint.address.ip].insert(itemIter.value());
456 const int row = (*itemIter)->row();
461 const QString client = peer.
client().toHtmlEscaped();
462 setModelData(row, PeerListColumns::CLIENT, client, client, {}, client);
465 setModelData(row, PeerListColumns::DOWN_SPEED, downSpeed, peer.
payloadDownSpeed(), intDataTextAlignment);
467 setModelData(row, PeerListColumns::UP_SPEED, upSpeed, peer.
payloadUpSpeed(), intDataTextAlignment);
469 setModelData(row, PeerListColumns::TOT_DOWN, totalDown, peer.
totalDownload(), intDataTextAlignment);
471 setModelData(row, PeerListColumns::TOT_UP, totalUp, peer.
totalUpload(), intDataTextAlignment);
474 const QStringList downloadingFiles {torrent->
hasMetadata()
477 const QString downloadingFilesDisplayValue = downloadingFiles.join(
';');
478 setModelData(row, PeerListColumns::DOWNLOADING_PIECE, downloadingFilesDisplayValue, downloadingFilesDisplayValue, {}, downloadingFiles.join(QLatin1Char(
'\n')));
497 if (hostname.isEmpty())
500 const QSet<QStandardItem *> items =
m_itemsByIP.value(ip);
501 for (QStandardItem *item : items)
502 item->setData(hostname, Qt::DisplayRole);
507 if (col == PeerListColumns::COUNTRY)
515 if (event->modifiers() & Qt::ShiftModifier)
519 QWheelEvent scrollHEvent(event->position(), event->globalPosition()
520 , event->pixelDelta(), event->angleDelta().transposed(), event->buttons()
521 , event->modifiers(), event->phase(), event->inverted(), event->source());
522 QTreeView::wheelEvent(&scrollHEvent);
526 QTreeView::wheelEvent(event);
qlonglong totalUpload() const
qlonglong totalDownload() const
QString flagsDescription() const
int payloadUpSpeed() const
int payloadDownSpeed() const
QString connectionType() const
PeerAddress address() const
int downloadingPieceIndex() const
static Session * instance()
void banIP(const QString &ip)
virtual QVector< PeerInfo > peers() const =0
virtual bool isChecking() const =0
virtual bool isQueued() const =0
virtual bool isPrivate() const =0
virtual TorrentInfo info() const =0
virtual bool hasMetadata() const =0
QStringList filesForPiece(int pieceIndex) const
static QString CountryName(const QString &countryISOCode)
void ipResolved(const QHostAddress &ip, const QString &hostname)
void resolve(const QHostAddress &ip)
static QVector< BitTorrent::PeerAddress > askForPeers(QWidget *parent)
bool resolvePeerCountries() const
static Preferences * instance()
void setPeerListState(const QByteArray &state)
bool getHideZeroValues() const
static UIThemeManager * instance()
QIcon getFlagIcon(const QString &countryIsoCode) const
QIcon getIcon(const QString &iconId, const QString &fallback={}) const
constexpr std::add_const_t< T > & asConst(T &t) noexcept
void LogMsg(const QString &message, const Log::MsgType &type)
QString friendlyUnit(qint64 bytes, bool isSpeed=false)
QString fromDouble(double n, int precision)
BitTorrent::PeerAddress address