Browse Source

Add (experimental) I2P support

PR #18717.
Closes #16257.
adaptive-webui-19844
Vladimir Golovnev 2 years ago committed by GitHub
parent
commit
cdded6cef7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 7
      src/base/algorithm.h
  2. 8
      src/base/bittorrent/session.h
  3. 84
      src/base/bittorrent/sessionimpl.cpp
  4. 12
      src/base/bittorrent/sessionimpl.h
  5. 15
      src/gui/optionsdialog.cpp
  6. 91
      src/gui/optionsdialog.ui
  7. 139
      src/gui/properties/peerlistwidget.cpp
  8. 3
      src/gui/properties/peerlistwidget.h

7
src/base/algorithm.h

@ -63,4 +63,11 @@ namespace Algorithm
while (it != set.end()) while (it != set.end())
it = (p(*it) ? set.erase(it) : ++it); it = (p(*it) ? set.erase(it) : ++it);
} }
template <typename List>
List sorted(List list)
{
list.sort();
return list;
}
} }

8
src/base/bittorrent/session.h

@ -260,6 +260,14 @@ namespace BitTorrent
virtual void setEncryption(int state) = 0; virtual void setEncryption(int state) = 0;
virtual int maxActiveCheckingTorrents() const = 0; virtual int maxActiveCheckingTorrents() const = 0;
virtual void setMaxActiveCheckingTorrents(int val) = 0; virtual void setMaxActiveCheckingTorrents(int val) = 0;
virtual bool isI2PEnabled() const = 0;
virtual void setI2PEnabled(bool enabled) = 0;
virtual QString I2PAddress() const = 0;
virtual void setI2PAddress(const QString &address) = 0;
virtual int I2PPort() const = 0;
virtual void setI2PPort(int port) = 0;
virtual bool I2PMixedMode() const = 0;
virtual void setI2PMixedMode(bool enabled) = 0;
virtual bool isProxyPeerConnectionsEnabled() const = 0; virtual bool isProxyPeerConnectionsEnabled() const = 0;
virtual void setProxyPeerConnectionsEnabled(bool enabled) = 0; virtual void setProxyPeerConnectionsEnabled(bool enabled) = 0;
virtual ChokingAlgorithm chokingAlgorithm() const = 0; virtual ChokingAlgorithm chokingAlgorithm() const = 0;

84
src/base/bittorrent/sessionimpl.cpp

@ -520,16 +520,12 @@ SessionImpl::SessionImpl(QObject *parent)
, m_requestQueueSize(BITTORRENT_SESSION_KEY(u"RequestQueueSize"_qs), 500) , m_requestQueueSize(BITTORRENT_SESSION_KEY(u"RequestQueueSize"_qs), 500)
, m_isExcludedFileNamesEnabled(BITTORRENT_KEY(u"ExcludedFileNamesEnabled"_qs), false) , m_isExcludedFileNamesEnabled(BITTORRENT_KEY(u"ExcludedFileNamesEnabled"_qs), false)
, m_excludedFileNames(BITTORRENT_SESSION_KEY(u"ExcludedFileNames"_qs)) , m_excludedFileNames(BITTORRENT_SESSION_KEY(u"ExcludedFileNames"_qs))
, m_bannedIPs(u"State/BannedIPs"_qs , m_bannedIPs(u"State/BannedIPs"_qs, QStringList(), Algorithm::sorted<QStringList>)
, QStringList()
, [](const QStringList &value)
{
QStringList tmp = value;
tmp.sort();
return tmp;
}
)
, m_resumeDataStorageType(BITTORRENT_SESSION_KEY(u"ResumeDataStorageType"_qs), ResumeDataStorageType::Legacy) , m_resumeDataStorageType(BITTORRENT_SESSION_KEY(u"ResumeDataStorageType"_qs), ResumeDataStorageType::Legacy)
, m_isI2PEnabled {BITTORRENT_SESSION_KEY(u"I2P/Enabled"_qs), false}
, m_I2PAddress {BITTORRENT_SESSION_KEY(u"I2P/Address"_qs), u"127.0.0.1"_qs}
, m_I2PPort {BITTORRENT_SESSION_KEY(u"I2P/Port"_qs), 7656}
, m_I2PMixedMode {BITTORRENT_SESSION_KEY(u"I2P/MixedMode"_qs), false}
, m_seedingLimitTimer {new QTimer(this)} , m_seedingLimitTimer {new QTimer(this)}
, m_resumeDataTimer {new QTimer(this)} , m_resumeDataTimer {new QTimer(this)}
, m_ioThread {new QThread} , m_ioThread {new QThread}
@ -1630,6 +1626,20 @@ lt::settings_pack SessionImpl::loadLTSettings() const
settingsPack.set_int(lt::settings_pack::active_checking, maxActiveCheckingTorrents()); settingsPack.set_int(lt::settings_pack::active_checking, maxActiveCheckingTorrents());
// I2P
if (isI2PEnabled())
{
settingsPack.set_str(lt::settings_pack::i2p_hostname, I2PAddress().toStdString());
settingsPack.set_int(lt::settings_pack::i2p_port, I2PPort());
settingsPack.set_bool(lt::settings_pack::allow_i2p_mixed, I2PMixedMode());
}
else
{
settingsPack.set_str(lt::settings_pack::i2p_hostname, "");
settingsPack.set_int(lt::settings_pack::i2p_port, 0);
settingsPack.set_bool(lt::settings_pack::allow_i2p_mixed, false);
}
// proxy // proxy
settingsPack.set_int(lt::settings_pack::proxy_type, lt::settings_pack::none); settingsPack.set_int(lt::settings_pack::proxy_type, lt::settings_pack::none);
if (Preferences::instance()->useProxyForBT()) if (Preferences::instance()->useProxyForBT())
@ -3533,6 +3543,62 @@ void SessionImpl::setMaxActiveCheckingTorrents(const int val)
configureDeferred(); configureDeferred();
} }
bool SessionImpl::isI2PEnabled() const
{
return m_isI2PEnabled;
}
void SessionImpl::setI2PEnabled(const bool enabled)
{
if (m_isI2PEnabled != enabled)
{
m_isI2PEnabled = enabled;
configureDeferred();
}
}
QString SessionImpl::I2PAddress() const
{
return m_I2PAddress;
}
void SessionImpl::setI2PAddress(const QString &address)
{
if (m_I2PAddress != address)
{
m_I2PAddress = address;
configureDeferred();
}
}
int SessionImpl::I2PPort() const
{
return m_I2PPort;
}
void SessionImpl::setI2PPort(int port)
{
if (m_I2PPort != port)
{
m_I2PPort = port;
configureDeferred();
}
}
bool SessionImpl::I2PMixedMode() const
{
return m_I2PMixedMode;
}
void SessionImpl::setI2PMixedMode(const bool enabled)
{
if (m_I2PMixedMode != enabled)
{
m_I2PMixedMode = enabled;
configureDeferred();
}
}
bool SessionImpl::isProxyPeerConnectionsEnabled() const bool SessionImpl::isProxyPeerConnectionsEnabled() const
{ {
return m_isProxyPeerConnectionsEnabled; return m_isProxyPeerConnectionsEnabled;

12
src/base/bittorrent/sessionimpl.h

@ -237,6 +237,14 @@ namespace BitTorrent
void setEncryption(int state) override; void setEncryption(int state) override;
int maxActiveCheckingTorrents() const override; int maxActiveCheckingTorrents() const override;
void setMaxActiveCheckingTorrents(int val) override; void setMaxActiveCheckingTorrents(int val) override;
bool isI2PEnabled() const override;
void setI2PEnabled(bool enabled) override;
QString I2PAddress() const override;
void setI2PAddress(const QString &address) override;
int I2PPort() const override;
void setI2PPort(int port) override;
bool I2PMixedMode() const override;
void setI2PMixedMode(bool enabled) override;
bool isProxyPeerConnectionsEnabled() const override; bool isProxyPeerConnectionsEnabled() const override;
void setProxyPeerConnectionsEnabled(bool enabled) override; void setProxyPeerConnectionsEnabled(bool enabled) override;
ChokingAlgorithm chokingAlgorithm() const override; ChokingAlgorithm chokingAlgorithm() const override;
@ -675,6 +683,10 @@ namespace BitTorrent
CachedSettingValue<QStringList> m_excludedFileNames; CachedSettingValue<QStringList> m_excludedFileNames;
CachedSettingValue<QStringList> m_bannedIPs; CachedSettingValue<QStringList> m_bannedIPs;
CachedSettingValue<ResumeDataStorageType> m_resumeDataStorageType; CachedSettingValue<ResumeDataStorageType> m_resumeDataStorageType;
CachedSettingValue<bool> m_isI2PEnabled;
CachedSettingValue<QString> m_I2PAddress;
CachedSettingValue<int> m_I2PPort;
CachedSettingValue<bool> m_I2PMixedMode;
bool m_isRestored = false; bool m_isRestored = false;

15
src/gui/optionsdialog.cpp

@ -782,6 +782,11 @@ void OptionsDialog::loadConnectionTabOptions()
m_ui->spinMaxUploadsPerTorrent->setEnabled(false); m_ui->spinMaxUploadsPerTorrent->setEnabled(false);
} }
m_ui->textI2PHost->setText(session->I2PAddress());
m_ui->spinI2PPort->setValue(session->I2PPort());
m_ui->checkI2PMixed->setChecked(session->I2PMixedMode());
m_ui->groupI2P->setChecked(session->isI2PEnabled());
const auto *proxyConfigManager = Net::ProxyConfigurationManager::instance(); const auto *proxyConfigManager = Net::ProxyConfigurationManager::instance();
const Net::ProxyConfiguration proxyConf = proxyConfigManager->proxyConfiguration(); const Net::ProxyConfiguration proxyConf = proxyConfigManager->proxyConfiguration();
@ -831,6 +836,11 @@ void OptionsDialog::loadConnectionTabOptions()
connect(m_ui->textProxyIP, &QLineEdit::textChanged, this, &ThisType::enableApplyButton); connect(m_ui->textProxyIP, &QLineEdit::textChanged, this, &ThisType::enableApplyButton);
connect(m_ui->spinProxyPort, qSpinBoxValueChanged, this, &ThisType::enableApplyButton); connect(m_ui->spinProxyPort, qSpinBoxValueChanged, this, &ThisType::enableApplyButton);
connect(m_ui->textI2PHost, &QLineEdit::textChanged, this, &ThisType::enableApplyButton);
connect(m_ui->spinI2PPort, qSpinBoxValueChanged, this, &ThisType::enableApplyButton);
connect(m_ui->checkI2PMixed, &QCheckBox::toggled, this, &ThisType::enableApplyButton);
connect(m_ui->groupI2P, &QGroupBox::toggled, this, &ThisType::enableApplyButton);
connect(m_ui->checkProxyBitTorrent, &QGroupBox::toggled, this, &ThisType::enableApplyButton); connect(m_ui->checkProxyBitTorrent, &QGroupBox::toggled, this, &ThisType::enableApplyButton);
connect(m_ui->checkProxyBitTorrent, &QGroupBox::toggled, this, &ThisType::adjustProxyOptions); connect(m_ui->checkProxyBitTorrent, &QGroupBox::toggled, this, &ThisType::adjustProxyOptions);
connect(m_ui->checkProxyPeerConnections, &QAbstractButton::toggled, this, &ThisType::enableApplyButton); connect(m_ui->checkProxyPeerConnections, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
@ -862,6 +872,11 @@ void OptionsDialog::saveConnectionTabOptions() const
session->setMaxUploads(getMaxUploads()); session->setMaxUploads(getMaxUploads());
session->setMaxUploadsPerTorrent(getMaxUploadsPerTorrent()); session->setMaxUploadsPerTorrent(getMaxUploadsPerTorrent());
session->setI2PEnabled(m_ui->groupI2P->isChecked());
session->setI2PAddress(m_ui->textI2PHost->text().trimmed());
session->setI2PPort(m_ui->spinI2PPort->value());
session->setI2PMixedMode(m_ui->checkI2PMixed->isChecked());
auto proxyConfigManager = Net::ProxyConfigurationManager::instance(); auto proxyConfigManager = Net::ProxyConfigurationManager::instance();
Net::ProxyConfiguration proxyConf; Net::ProxyConfiguration proxyConf;
proxyConf.type = getProxyType(); proxyConf.type = getProxyType();

91
src/gui/optionsdialog.ui

@ -1572,8 +1572,8 @@ readme[0-9].txt: filter 'readme1.txt', 'readme2.txt' but not 'readme10.txt'.</st
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>377</width> <width>504</width>
<height>756</height> <height>848</height>
</rect> </rect>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout_20"> <layout class="QVBoxLayout" name="verticalLayout_20">
@ -1788,6 +1788,78 @@ readme[0-9].txt: filter 'readme1.txt', 'readme2.txt' but not 'readme10.txt'.</st
</layout> </layout>
</widget> </widget>
</item> </item>
<item>
<widget class="QGroupBox" name="groupI2P">
<property name="title">
<string>I2P (experimental)</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<property name="checked">
<bool>true</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout_40">
<item>
<layout class="QHBoxLayout" name="i2pLayout">
<item>
<widget class="QLabel" name="labelI2PHost">
<property name="text">
<string>Host:</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="textI2PHost"/>
</item>
<item>
<widget class="QLabel" name="labelI2PPort">
<property name="text">
<string>Port:</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="spinI2PPort">
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>65535</number>
</property>
<property name="value">
<number>8080</number>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_22">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<widget class="QCheckBox" name="checkI2PMixed">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;If &amp;quot;mixed mode&amp;quot; is enabled I2P torrents are allowed to also get peers from other sources than the tracker, and connect to regular IPs, not providing any anonymization. This may be useful if the user is not interested in the anonymization of I2P, but still wants to be able to connect to I2P peers.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Mixed mode</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item> <item>
<widget class="QGroupBox" name="groupProxy"> <widget class="QGroupBox" name="groupProxy">
<property name="enabled"> <property name="enabled">
@ -1839,6 +1911,19 @@ readme[0-9].txt: filter 'readme1.txt', 'readme2.txt' but not 'readme10.txt'.</st
</property> </property>
</widget> </widget>
</item> </item>
<item>
<spacer name="horizontalSpacer_24">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout> </layout>
</item> </item>
<item> <item>
@ -1859,7 +1944,7 @@ readme[0-9].txt: filter 'readme1.txt', 'readme2.txt' but not 'readme10.txt'.</st
<string>If checked, hostname lookups are done via the proxy</string> <string>If checked, hostname lookups are done via the proxy</string>
</property> </property>
<property name="text"> <property name="text">
<string>Use proxy for hostname lookups</string> <string>Perform hostname lookup via proxy</string>
</property> </property>
</widget> </widget>
</item> </item>

139
src/gui/properties/peerlistwidget.cpp

@ -1,5 +1,6 @@
/* /*
* Bittorrent Client using Qt and libtorrent. * Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2023 Vladimir Golovnev <glassez@yandex.ru>
* Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org> * Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
@ -85,6 +86,21 @@ uint qHash(const PeerEndpoint &peerEndpoint, const uint seed = 0)
} }
#endif #endif
namespace
{
void setModelData(QStandardItemModel *model, const int row, const int column, const QString &displayData
, const QVariant &underlyingData, const Qt::Alignment textAlignmentData = {}, const QString &toolTip = {})
{
const QMap<int, QVariant> data = {
{Qt::DisplayRole, displayData},
{PeerListSortModel::UnderlyingDataRole, underlyingData},
{Qt::TextAlignmentRole, QVariant {textAlignmentData}},
{Qt::ToolTipRole, toolTip}};
model->setItemData(model->index(row, column), data);
}
}
PeerListWidget::PeerListWidget(PropertiesWidget *parent) PeerListWidget::PeerListWidget(PropertiesWidget *parent)
: QTreeView(parent) : QTreeView(parent)
, m_properties(parent) , m_properties(parent)
@ -404,23 +420,58 @@ void PeerListWidget::loadPeers(const BitTorrent::Torrent *torrent)
if (torrent != m_properties->getCurrentTorrent()) if (torrent != m_properties->getCurrentTorrent())
return; return;
// Remove I2P peers since they will be completely reloaded.
for (QStandardItem *item : asConst(m_I2PPeerItems))
m_listModel->removeRow(item->row());
m_I2PPeerItems.clear();
QSet<PeerEndpoint> existingPeers; QSet<PeerEndpoint> existingPeers;
existingPeers.reserve(m_peerItems.size()); existingPeers.reserve(m_peerItems.size());
for (auto i = m_peerItems.cbegin(); i != m_peerItems.cend(); ++i) for (auto i = m_peerItems.cbegin(); i != m_peerItems.cend(); ++i)
existingPeers.insert(i.key()); existingPeers.insert(i.key());
const bool hideZeroValues = Preferences::instance()->getHideZeroValues();
for (const BitTorrent::PeerInfo &peer : peers) for (const BitTorrent::PeerInfo &peer : peers)
{ {
if (peer.address().ip.isNull()) if (peer.address().ip.isNull())
continue; continue;
bool isNewPeer = false;
updatePeer(torrent, peer, isNewPeer);
if (!isNewPeer)
{
const PeerEndpoint peerEndpoint {peer.address(), peer.connectionType()}; const PeerEndpoint peerEndpoint {peer.address(), peer.connectionType()};
auto itemIter = m_peerItems.find(peerEndpoint);
const bool isNewPeer = (itemIter == m_peerItems.end());
const int row = isNewPeer ? m_listModel->rowCount() : (*itemIter)->row();
if (isNewPeer)
{
m_listModel->insertRow(row);
const bool useI2PSocket = peer.useI2PSocket();
const QString peerIPString = useI2PSocket ? tr("N/A") : peerEndpoint.address.ip.toString();
setModelData(m_listModel, row, PeerListColumns::IP, peerIPString, peerIPString, {}, peerIPString);
const QString peerIPHiddenString = useI2PSocket ? QString() : peerEndpoint.address.ip.toString();
setModelData(m_listModel, row, PeerListColumns::IP_HIDDEN, peerIPHiddenString, peerIPHiddenString);
const QString peerPortString = useI2PSocket ? tr("N/A") : QString::number(peer.address().port);
setModelData(m_listModel, row, PeerListColumns::PORT, peerPortString, peer.address().port, (Qt::AlignRight | Qt::AlignVCenter));
if (useI2PSocket)
{
m_I2PPeerItems.append(m_listModel->item(row, PeerListColumns::IP));
}
else
{
itemIter = m_peerItems.insert(peerEndpoint, m_listModel->item(row, PeerListColumns::IP));
m_itemsByIP[peerEndpoint.address.ip].insert(itemIter.value());
}
}
else
{
existingPeers.remove(peerEndpoint); existingPeers.remove(peerEndpoint);
} }
updatePeer(row, torrent, peer, hideZeroValues);
} }
// Remove peers that are gone // Remove peers that are gone
@ -438,73 +489,51 @@ void PeerListWidget::loadPeers(const BitTorrent::Torrent *torrent)
}); });
} }
void PeerListWidget::updatePeer(const BitTorrent::Torrent *torrent, const BitTorrent::PeerInfo &peer, bool &isNewPeer) void PeerListWidget::updatePeer(const int row, const BitTorrent::Torrent *torrent, const BitTorrent::PeerInfo &peer, const bool hideZeroValues)
{ {
const PeerEndpoint peerEndpoint {peer.address(), peer.connectionType()};
const QString peerIp = peerEndpoint.address.ip.toString();
const Qt::Alignment intDataTextAlignment = Qt::AlignRight | Qt::AlignVCenter; const Qt::Alignment intDataTextAlignment = Qt::AlignRight | Qt::AlignVCenter;
const auto setModelData = const QString client = peer.client().toHtmlEscaped();
[this](const int row, const int column, const QString &displayData setModelData(m_listModel, row, PeerListColumns::CLIENT, client, client, {}, client);
, const QVariant &underlyingData, const Qt::Alignment textAlignmentData = {}
, const QString &toolTip = {})
{
const QMap<int, QVariant> data =
{
{Qt::DisplayRole, displayData},
{PeerListSortModel::UnderlyingDataRole, underlyingData},
{Qt::TextAlignmentRole, QVariant {textAlignmentData}},
{Qt::ToolTipRole, toolTip}
};
m_listModel->setItemData(m_listModel->index(row, column), data);
};
auto itemIter = m_peerItems.find(peerEndpoint); const QString peerIdClient = peer.peerIdClient().toHtmlEscaped();
isNewPeer = (itemIter == m_peerItems.end()); setModelData(m_listModel, row, PeerListColumns::PEERID_CLIENT, peerIdClient, peerIdClient);
if (isNewPeer)
{
// new item
const int row = m_listModel->rowCount();
m_listModel->insertRow(row);
setModelData(row, PeerListColumns::IP, peerIp, peerIp, {}, peerIp); const QString downSpeed = (hideZeroValues && (peer.payloadDownSpeed() <= 0))
setModelData(row, PeerListColumns::PORT, QString::number(peer.address().port), peer.address().port, intDataTextAlignment); ? QString() : Utils::Misc::friendlyUnit(peer.payloadDownSpeed(), true);
setModelData(row, PeerListColumns::IP_HIDDEN, peerIp, peerIp); setModelData(m_listModel, row, PeerListColumns::DOWN_SPEED, downSpeed, peer.payloadDownSpeed(), intDataTextAlignment);
itemIter = m_peerItems.insert(peerEndpoint, m_listModel->item(row, PeerListColumns::IP)); const QString upSpeed = (hideZeroValues && (peer.payloadUpSpeed() <= 0))
m_itemsByIP[peerEndpoint.address.ip].insert(itemIter.value()); ? QString() : Utils::Misc::friendlyUnit(peer.payloadUpSpeed(), true);
} setModelData(m_listModel, row, PeerListColumns::UP_SPEED, upSpeed, peer.payloadUpSpeed(), intDataTextAlignment);
const int row = (*itemIter)->row(); const QString totalDown = (hideZeroValues && (peer.totalDownload() <= 0))
const bool hideValues = Preferences::instance()->getHideZeroValues(); ? QString() : Utils::Misc::friendlyUnit(peer.totalDownload());
setModelData(m_listModel, row, PeerListColumns::TOT_DOWN, totalDown, peer.totalDownload(), intDataTextAlignment);
setModelData(row, PeerListColumns::CONNECTION, peer.connectionType(), peer.connectionType()); const QString totalUp = (hideZeroValues && (peer.totalUpload() <= 0))
setModelData(row, PeerListColumns::FLAGS, peer.flags(), peer.flags(), {}, peer.flagsDescription()); ? QString() : Utils::Misc::friendlyUnit(peer.totalUpload());
const QString client = peer.client().toHtmlEscaped(); setModelData(m_listModel, row, PeerListColumns::TOT_UP, totalUp, peer.totalUpload(), intDataTextAlignment);
setModelData(row, PeerListColumns::CLIENT, client, client, {}, client);
const QString peerIdClient = peer.peerIdClient().toHtmlEscaped(); setModelData(m_listModel, row, PeerListColumns::CONNECTION, peer.connectionType(), peer.connectionType());
setModelData(row, PeerListColumns::PEERID_CLIENT, peerIdClient, peerIdClient); setModelData(m_listModel, row, PeerListColumns::FLAGS, peer.flags(), peer.flags(), {}, peer.flagsDescription());
setModelData(row, PeerListColumns::PROGRESS, (Utils::String::fromDouble(peer.progress() * 100, 1) + u'%'), peer.progress(), intDataTextAlignment); setModelData(m_listModel, row, PeerListColumns::PROGRESS, (Utils::String::fromDouble(peer.progress() * 100, 1) + u'%')
const QString downSpeed = (hideValues && (peer.payloadDownSpeed() <= 0)) ? QString {} : Utils::Misc::friendlyUnit(peer.payloadDownSpeed(), true); , peer.progress(), intDataTextAlignment);
setModelData(row, PeerListColumns::DOWN_SPEED, downSpeed, peer.payloadDownSpeed(), intDataTextAlignment); setModelData(m_listModel, row, PeerListColumns::RELEVANCE, (Utils::String::fromDouble(peer.relevance() * 100, 1) + u'%')
const QString upSpeed = (hideValues && (peer.payloadUpSpeed() <= 0)) ? QString {} : Utils::Misc::friendlyUnit(peer.payloadUpSpeed(), true); , peer.relevance(), intDataTextAlignment);
setModelData(row, PeerListColumns::UP_SPEED, upSpeed, peer.payloadUpSpeed(), intDataTextAlignment);
const QString totalDown = (hideValues && (peer.totalDownload() <= 0)) ? QString {} : Utils::Misc::friendlyUnit(peer.totalDownload());
setModelData(row, PeerListColumns::TOT_DOWN, totalDown, peer.totalDownload(), intDataTextAlignment);
const QString totalUp = (hideValues && (peer.totalUpload() <= 0)) ? QString {} : Utils::Misc::friendlyUnit(peer.totalUpload());
setModelData(row, PeerListColumns::TOT_UP, totalUp, peer.totalUpload(), intDataTextAlignment);
setModelData(row, PeerListColumns::RELEVANCE, (Utils::String::fromDouble(peer.relevance() * 100, 1) + u'%'), peer.relevance(), intDataTextAlignment);
const PathList filePaths = torrent->info().filesForPiece(peer.downloadingPieceIndex()); const PathList filePaths = torrent->info().filesForPiece(peer.downloadingPieceIndex());
QStringList downloadingFiles; QStringList downloadingFiles;
downloadingFiles.reserve(filePaths.size()); downloadingFiles.reserve(filePaths.size());
for (const Path &filePath : filePaths) for (const Path &filePath : filePaths)
downloadingFiles.append(filePath.toString()); downloadingFiles.append(filePath.toString());
const QString downloadingFilesDisplayValue = downloadingFiles.join(u';'); const QString downloadingFilesDisplayValue = downloadingFiles.join(u';');
setModelData(row, PeerListColumns::DOWNLOADING_PIECE, downloadingFilesDisplayValue, downloadingFilesDisplayValue, {}, downloadingFiles.join(u'\n')); setModelData(m_listModel, row, PeerListColumns::DOWNLOADING_PIECE, downloadingFilesDisplayValue
, downloadingFilesDisplayValue, {}, downloadingFiles.join(u'\n'));
if (m_resolver) if (!peer.useI2PSocket() && m_resolver)
m_resolver->resolve(peerEndpoint.address.ip); m_resolver->resolve(peer.address().ip);
if (m_resolveCountries) if (m_resolveCountries)
{ {

3
src/gui/properties/peerlistwidget.h

@ -98,7 +98,7 @@ private slots:
void handleResolved(const QHostAddress &ip, const QString &hostname) const; void handleResolved(const QHostAddress &ip, const QString &hostname) const;
private: private:
void updatePeer(const BitTorrent::Torrent *torrent, const BitTorrent::PeerInfo &peer, bool &isNewPeer); void updatePeer(int row, const BitTorrent::Torrent *torrent, const BitTorrent::PeerInfo &peer, bool hideZeroValues);
int visibleColumnsCount() const; int visibleColumnsCount() const;
void wheelEvent(QWheelEvent *event) override; void wheelEvent(QWheelEvent *event) override;
@ -108,6 +108,7 @@ private:
PropertiesWidget *m_properties = nullptr; PropertiesWidget *m_properties = nullptr;
Net::ReverseResolution *m_resolver = nullptr; Net::ReverseResolution *m_resolver = nullptr;
QHash<PeerEndpoint, QStandardItem *> m_peerItems; QHash<PeerEndpoint, QStandardItem *> m_peerItems;
QList<QStandardItem *> m_I2PPeerItems;
QHash<QHostAddress, QSet<QStandardItem *>> m_itemsByIP; // must be kept in sync with `m_peerItems` QHash<QHostAddress, QSet<QStandardItem *>> m_itemsByIP; // must be kept in sync with `m_peerItems`
bool m_resolveCountries; bool m_resolveCountries;
}; };

Loading…
Cancel
Save