2009-11-14 13:37:45 +03:00
/*
2017-09-07 03:00:04 +03:00
* Bittorrent Client using Qt and libtorrent .
* Copyright ( C ) 2006 Christophe Dumez < chris @ qbittorrent . org >
2009-11-14 13:37:45 +03:00
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation ; either version 2
* of the License , or ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 51 Franklin Street , Fifth Floor , Boston , MA 02110 - 1301 , USA .
*
* In addition , as a special exception , the copyright holders give permission to
* link this program with the OpenSSL project ' s " OpenSSL " library ( or with
* modified versions of it that use the same license as the " OpenSSL " library ) ,
* and distribute the linked executables . You must obey the GNU General Public
* License in all respects for all of the code used other than " OpenSSL " . If you
* modify file ( s ) , you may extend this exception to your version of the file ( s ) ,
* but you are not obligated to do so . If you do not wish to do so , delete this
* exception statement from your version .
*/
2017-04-11 15:35:10 +03:00
# include "peerlistwidget.h"
2019-09-03 08:58:35 +03:00
# include <algorithm>
2022-03-12 15:59:29 +03:00
# include <QtGlobal>
2017-04-11 15:35:10 +03:00
# include <QApplication>
2017-09-07 03:00:04 +03:00
# include <QClipboard>
2015-06-02 12:09:15 +03:00
# include <QHeaderView>
2020-01-28 08:53:06 +03:00
# include <QHostAddress>
2015-06-02 12:09:15 +03:00
# include <QMenu>
# include <QMessageBox>
2017-09-07 03:00:04 +03:00
# include <QSet>
2019-07-13 07:55:21 +03:00
# include <QShortcut>
2017-09-07 03:00:04 +03:00
# include <QSortFilterProxyModel>
# include <QStandardItemModel>
2020-09-18 22:33:17 +03:00
# include <QVector>
2017-09-07 03:00:04 +03:00
# include <QWheelEvent>
2015-06-02 12:09:15 +03:00
2019-07-22 19:50:42 +03:00
# include "base/bittorrent/peeraddress.h"
2015-09-25 11:10:05 +03:00
# include "base/bittorrent/peerinfo.h"
2018-11-02 08:45:16 +03:00
# include "base/bittorrent/session.h"
2021-01-06 15:12:40 +03:00
# include "base/bittorrent/torrent.h"
2019-08-23 12:04:55 +03:00
# include "base/global.h"
2015-09-25 11:10:05 +03:00
# include "base/logger.h"
# include "base/net/geoipmanager.h"
2017-09-07 03:00:04 +03:00
# include "base/net/reverseresolution.h"
# include "base/preferences.h"
2020-07-18 22:03:22 +03:00
# include "base/utils/misc.h"
# include "base/utils/string.h"
2020-04-30 10:53:43 +03:00
# include "gui/uithememanager.h"
2015-06-02 12:09:15 +03:00
# include "peerlistsortmodel.h"
2018-06-14 12:54:23 +03:00
# include "peersadditiondialog.h"
2017-09-07 03:00:04 +03:00
# include "propertieswidget.h"
2010-11-23 00:55:32 +03:00
2020-01-28 22:32:15 +03:00
struct PeerEndpoint
{
BitTorrent : : PeerAddress address ;
QString connectionType ; // matches return type of `PeerInfo::connectionType()`
} ;
bool operator = = ( const PeerEndpoint & left , const PeerEndpoint & right )
{
return ( left . address = = right . address ) & & ( left . connectionType = = right . connectionType ) ;
}
2022-03-12 15:59:29 +03:00
# if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
std : : size_t qHash ( const PeerEndpoint & peerEndpoint , const std : : size_t seed = 0 )
2022-03-13 08:24:26 +03:00
{
return qHashMulti ( seed , peerEndpoint . address , peerEndpoint . connectionType ) ;
}
2022-03-12 15:59:29 +03:00
# else
uint qHash ( const PeerEndpoint & peerEndpoint , const uint seed = 0 )
2020-01-28 22:32:15 +03:00
{
return ( qHash ( peerEndpoint . address , seed ) ^ : : qHash ( peerEndpoint . connectionType ) ) ;
}
2022-03-13 08:24:26 +03:00
# endif
2020-01-28 22:32:15 +03:00
2015-11-12 13:54:36 +03:00
PeerListWidget : : PeerListWidget ( PropertiesWidget * parent )
: QTreeView ( parent )
, m_properties ( parent )
2012-02-25 23:02:19 +04:00
{
2015-11-12 13:54:36 +03:00
// Load settings
loadSettings ( ) ;
// Visual settings
setUniformRowHeights ( true ) ;
setRootIsDecorated ( false ) ;
setItemsExpandable ( false ) ;
setAllColumnsShowFocus ( true ) ;
2020-07-30 09:20:25 +03:00
setEditTriggers ( QAbstractItemView : : NoEditTriggers ) ;
2015-11-12 13:54:36 +03:00
setSelectionMode ( QAbstractItemView : : ExtendedSelection ) ;
2022-03-01 07:50:10 +03:00
header ( ) - > setFirstSectionMovable ( true ) ;
2016-02-07 20:46:56 +03:00
header ( ) - > setStretchLastSection ( false ) ;
2021-09-08 06:47:55 +03:00
header ( ) - > setTextElideMode ( Qt : : ElideRight ) ;
2015-11-12 13:54:36 +03:00
// List Model
2020-07-18 22:03:22 +03:00
m_listModel = new QStandardItemModel ( 0 , PeerListColumns : : COL_COUNT , this ) ;
m_listModel - > setHeaderData ( PeerListColumns : : COUNTRY , Qt : : Horizontal , tr ( " Country/Region " ) ) ; // Country flag column
m_listModel - > setHeaderData ( PeerListColumns : : IP , Qt : : Horizontal , tr ( " IP " ) ) ;
m_listModel - > setHeaderData ( PeerListColumns : : PORT , Qt : : Horizontal , tr ( " Port " ) ) ;
m_listModel - > setHeaderData ( PeerListColumns : : FLAGS , Qt : : Horizontal , tr ( " Flags " ) ) ;
m_listModel - > setHeaderData ( PeerListColumns : : CONNECTION , Qt : : Horizontal , tr ( " Connection " ) ) ;
m_listModel - > setHeaderData ( PeerListColumns : : CLIENT , Qt : : Horizontal , tr ( " Client " , " i.e.: Client application " ) ) ;
m_listModel - > setHeaderData ( PeerListColumns : : PROGRESS , Qt : : Horizontal , tr ( " Progress " , " i.e: % downloaded " ) ) ;
m_listModel - > setHeaderData ( PeerListColumns : : DOWN_SPEED , Qt : : Horizontal , tr ( " Down Speed " , " i.e: Download speed " ) ) ;
m_listModel - > setHeaderData ( PeerListColumns : : UP_SPEED , Qt : : Horizontal , tr ( " Up Speed " , " i.e: Upload speed " ) ) ;
m_listModel - > setHeaderData ( PeerListColumns : : TOT_DOWN , Qt : : Horizontal , tr ( " Downloaded " , " i.e: total data downloaded " ) ) ;
m_listModel - > setHeaderData ( PeerListColumns : : TOT_UP , Qt : : Horizontal , tr ( " Uploaded " , " i.e: total data uploaded " ) ) ;
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. " ) ) ;
m_listModel - > setHeaderData ( PeerListColumns : : DOWNLOADING_PIECE , Qt : : Horizontal , tr ( " Files " , " i.e. files that are being downloaded right now " ) ) ;
2016-06-16 21:35:00 +03:00
// Set header text alignment
2020-07-18 22:03:22 +03:00
m_listModel - > setHeaderData ( PeerListColumns : : PORT , Qt : : Horizontal , QVariant ( Qt : : AlignRight | Qt : : AlignVCenter ) , Qt : : TextAlignmentRole ) ;
m_listModel - > setHeaderData ( PeerListColumns : : PROGRESS , Qt : : Horizontal , QVariant ( Qt : : AlignRight | Qt : : AlignVCenter ) , Qt : : TextAlignmentRole ) ;
m_listModel - > setHeaderData ( PeerListColumns : : DOWN_SPEED , Qt : : Horizontal , QVariant ( Qt : : AlignRight | Qt : : AlignVCenter ) , Qt : : TextAlignmentRole ) ;
m_listModel - > setHeaderData ( PeerListColumns : : UP_SPEED , Qt : : Horizontal , QVariant ( Qt : : AlignRight | Qt : : AlignVCenter ) , Qt : : TextAlignmentRole ) ;
m_listModel - > setHeaderData ( PeerListColumns : : TOT_DOWN , Qt : : Horizontal , QVariant ( Qt : : AlignRight | Qt : : AlignVCenter ) , Qt : : TextAlignmentRole ) ;
m_listModel - > setHeaderData ( PeerListColumns : : TOT_UP , Qt : : Horizontal , QVariant ( Qt : : AlignRight | Qt : : AlignVCenter ) , Qt : : TextAlignmentRole ) ;
m_listModel - > setHeaderData ( PeerListColumns : : RELEVANCE , Qt : : Horizontal , QVariant ( Qt : : AlignRight | Qt : : AlignVCenter ) , Qt : : TextAlignmentRole ) ;
2015-11-12 13:54:36 +03:00
// Proxy model to support sorting without actually altering the underlying model
2017-03-08 08:01:59 +03:00
m_proxyModel = new PeerListSortModel ( this ) ;
2015-11-12 13:54:36 +03:00
m_proxyModel - > setDynamicSortFilter ( true ) ;
m_proxyModel - > setSourceModel ( m_listModel ) ;
m_proxyModel - > setSortCaseSensitivity ( Qt : : CaseInsensitive ) ;
setModel ( m_proxyModel ) ;
2020-07-18 22:03:22 +03:00
hideColumn ( PeerListColumns : : IP_HIDDEN ) ;
hideColumn ( PeerListColumns : : COL_COUNT ) ;
2016-05-11 15:05:51 +03:00
m_resolveCountries = Preferences : : instance ( ) - > resolvePeerCountries ( ) ;
if ( ! m_resolveCountries )
2020-07-18 22:03:22 +03:00
hideColumn ( PeerListColumns : : COUNTRY ) ;
2017-04-05 10:21:12 +03:00
// Ensure that at least one column is visible at all times
2016-02-07 20:46:56 +03:00
bool atLeastOne = false ;
2020-11-16 10:02:11 +03:00
for ( int i = 0 ; i < PeerListColumns : : IP_HIDDEN ; + + i )
{
if ( ! isColumnHidden ( i ) )
{
2016-02-07 20:46:56 +03:00
atLeastOne = true ;
break ;
}
}
if ( ! atLeastOne )
2020-07-18 22:03:22 +03:00
setColumnHidden ( PeerListColumns : : IP , false ) ;
2017-04-05 10:21:12 +03:00
// To also mitigate the above issue, we have to resize each column when
// its size is 0, because explicitly 'showing' the column isn't enough
// in the above scenario.
2020-11-16 10:02:11 +03:00
for ( int i = 0 ; i < PeerListColumns : : IP_HIDDEN ; + + i )
{
2016-05-11 15:05:51 +03:00
if ( ( columnWidth ( i ) < = 0 ) & & ! isColumnHidden ( i ) )
2015-11-12 13:54:36 +03:00
resizeColumnToContents ( i ) ;
2019-08-23 12:04:55 +03:00
}
2015-11-12 13:54:36 +03:00
// Context menu
setContextMenuPolicy ( Qt : : CustomContextMenu ) ;
2018-04-18 16:59:41 +03:00
connect ( this , & QWidget : : customContextMenuRequested , this , & PeerListWidget : : showPeerListMenu ) ;
2015-11-12 13:54:36 +03:00
// Enable sorting
setSortingEnabled ( true ) ;
// IP to Hostname resolver
updatePeerHostNameResolutionState ( ) ;
// SIGNAL/SLOT
2016-02-07 20:46:56 +03:00
header ( ) - > setContextMenuPolicy ( Qt : : CustomContextMenu ) ;
2022-01-18 10:15:54 +03:00
connect ( header ( ) , & QWidget : : customContextMenuRequested , this , & PeerListWidget : : displayColumnHeaderMenu ) ;
2018-04-18 16:59:41 +03:00
connect ( header ( ) , & QHeaderView : : sectionClicked , this , & PeerListWidget : : handleSortColumnChanged ) ;
2018-05-08 16:13:17 +03:00
connect ( header ( ) , & QHeaderView : : sectionMoved , this , & PeerListWidget : : saveSettings ) ;
connect ( header ( ) , & QHeaderView : : sectionResized , this , & PeerListWidget : : saveSettings ) ;
connect ( header ( ) , & QHeaderView : : sortIndicatorChanged , this , & PeerListWidget : : saveSettings ) ;
2015-11-12 13:54:36 +03:00
handleSortColumnChanged ( header ( ) - > sortIndicatorSection ( ) ) ;
2019-07-13 07:55:21 +03:00
const auto * copyHotkey = new QShortcut ( QKeySequence : : Copy , this , nullptr , nullptr , Qt : : WidgetShortcut ) ;
connect ( copyHotkey , & QShortcut : : activated , this , & PeerListWidget : : copySelectedPeers ) ;
2009-11-14 13:37:45 +03:00
}
2012-02-25 23:02:19 +04:00
PeerListWidget : : ~ PeerListWidget ( )
{
2015-11-12 13:54:36 +03:00
saveSettings ( ) ;
2009-11-15 13:00:07 +03:00
}
2022-01-18 10:15:54 +03:00
void PeerListWidget : : displayColumnHeaderMenu ( )
2016-02-07 20:46:56 +03:00
{
2019-06-03 10:10:19 +03:00
QMenu * menu = new QMenu ( this ) ;
menu - > setAttribute ( Qt : : WA_DeleteOnClose ) ;
menu - > setTitle ( tr ( " Column visibility " ) ) ;
2022-01-18 10:45:31 +03:00
menu - > setToolTipsVisible ( true ) ;
2019-06-03 10:10:19 +03:00
2020-11-16 10:02:11 +03:00
for ( int i = 0 ; i < PeerListColumns : : IP_HIDDEN ; + + i )
{
2020-07-18 22:03:22 +03:00
if ( ( i = = PeerListColumns : : COUNTRY ) & & ! Preferences : : instance ( ) - > resolvePeerCountries ( ) )
2016-03-14 02:15:08 +03:00
continue ;
2019-06-03 10:10:19 +03:00
2022-01-18 10:15:54 +03:00
const auto columnName = m_listModel - > headerData ( i , Qt : : Horizontal , Qt : : DisplayRole ) . toString ( ) ;
QAction * action = menu - > addAction ( columnName , this , [ this , i ] ( const bool checked )
2020-11-16 10:02:11 +03:00
{
2022-01-18 10:15:54 +03:00
if ( ! checked & & ( visibleColumnsCount ( ) < = 1 ) )
return ;
2019-06-03 10:10:19 +03:00
2022-01-18 10:15:54 +03:00
setColumnHidden ( i , ! checked ) ;
2016-02-07 20:46:56 +03:00
2022-01-18 10:15:54 +03:00
if ( checked & & ( columnWidth ( i ) < = 5 ) )
resizeColumnToContents ( i ) ;
2019-06-03 10:10:19 +03:00
2022-01-18 10:15:54 +03:00
saveSettings ( ) ;
} ) ;
action - > setCheckable ( true ) ;
action - > setChecked ( ! isColumnHidden ( i ) ) ;
}
2019-06-03 10:10:19 +03:00
2022-01-18 10:45:31 +03:00
menu - > addSeparator ( ) ;
QAction * resizeAction = menu - > addAction ( tr ( " Resize columns " ) , this , [ this ] ( )
{
for ( int i = 0 , count = header ( ) - > count ( ) ; i < count ; + + i )
{
if ( ! isColumnHidden ( i ) )
resizeColumnToContents ( i ) ;
}
saveSettings ( ) ;
} ) ;
resizeAction - > setToolTip ( tr ( " Resize all non-hidden columns to the size of their contents " ) ) ;
2019-06-03 10:10:19 +03:00
menu - > popup ( QCursor : : pos ( ) ) ;
2016-02-07 20:46:56 +03:00
}
2012-02-25 23:02:19 +04:00
void PeerListWidget : : updatePeerHostNameResolutionState ( )
{
2020-11-16 10:02:11 +03:00
if ( Preferences : : instance ( ) - > resolvePeerHostNames ( ) )
{
if ( ! m_resolver )
{
2015-11-12 13:54:36 +03:00
m_resolver = new Net : : ReverseResolution ( this ) ;
2019-08-23 12:04:55 +03:00
connect ( m_resolver , & Net : : ReverseResolution : : ipResolved , this , & PeerListWidget : : handleResolved ) ;
loadPeers ( m_properties - > getCurrentTorrent ( ) ) ;
2015-11-12 13:54:36 +03:00
}
}
2020-11-16 10:02:11 +03:00
else
{
2015-11-12 13:54:36 +03:00
delete m_resolver ;
2019-08-23 12:04:55 +03:00
m_resolver = nullptr ;
2009-11-15 13:00:07 +03:00
}
}
2012-02-25 23:02:19 +04:00
void PeerListWidget : : updatePeerCountryResolutionState ( )
{
2019-08-23 12:04:55 +03:00
const bool resolveCountries = Preferences : : instance ( ) - > resolvePeerCountries ( ) ;
if ( resolveCountries = = m_resolveCountries )
return ;
m_resolveCountries = resolveCountries ;
2020-11-16 10:02:11 +03:00
if ( m_resolveCountries )
{
2019-08-23 12:04:55 +03:00
loadPeers ( m_properties - > getCurrentTorrent ( ) ) ;
2020-07-18 22:03:22 +03:00
showColumn ( PeerListColumns : : COUNTRY ) ;
if ( columnWidth ( PeerListColumns : : COUNTRY ) < = 0 )
resizeColumnToContents ( PeerListColumns : : COUNTRY ) ;
2019-08-23 12:04:55 +03:00
}
2020-11-16 10:02:11 +03:00
else
{
2020-07-18 22:03:22 +03:00
hideColumn ( PeerListColumns : : COUNTRY ) ;
2015-06-18 19:44:01 +03:00
}
2009-11-15 15:57:25 +03:00
}
2022-01-21 12:31:31 +03:00
void PeerListWidget : : showPeerListMenu ( )
2012-02-25 23:02:19 +04:00
{
2021-12-06 08:54:38 +03:00
BitTorrent : : Torrent * torrent = m_properties - > getCurrentTorrent ( ) ;
2015-11-12 13:54:36 +03:00
if ( ! torrent ) return ;
2021-12-06 08:54:38 +03:00
auto * menu = new QMenu ( this ) ;
2019-06-03 10:10:19 +03:00
menu - > setAttribute ( Qt : : WA_DeleteOnClose ) ;
2021-12-06 08:54:38 +03:00
menu - > setToolTipsVisible ( true ) ;
2019-06-03 10:10:19 +03:00
2022-03-12 17:00:58 +03:00
QAction * addNewPeer = menu - > addAction ( UIThemeManager : : instance ( ) - > getIcon ( u " user-group-new " _qs ) , tr ( " Add peers... " )
2021-12-06 08:54:38 +03:00
, this , [ this , torrent ] ( )
2020-11-16 10:02:11 +03:00
{
2021-12-06 08:54:38 +03:00
const QVector < BitTorrent : : PeerAddress > peersList = PeersAdditionDialog : : askForPeers ( this ) ;
const int peerCount = std : : count_if ( peersList . cbegin ( ) , peersList . cend ( ) , [ torrent ] ( const BitTorrent : : PeerAddress & peer )
2019-06-03 10:10:19 +03:00
{
2021-12-06 08:54:38 +03:00
return torrent - > connectPeer ( peer ) ;
2019-06-03 10:10:19 +03:00
} ) ;
2021-12-06 08:54:38 +03:00
if ( peerCount < peersList . length ( ) )
QMessageBox : : information ( this , tr ( " Adding peers " ) , tr ( " Some peers cannot be added. Check the Log for details. " ) ) ;
else if ( peerCount > 0 )
QMessageBox : : information ( this , tr ( " Adding peers " ) , tr ( " Peers are added to this torrent. " ) ) ;
} ) ;
2022-03-12 17:00:58 +03:00
QAction * copyPeers = menu - > addAction ( UIThemeManager : : instance ( ) - > getIcon ( u " edit-copy " _qs ) , tr ( " Copy IP:port " )
2021-12-06 08:54:38 +03:00
, this , & PeerListWidget : : copySelectedPeers ) ;
menu - > addSeparator ( ) ;
2022-03-12 17:00:58 +03:00
QAction * banPeers = menu - > addAction ( UIThemeManager : : instance ( ) - > getIcon ( u " user-group-delete " _qs ) , tr ( " Ban peer permanently " )
2021-12-06 08:54:38 +03:00
, this , & PeerListWidget : : banSelectedPeers ) ;
// disable actions
const auto disableAction = [ ] ( QAction * action , const QString & tooltip )
2020-11-16 10:02:11 +03:00
{
2021-12-06 08:54:38 +03:00
action - > setEnabled ( false ) ;
action - > setToolTip ( tooltip ) ;
} ;
if ( torrent - > isPrivate ( ) )
disableAction ( addNewPeer , tr ( " Cannot add peers to a private torrent " ) ) ;
else if ( torrent - > isChecking ( ) )
disableAction ( addNewPeer , tr ( " Cannot add peers when the torrent is checking " ) ) ;
else if ( torrent - > isQueued ( ) )
disableAction ( addNewPeer , tr ( " Cannot add peers when the torrent is queued " ) ) ;
if ( selectionModel ( ) - > selectedRows ( ) . isEmpty ( ) )
{
const QString tooltip = tr ( " No peer was selected " ) ;
disableAction ( copyPeers , tooltip ) ;
disableAction ( banPeers , tooltip ) ;
2009-11-17 14:46:43 +03:00
}
2019-06-03 10:10:19 +03:00
2021-12-06 08:54:38 +03:00
menu - > popup ( QCursor : : pos ( ) ) ;
2009-11-17 19:02:35 +03:00
}
2015-07-15 01:10:58 +03:00
void PeerListWidget : : banSelectedPeers ( )
2012-02-25 23:02:19 +04:00
{
2020-09-18 22:33:17 +03:00
// Store selected rows first as selected peers may disconnect
2018-11-18 21:40:37 +03:00
const QModelIndexList selectedIndexes = selectionModel ( ) - > selectedRows ( ) ;
2020-09-18 22:33:17 +03:00
QVector < QString > selectedIPs ;
selectedIPs . reserve ( selectedIndexes . size ( ) ) ;
2020-11-16 10:02:11 +03:00
for ( const QModelIndex & index : selectedIndexes )
{
2019-08-23 12:04:55 +03:00
const int row = m_proxyModel - > mapToSource ( index ) . row ( ) ;
2020-07-18 22:03:22 +03:00
const QString ip = m_listModel - > item ( row , PeerListColumns : : IP_HIDDEN ) - > text ( ) ;
2020-09-18 22:33:17 +03:00
selectedIPs + = ip ;
}
// Confirm before banning peer
const QMessageBox : : StandardButton btn = QMessageBox : : question ( this , tr ( " Ban peer permanently " )
, tr ( " Are you sure you want to permanently ban the selected peers? " ) ) ;
if ( btn ! = QMessageBox : : Yes ) return ;
2020-11-16 10:02:11 +03:00
for ( const QString & ip : selectedIPs )
{
2015-11-12 13:54:36 +03:00
BitTorrent : : Session : : instance ( ) - > banIP ( ip ) ;
2019-08-23 12:04:55 +03:00
LogMsg ( tr ( " Peer \" %1 \" is manually banned " ) . arg ( ip ) ) ;
2015-11-12 13:54:36 +03:00
}
// Refresh list
loadPeers ( m_properties - > getCurrentTorrent ( ) ) ;
2009-11-17 14:46:43 +03:00
}
2015-07-15 01:10:58 +03:00
void PeerListWidget : : copySelectedPeers ( )
{
2018-11-18 21:40:37 +03:00
const QModelIndexList selectedIndexes = selectionModel ( ) - > selectedRows ( ) ;
2015-11-12 13:54:36 +03:00
QStringList selectedPeers ;
2019-08-23 12:04:55 +03:00
2020-11-16 10:02:11 +03:00
for ( const QModelIndex & index : selectedIndexes )
{
2019-08-23 12:04:55 +03:00
const int row = m_proxyModel - > mapToSource ( index ) . row ( ) ;
2020-07-18 22:03:22 +03:00
const QString ip = m_listModel - > item ( row , PeerListColumns : : IP_HIDDEN ) - > text ( ) ;
const QString port = m_listModel - > item ( row , PeerListColumns : : PORT ) - > text ( ) ;
2019-08-23 12:04:55 +03:00
2022-03-12 17:00:58 +03:00
if ( ! ip . contains ( u ' . ' ) ) // IPv6
selectedPeers < < ( u ' [ ' + ip + u " ]: " + port ) ;
2019-08-23 12:04:55 +03:00
else // IPv4
2022-03-12 17:00:58 +03:00
selectedPeers < < ( ip + u ' : ' + port ) ;
2015-11-12 13:54:36 +03:00
}
2019-08-23 12:04:55 +03:00
2022-03-12 17:00:58 +03:00
QApplication : : clipboard ( ) - > setText ( selectedPeers . join ( u ' \n ' ) ) ;
2015-07-15 01:10:58 +03:00
}
2015-11-12 13:54:36 +03:00
void PeerListWidget : : clear ( )
{
m_peerItems . clear ( ) ;
2020-01-30 08:37:46 +03:00
m_itemsByIP . clear ( ) ;
2019-08-23 12:04:55 +03:00
const int nbrows = m_listModel - > rowCount ( ) ;
if ( nbrows > 0 )
2018-11-06 18:49:17 +03:00
m_listModel - > removeRows ( 0 , nbrows ) ;
2009-11-14 13:37:45 +03:00
}
2015-11-12 13:54:36 +03:00
void PeerListWidget : : loadSettings ( )
{
header ( ) - > restoreState ( Preferences : : instance ( ) - > getPeerListState ( ) ) ;
2009-11-15 11:40:26 +03:00
}
2015-11-12 13:54:36 +03:00
void PeerListWidget : : saveSettings ( ) const
{
Preferences : : instance ( ) - > setPeerListState ( header ( ) - > saveState ( ) ) ;
2009-11-15 11:40:26 +03:00
}
2021-01-06 15:12:40 +03:00
void PeerListWidget : : loadPeers ( const BitTorrent : : Torrent * torrent )
2015-11-12 13:54:36 +03:00
{
if ( ! torrent ) return ;
2019-08-02 07:55:06 +03:00
const QVector < BitTorrent : : PeerInfo > peers = torrent - > peers ( ) ;
2020-01-28 22:32:15 +03:00
QSet < PeerEndpoint > existingPeers ;
2019-08-23 12:04:55 +03:00
for ( auto i = m_peerItems . cbegin ( ) ; i ! = m_peerItems . cend ( ) ; + + i )
existingPeers < < i . key ( ) ;
2015-11-12 13:54:36 +03:00
2020-11-16 10:02:11 +03:00
for ( const BitTorrent : : PeerInfo & peer : peers )
{
2020-01-28 22:32:15 +03:00
if ( peer . address ( ) . ip . isNull ( ) ) continue ;
2015-11-12 13:54:36 +03:00
2019-08-23 12:04:55 +03:00
bool isNewPeer = false ;
2020-01-28 22:32:15 +03:00
updatePeer ( torrent , peer , isNewPeer ) ;
2020-11-16 10:02:11 +03:00
if ( ! isNewPeer )
{
2020-01-28 22:32:15 +03:00
const PeerEndpoint peerEndpoint { peer . address ( ) , peer . connectionType ( ) } ;
existingPeers . remove ( peerEndpoint ) ;
}
2015-11-12 13:54:36 +03:00
}
2019-08-23 12:04:55 +03:00
// Remove peers that are gone
2020-11-16 10:02:11 +03:00
for ( const PeerEndpoint & peerEndpoint : asConst ( existingPeers ) )
{
2020-01-30 08:37:46 +03:00
QStandardItem * item = m_peerItems . take ( peerEndpoint ) ;
QSet < QStandardItem * > & items = m_itemsByIP [ peerEndpoint . address . ip ] ;
items . remove ( item ) ;
if ( items . isEmpty ( ) )
m_itemsByIP . remove ( peerEndpoint . address . ip ) ;
2015-11-12 13:54:36 +03:00
m_listModel - > removeRow ( item - > row ( ) ) ;
}
2009-11-14 13:37:45 +03:00
}
2021-01-06 15:12:40 +03:00
void PeerListWidget : : updatePeer ( const BitTorrent : : Torrent * torrent , const BitTorrent : : PeerInfo & peer , bool & isNewPeer )
2015-11-12 13:54:36 +03:00
{
2020-01-28 22:32:15 +03:00
const PeerEndpoint peerEndpoint { peer . address ( ) , peer . connectionType ( ) } ;
const QString peerIp = peerEndpoint . address . ip . toString ( ) ;
2020-07-18 22:03:22 +03:00
const Qt : : Alignment intDataTextAlignment = Qt : : AlignRight | Qt : : AlignVCenter ;
const auto setModelData =
[ this ] ( const int row , const int column , const QString & displayData
, const QVariant & underlyingData , const Qt : : Alignment textAlignmentData = { }
, const QString & toolTip = { } )
{
const QMap < int , QVariant > data =
{
2020-07-30 09:20:25 +03:00
{ Qt : : DisplayRole , displayData } ,
2020-07-18 22:03:22 +03:00
{ PeerListSortModel : : UnderlyingDataRole , underlyingData } ,
{ Qt : : TextAlignmentRole , QVariant { textAlignmentData } } ,
{ Qt : : ToolTipRole , toolTip }
} ;
m_listModel - > setItemData ( m_listModel - > index ( row , column ) , data ) ;
} ;
2020-01-28 22:32:15 +03:00
auto itemIter = m_peerItems . find ( peerEndpoint ) ;
2019-08-23 12:04:55 +03:00
isNewPeer = ( itemIter = = m_peerItems . end ( ) ) ;
2020-11-16 10:02:11 +03:00
if ( isNewPeer )
{
2019-08-23 12:04:55 +03:00
// new item
const int row = m_listModel - > rowCount ( ) ;
m_listModel - > insertRow ( row ) ;
2020-07-18 22:03:22 +03:00
setModelData ( row , PeerListColumns : : IP , peerIp , peerIp , { } , peerIp ) ;
setModelData ( row , PeerListColumns : : PORT , QString : : number ( peer . address ( ) . port ) , peer . address ( ) . port , intDataTextAlignment ) ;
setModelData ( row , PeerListColumns : : IP_HIDDEN , peerIp , peerIp ) ;
itemIter = m_peerItems . insert ( peerEndpoint , m_listModel - > item ( row , PeerListColumns : : IP ) ) ;
2020-01-30 08:37:46 +03:00
m_itemsByIP [ peerEndpoint . address . ip ] . insert ( itemIter . value ( ) ) ;
2015-11-12 13:54:36 +03:00
}
2019-08-23 12:04:55 +03:00
const int row = ( * itemIter ) - > row ( ) ;
2020-10-11 19:06:51 +03:00
const bool hideValues = Preferences : : instance ( ) - > getHideZeroValues ( ) ;
2020-07-18 22:03:22 +03:00
setModelData ( row , PeerListColumns : : CONNECTION , peer . connectionType ( ) , peer . connectionType ( ) ) ;
setModelData ( row , PeerListColumns : : FLAGS , peer . flags ( ) , peer . flags ( ) , { } , peer . flagsDescription ( ) ) ;
const QString client = peer . client ( ) . toHtmlEscaped ( ) ;
2021-07-08 09:13:26 +03:00
setModelData ( row , PeerListColumns : : CLIENT , client , client , { } , client ) ;
2022-03-12 17:00:58 +03:00
setModelData ( row , PeerListColumns : : PROGRESS , ( Utils : : String : : fromDouble ( peer . progress ( ) * 100 , 1 ) + u ' % ' ) , peer . progress ( ) , intDataTextAlignment ) ;
2020-10-11 19:06:51 +03:00
const QString downSpeed = ( hideValues & & ( peer . payloadDownSpeed ( ) < = 0 ) ) ? QString { } : Utils : : Misc : : friendlyUnit ( peer . payloadDownSpeed ( ) , true ) ;
setModelData ( row , PeerListColumns : : DOWN_SPEED , downSpeed , peer . payloadDownSpeed ( ) , intDataTextAlignment ) ;
const QString upSpeed = ( hideValues & & ( peer . payloadUpSpeed ( ) < = 0 ) ) ? QString { } : Utils : : Misc : : friendlyUnit ( peer . payloadUpSpeed ( ) , true ) ;
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 ) ;
2022-03-12 17:00:58 +03:00
setModelData ( row , PeerListColumns : : RELEVANCE , ( Utils : : String : : fromDouble ( peer . relevance ( ) * 100 , 1 ) + u ' % ' ) , peer . relevance ( ) , intDataTextAlignment ) ;
2016-02-27 22:51:39 +03:00
2022-02-08 06:03:48 +03:00
const PathList filePaths = torrent - > info ( ) . filesForPiece ( peer . downloadingPieceIndex ( ) ) ;
QStringList downloadingFiles ;
downloadingFiles . reserve ( filePaths . size ( ) ) ;
for ( const Path & filePath : filePaths )
downloadingFiles . append ( filePath . toString ( ) ) ;
2022-03-12 17:00:58 +03:00
const QString downloadingFilesDisplayValue = downloadingFiles . join ( u ' ; ' ) ;
2022-03-29 05:41:17 +03:00
setModelData ( row , PeerListColumns : : DOWNLOADING_PIECE , downloadingFilesDisplayValue , downloadingFilesDisplayValue , { } , downloadingFiles . join ( u ' \n ' ) ) ;
2019-08-23 12:04:55 +03:00
if ( m_resolver )
2020-01-28 08:53:06 +03:00
m_resolver - > resolve ( peerEndpoint . address . ip ) ;
2009-11-14 13:37:45 +03:00
2020-11-16 10:02:11 +03:00
if ( m_resolveCountries )
{
2019-08-23 12:04:55 +03:00
const QIcon icon = UIThemeManager : : instance ( ) - > getFlagIcon ( peer . country ( ) ) ;
2020-11-16 10:02:11 +03:00
if ( ! icon . isNull ( ) )
{
2020-07-18 22:03:22 +03:00
m_listModel - > setData ( m_listModel - > index ( row , PeerListColumns : : COUNTRY ) , icon , Qt : : DecorationRole ) ;
2015-11-12 13:54:36 +03:00
const QString countryName = Net : : GeoIPManager : : CountryName ( peer . country ( ) ) ;
2020-07-18 22:03:22 +03:00
m_listModel - > setData ( m_listModel - > index ( row , PeerListColumns : : COUNTRY ) , countryName , Qt : : ToolTipRole ) ;
2015-11-12 13:54:36 +03:00
}
}
2009-11-14 13:37:45 +03:00
}
2009-11-14 16:33:55 +03:00
2022-01-18 10:15:54 +03:00
int PeerListWidget : : visibleColumnsCount ( ) const
{
int count = 0 ;
for ( int i = 0 , iMax = header ( ) - > count ( ) ; i < iMax ; + + i )
{
if ( ! isColumnHidden ( i ) )
+ + count ;
}
return count ;
}
2020-01-28 08:53:06 +03:00
void PeerListWidget : : handleResolved ( const QHostAddress & ip , const QString & hostname ) const
2015-11-12 13:54:36 +03:00
{
2020-01-30 10:04:46 +03:00
if ( hostname . isEmpty ( ) )
return ;
2020-01-30 08:37:46 +03:00
const QSet < QStandardItem * > items = m_itemsByIP . value ( ip ) ;
for ( QStandardItem * item : items )
item - > setData ( hostname , Qt : : DisplayRole ) ;
2009-11-14 16:33:55 +03:00
}
2010-10-24 13:32:28 +04:00
2019-08-23 12:04:55 +03:00
void PeerListWidget : : handleSortColumnChanged ( const int col )
2010-10-24 13:32:28 +04:00
{
2020-07-18 22:03:22 +03:00
if ( col = = PeerListColumns : : COUNTRY )
2015-11-12 13:54:36 +03:00
m_proxyModel - > setSortRole ( Qt : : ToolTipRole ) ;
2019-08-23 12:04:55 +03:00
else
2020-07-18 22:03:22 +03:00
m_proxyModel - > setSortRole ( PeerListSortModel : : UnderlyingDataRole ) ;
2010-10-24 13:32:28 +04:00
}
2011-04-17 18:42:38 +04:00
2016-11-27 10:14:41 +03:00
void PeerListWidget : : wheelEvent ( QWheelEvent * event )
{
2020-11-16 10:02:11 +03:00
if ( event - > modifiers ( ) & Qt : : ShiftModifier )
{
2016-11-27 10:14:41 +03:00
// Shift + scroll = horizontal scroll
2019-06-11 15:32:34 +03:00
event - > accept ( ) ;
2022-03-17 04:45:24 +03:00
QWheelEvent scrollHEvent { event - > position ( ) , event - > globalPosition ( )
2020-06-04 08:29:32 +03:00
, event - > pixelDelta ( ) , event - > angleDelta ( ) . transposed ( ) , event - > buttons ( )
2022-03-17 04:45:24 +03:00
, event - > modifiers ( ) , event - > phase ( ) , event - > inverted ( ) , event - > source ( ) } ;
2016-11-27 10:14:41 +03:00
QTreeView : : wheelEvent ( & scrollHEvent ) ;
return ;
}
QTreeView : : wheelEvent ( event ) ; // event delegated to base class
}